<<<<< Back to jsmain

Library

edit

tooltip

edit
  • Usage:

1. Declare the new object anywhere in the body section.

var tt=new tooltip("style1","style2");
  • Put the "function tooltip()" in the head section.

2. .style and .style2 must exist in style section (if not, font is ugly). For example:

.tooltiptitle{COLOR: #FFFFFF; TEXT-DECORATION: none; CURSOR: Default; font-family: arial; font-weight: bold; font-size: 8pt}
.tooltipcontent{COLOR: #000000; TEXT-DECORATION: none; CURSOR: Default; font-family: arial; font-size: 8pt}

3. Add the following in an element to call the tooltip: (1st method)

<a onmousemove="tt.move(event)" onmouseover="tt.on('title text','content text')" onmouseout="tt.off()">

4. Change the object's attributes (if desired):

tt.top_color(color);
tt.sub_color(color);
tt.width(number|string);       // Two modes: fixed width or native width
tt.format = function(ary) { return(htmltext) };   // Changing the tooltip text-format entirely
     e.g. var xx = new tooltip();    // arguments to tooltip are useless if .format is overridden
          xx.format=function(ary) {
            return('html format '+ary[0]+'etc, etc'+ary[1]+ary[2]);     // number of ary elements depends upon the call to .on() or .textassign
          }

5. Two modes of tooltip size: Fixed width mode (.width(number)) and native width mode (.width(string))

If number, then the number is the pixel-width. Tooltip will be fixed width.
If string, then tooltip will be native width (The width of the input text).
   string is used as a padding at start and end (e.g. "& nbsp; & nbsp;").
Default is native width.

6. Multiple tooltip object could be created if different color/width/text-format is desired.

  • Use the same object if the text/"arguments" are different. Create another object if the FORMAT is different. See "x","y","z" objects in the example below. "x" is used twice (two different arguments). "y" is different from "x" bec of different font style. "z" has an entirely different format.

7. The 2nd method of assigning tooltips is via javascript assignment:

 <body><div id="i1">This is the text</div>
 <script>
    var xx = new tooltip("tooltiptitle","tooltipcontent");
    xx.textassign('i1', "title text", "content text");          // mouse events are automatically added
 </script>
  • Example is
<head>
<style>
  .tooltiptitle    {COLOR: #FFFFFF; TEXT-DECORATION: none; CURSOR: Default; font-family: arial; font-weight: bold; font-size: 8pt}
  .tooltipcontent  {COLOR: #000000; TEXT-DECORATION: none; CURSOR: Default; font-family: arial; font-size: 8pt}
  .tooltiptitle1   {COLOR: #FFFF00; TEXT-DECORATION: none; CURSOR: Default; font-family: arial; font-weight: bold; font-size: 8pt}
  .tooltipcontent1 {COLOR: #0000FF; TEXT-DECORATION: none; CURSOR: Default; font-family: arial; font-size: 8pt}
</style>
 ... put tooltip script here...
</head>
<body>

<div onmousemove="x.move(event)" onmouseover="x.on('hi There! Hello There!There! Hello There!There! Hello There!There! Hello There!')" onmouseout="x.off()">1. This is a long-single line tooltip (native width)</div>
<div onmousemove="x.move(event)" onmouseover="x.on('This is the title', 'This is the body. This is the body text')" onmouseout="x.off()">2. This is a two line tooltip (native width)</div>
<div onmousemove="y.move(event)" onmouseover="y.on('hi There! Hello There!There! Hello There!There! Hello There!There! Hello There!')" onmouseout="y.off()">3. This is a long-single line tooltip (fixed width)</div>
<div onmousemove="y.move(event)" onmouseover="y.on('This is the title', 'This is the body. This is the body text')" onmouseout="y.off()">4. This is a two line tooltip (fixed width)</div>
<div onmousemove="z.move(event)" onmouseover="z.on('This is a wow! ','abc')" onmouseout="z.off()">5. Self configured tooltip</div>
<hr>
<div id="i1">6. This is a long-single line tooltip (native width)</div>
<div id="i2">7. This is a two line tooltip (native width)</div>
<div id="i3">8. This is a long-single line tooltip (fixed width)</div>
<div id="i4">9. This is a two line tooltip (fixed width)</div>
<div id="i5">10. Self configured tooltip</div>

<script>
// Create the tooltip objects
var x = new tooltip("tooltiptitle","tooltipcontent");
var y = new tooltip("tooltiptitle1","tooltipcontent1");
y.width(350);
var z = new tooltip();
z.format = function(ary) { return('<font color="blue">'+ary[0]+ary[1]+(ary[2]?ary[2]:"")+(ary[3]?ary[3]:"")+'</font>') };

x.textassign('i1', "textassign: This is a very long one. Very Long. This is a very long one. Very Long. This is a very long one. Very Long. This is a very long one. Very Long. ");
x.textassign('i2', "textassign: This is TITLE", "This is MESSAGE");
y.textassign('i3', "textassign: This is a very long one. Very Long. This is a very long one. Very Long. This is a very long one. Very Long. This is a very long one. Very Long. ");
y.textassign('i4', "textassign: This is TITLE", "This is MESSAGE");
z.textassign('i5', "textassign: WOW! ","abc ","def ","ghi");

</script>
</body>
  • Note: memory leak is checked and no apparent leak.

tooltip code

edit
  • Click on Edit to Copy

// Developed by JohnDR
function tooltip(title_font_classname, content_font_classname) {   // argument is the FONT class names defined in style section

  // The following are default variables that could be changed via object methods
  var tt_top_color = "#0099CC";   // default values
  var tt_sub_color = "#99CCFF";

  var tt_width;                                    // width of tooltip
  var tt_internal_tble_width;                      // table width (92% for fixed pixel width, 100% for native width)
  var tt_internal_top_width;                       // top table width (exist for fixed pixel, non-existent for native width)
  var tt_pad;                                      // padding, used for native width

  // Object variables
  var tt_title_classname = title_font_classname;
  var tt_content_classname = content_font_classname;
  var ttdiv = document.getElementById('tooltipdiv');
  var h_title = new Array;
  var tt_this = this;                              // This is a neat trick for storing "this" during mouse events. see tt_this usage in .on_from_java()

  // object methods
  this.top_color = function(txt) { tt_top_color=txt };
  this.sub_color = function(txt) { tt_sub_color=txt };

  // There are two modes: Fixed width mode (nn is number) and native width mode (nn is string)
  this.width     = function(nn)  {     // set width to number for fixed pixel width or string for the padding.
    if(typeof(nn)=='number') {
      tt_width=nn;
      tt_internal_tble_width=92;       // 92% to allow table margin
      tt_internal_top_width='width='+tt_width;
      tt_pad='';
    } else {
      // This is native width
      tt_width=0;
      tt_internal_tble_width=100;
      tt_internal_top_width='';   // empty
      if(typeof(nn)=='string') {
        tt_pad=nn;
      } else {
        tt_pad='&'+'nbsp;&'+'nbsp;&'+'nbsp;';     // default spacer (beginning and end). 4 times of "& nbsp;" without the space
      }
    }
  }

  this.move = function(ee) {    // move the tooltip
    var myEvent = ee ? ee : window.event;             // just in case ee doesnt exist (msie)
    var yy;
    ttdiv.style.top=myEvent.clientY + document.body.scrollTop + 5;

    if(tt_pad==='') {           // fixed tooltip width
      yy= myEvent.clientX - (tt_width/2);   // tooltip starts at middle of the mouse cursor.
      if(yy<0) yy=0;
      ttdiv.style.left=yy + document.body.scrollLeft + 5;
    } else {                    // native tooltip width
      ttdiv.style.left=myEvent.clientX + document.body.scrollLeft + 5;

    }
  }

  this.off = function() {
    ttdiv.style.visibility='hidden';
  }

  // This method is only called from within html (1st method only).
  this.on = function() {
    ttdiv.innerHTML = this.format(arguments);    // There could be as many arguments
    ttdiv.style.visibility='visible';
  }

  this.on_from_java = function(the_event) {
     var ee = the_event ? the_event : window.event;                      // just in case the_event doesnt exist (msie)
     var elm_id = ee.target ? ee.target.id : ee.srcElement.id;           // ff : msie
     var ary = new Array;
     var i;

     if(h_title[elm_id]==undefined) {
       ary[0] = "No tooltip text defined for id="+elm_id;
     } else {
       for (i in h_title[elm_id]) {
         ary.push(h_title[elm_id][i]);
       }
     }

     ttdiv.innerHTML = tt_this.format(ary);                           // Cannot use a simple "this." bec of mouseevent
     ttdiv.style.visibility='visible';
  }

  // This could be overridden. Below only takes two arguments.
  this.format = function(ary) {
    return('<table border=0 '+ tt_internal_top_width +' cellspacing=0 cellpadding=0>' +
                      '   <tr><td bgcolor=#000000>' +
                      '   <table width="100%" border=0 cellspacing=1 cellpadding=0>' +
                      '      <tr><td width="100%" bgcolor='+ tt_top_color +'>' +
                      '      <table border=0 width="'+ tt_internal_tble_width +'%" cellspacing=0 cellpadding=0 align="center">' +
                      '        <tr><td width="100%" class="'+ tt_title_classname +'"><b>'+ tt_pad+ ary[0] + tt_pad+'</b></td></tr>' +
                      '      </table></td></tr>' + ( ary[1] ?
                        '      <tr><td width="100%" bgcolor='+ tt_sub_color +'>' +
                        '      <table border=0 width="'+ tt_internal_tble_width +'%" cellpadding=0 cellspacing=1 align="center">' +
                        '        <tr><td width="100%" class="'+ tt_content_classname +'">'+ tt_pad+ ary[1] + tt_pad+'</td></tr>' : '') +
                      '      </table></td></tr>' +
                      '    </table></td></tr>' +
                    '  </table>');
  }


  // This is the 2nd method of using this object. (Adding the event dynamically)
  this.textassign = function() {    // vid, TTitle, [TContent]
    var vid=arguments[0];
    var i;
    if(arguments[1]==undefined) { alert('textassign needs at least two arguments'); return; }
    h_title[vid]=new Array;
    for(i=1;i<100;i++) {
      if(arguments[i]==undefined) break;
      h_title[vid].push(arguments[i]);
    }

    // Add the events
    var elm = document.getElementById(vid);
    if(!elm) { alert('element id='+vid+' is not found'); return; }
    elm.onmousemove= this.move;
    elm.onmouseout = this.off;
    elm.onmouseover= this.on_from_java;
  }

  // create the new div
  if(!ttdiv) {
    // try to create the new div element
    var newdiv = document.createElement("div");
    newdiv.id="tooltipdiv";
    document.body.appendChild(newdiv);
    
    ttdiv = document.getElementById('tooltipdiv');
    if(!ttdiv) alert("Cannot create empty div");
  }

  // Initialize the object.
  ttdiv.style.position   = 'absolute';
  ttdiv.style.visibility = 'hidden';
  this.width();                          // Set the default to Native width
}    // end tooltip object

sortable

edit
// Just add: <TABLE class="sortable" id="unique_id"> in your table.

addEvent(window, "load", ts_resortTable);

// Original sortable code is from http://www.kryogenix.org/code/browser/sorttable/
// This version is modified to make it more encapsulated
function ts_resortTable(lnk) {
    var nbsp = '&'+'nbsp;';     // so that wiki copy+paste will work

    // check initialization
    if(typeof(ts_resortTable_init)=='undefined') {
      sortables_init();
      ts_resortTable_init=1;      // global variable, sortables_init is executed only once.
      return;
    }

    // get the span
    var span;
    for (var ci=0;ci<lnk.childNodes.length;ci++) {
        if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
    }
    var spantext = ts_getInnerText(span);
    var td = lnk.parentNode;
    var column = td.cellIndex;
    var table = getParent(td,'TABLE');
    var SORT_COLUMN_INDEX;

    // Work out a type for the column
    if (table.rows.length <= 1) return;
    var itm = ts_getInnerText(table.rows[1].cells[column]);
    sortfn = ts_sort_caseinsensitive;
    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^[£$]/)) sortfn = ts_sort_currency;
    if (itm.match(/^[\d\.]+$/)) sortfn = ts_sort_numeric;
    SORT_COLUMN_INDEX = column;
    var firstRow = new Array();
    var newRows = new Array();
    for (i=0;i<table.rows[0].length;i++) { firstRow[i] = table.rows[0][i]; }
    for (j=1;j<table.rows.length;j++) { newRows[j-1] = table.rows[j]; }

    newRows.sort(sortfn);

    if (span.getAttribute("sortdir") == 'down') {
        ARROW = nbsp+nbsp+'&'+'uarr;';
        newRows.reverse();
        span.setAttribute('sortdir','up');
    } else {
        ARROW = nbsp+nbsp+'&'+'darr;';  
        span.setAttribute('sortdir','down');
    }

    // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
    // dont do sortbottom rows
    for (i=0;i<newRows.length;i++) { if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) table.tBodies[0].appendChild(newRows[i]);}
    // do sortbottom rows only
    for (i=0;i<newRows.length;i++) { if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)) table.tBodies[0].appendChild(newRows[i]);}

    // Delete any other arrows there may be showing
    var allspans = document.getElementsByTagName("span");
    for (var ci=0;ci<allspans.length;ci++) {
        if (allspans[ci].className == 'sortarrow') {
            if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
                allspans[ci].innerHTML = nbsp+nbsp+nbsp;
            }
        }
    }

    span.innerHTML = ARROW;
    // Routine is DONE!

  // sortable initialization
  function sortables_init() {
    // Find all tables with class sortable and make them sortable
    if (!document.getElementsByTagName) return;
    tbls = document.getElementsByTagName("table");
    for (ti=0;ti<tbls.length;ti++) {
        thisTbl = tbls[ti];
        if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
            //initTable(thisTbl.id);
            ts_makeSortable(thisTbl);
        }
    }

  function ts_makeSortable(table) {
    if (table.rows && table.rows.length > 0) {
        var firstRow = table.rows[0];
    }
    if (!firstRow) return;

    // We have a first row: assume its the header, and make its contents clickable links
    for (var i=0;i<firstRow.cells.length;i++) {
        var cell = firstRow.cells[i];
        var txt = ts_getInnerText(cell);
        cell.innerHTML = '<a href="#" class="sortheader" onclick="ts_resortTable(this);return false;">'+txt+'<span class="sortarrow">'+nbsp+nbsp+nbsp+'</span></a>';
    }
   }
 }

  // Routine used by sortables_init and ts_resorttable
  function ts_getInnerText(el) {
	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.innerText) return el.innerText;	//Not needed but it is faster
	var str = "";

	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		switch (cs[i].nodeType) {
			case 1: //ELEMENT_NODE
				str += ts_getInnerText(cs[i]);
				break;
			case 3:	//TEXT_NODE
				str += cs[i].nodeValue;
				break;
		}
	}
	return str;
  }

  function ts_sort_date(a,b) {
    var aa,bb;
    // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa.length == 10) {
        dt1 = aa.substr(6,4)+aa.substr(3,2)+aa.substr(0,2);
    } else {
        yr = aa.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt1 = yr+aa.substr(3,2)+aa.substr(0,2);
    }
    if (bb.length == 10) {
        dt2 = bb.substr(6,4)+bb.substr(3,2)+bb.substr(0,2);
    } else {
        yr = bb.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt2 = yr+bb.substr(3,2)+bb.substr(0,2);
    }
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;
  }

  function ts_sort_currency(a,b) {
    var aa,bb;
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    return parseFloat(aa) - parseFloat(bb);
  }

  function ts_sort_numeric(a,b) {
    var aa,bb;
    aa = parseFloat(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
    if (isNaN(aa)) aa = 0;
    bb = parseFloat(ts_getInnerText(b.cells[SORT_COLUMN_INDEX]));
    if (isNaN(bb)) bb = 0;
    return aa-bb;
  }

  function ts_sort_caseinsensitive(a,b) {
    var aa,bb;
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
  }

  function ts_sort_default(a,b) {
    var aa,bb;
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
  }

  function getParent(el, pTagName) {
	if (el == null) return null;
	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
		return el;
	else
		return getParent(el.parentNode, pTagName);
  }

}   // end of ts_resortTable

addEvent

edit
  • Multiple addEvent() is OK.
  • Generic addEvent routine. Works for both ff and msie
// e.g. addEvent(window, "load", sortables_init);
function addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+,  NS6 and Mozilla
// By Scott Andrew
{
  if (elm.addEventListener){
    elm.addEventListener(evType, fn, useCapture);
    return true;
  } else if (elm.attachEvent){
    var r = elm.attachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be removed");
  }
}

animated_div

edit
  • animated or non-animatated "persistent" div element, ala-dell like style. This includes the "table header" trick.
  • There are two objects. This is a very good example of an object inheritance.
 // This is the generic, ala-dell style persistent animated div
 var obj=new animated_div("divIDname");

 // This is a persistent table header. Height is in pixels. (inherited)
 var obj=new table_header("divIDname", "table_ID_name", "height");
           // divIDname must be empty and must exist
           // table header defaults: leftoffset is automatically computed, topoffset=0, stepwalk=10 (faster)
           // table header will not display if table is not scrolled
           // marginright might need to be set manually if the header does not match the table.
  • Object methods to change behavior:
 obj.topoffset(pixels)   : where will the persistent div be located (default=50)
 obj.leftoffset(pixels)  : where will the persistent div be located (default=300)
 obj.delaywalk(ms)       : how fast is the walk in ms (default=10ms)
 obj.delaydisp(ms)       : how fast is the display after mouse is released in ms (must be greater than 500) (default=2000ms). It is advisable to set this to at least 500ms.
 obj.stepwalk(pixels)    : how many increment is the walk (default=5)
 obj.noanim(boolean)     : set to true to disable animation
 obj.height(pixels)      : set the div height (default is auto)
 obj.marginright(pixels) : set the div's right margin (ff only)
 obj.refreshtable(true/false) : set to true if innerhtml refresh will happen every scroll. Default is false. Set this to true if the table is changing dynamically. This method is for table_header only.
  • Note: This object is tested for memory leak (set delaydisp(10)). ff releases the memory after some time, specially when going to a different tab and doing an operation.
  • Edit this section to reveal the unit test for animated_div/table_header object.

animated_div code

edit
  • Code (Click on Edit to easily copy)

// Developed by JohnDR
function animated_div(elmname) {
  this.elm=document.getElementById(elmname);
  var mythis=this;
  var winheight;
  var thand;

  // default values
  var offst=50;
  var t_delaywalk=10;
  var t_delaydisp=2000;
  var t_leftoffset=300;
  var t_noanim=0;
  var t_stepwalk=5;
  var globalid=0;
  var poslast=0;

  if(!this.elm) { alert("id="+elmname+" is not found."); return; }

  this.elm.style.position   = 'absolute';
  this.elm.style.left = t_leftoffset;             // fixed x location
  getwinheight();
  putit(poslast, offst, globalid);                // initial put

  addEvent(window, "scroll", scrollhappened);
  // End Constructor

  this.topoffset = function(nn) { globalid++; offst=nn; putit(poslast, offst, globalid); }
  this.delaywalk = function(nn) { t_delaywalk=nn; }
  this.delaydisp = function(nn) { t_delaydisp=nn; }   // It is advisable to have 500ms at least for delay disp.
  this.stepwalk  = function(nn) { t_stepwalk=nn; }
  this.leftoffset= function(nn) { t_leftoffset=nn; this.elm.style.left = t_leftoffset; }
  this.noanim    = function(nn) { t_noanim=nn; }
  this.height      = function(nn) { this.elm.style.height = nn; }
  this.marginright = function(nn) { this.elm.style.marginRight = nn; };

  // Private Methods
  this.donotwalk   = function()    {  return(false); }       // overridden in table_header

  function scrollhappened() {
    var tt=document.body.scrollTop + offst;     // this is the target position
    getwinheight();
    poslast=mythis.elm.offsetTop;
    globalid++;
    if(thand) clearTimeout(thand);        // This is very important. Need to delete previous ones.
    thand=setTimeout(function() {
      if(mythis.donotwalk()) return;
      if(poslast<tt-offst) {              // beyond top
        mythis.elm.style.top=document.body.scrollTop;
        poslast=document.body.scrollTop;
        walk(tt, globalid);
        return;
      }
      if(poslast>tt+winheight-offst) {    // too far beyond bottom
        mythis.elm.style.top=tt+winheight-offst;
        poslast=tt+winheight-offst;
      }
      walk(tt, globalid);
    }, t_delaydisp);
  }

  function getwinheight() {
    winheight = window.innerHeight || document.body.offsetHeight;   // ff vs msie
  }

  function walk(tt1, lid) {
    var cc;

    // dv("walk"+document.body.scrollTop,"target="+tt1+" lid="+lid);     // debug watch
    if(lid!=globalid) return;               // stale process! neat trick for timeout related calls! (avoid duplication)
    if(poslast>tt1) {
      cc=poslast-t_stepwalk;
    } else {
      cc=poslast+t_stepwalk;
    }
    if(t_noanim) {
      putit(tt1, tt1, lid);                // immediate
    } else {
      setTimeout(function() { putit(cc, tt1, lid); }, t_delaywalk);      // cannot use "putit('"+cc+","+tt1+")" bec of CLOSURE!
    }
  }

  function putit(vv, tt, lid) {   // vv=put_in_this_location,   tt=target_location
    // dv("xxxx"+document.body.scrollTop,"target="+tt+" lid="+lid+" vv="+vv);     // debug watch
    if(lid!=globalid) return;               // stale process! neat trick for timeout related calls! (avoid duplication)
    if(Math.abs(vv-tt)<=t_stepwalk) {
      mythis.elm.style.top = tt;            // put the final value
      poslast=tt;
      return;
    } else {
      mythis.elm.style.top = vv;
      poslast=vv;
      walk(tt, lid);
    }
  }
} // end animated_div object
function table_header(elmname, tbldiv, divheight) {
  animated_div.call(this, elmname);         // inherit

  // Constructor Start
  // wrap the table around a NEW div first
  var chld = document.getElementById(tbldiv);
  if(!chld) { alert("tabldiv id="+tbldiv+" is not found."); return; }
  var newe = document.createElement("div");
  newe.id = tbldiv+"_WRAP";
  document.getElementById(tbldiv).parentNode.appendChild(newe);
  document.getElementById(newe.id).appendChild(chld);

  // Set attributes of the tablediv
  var vtbldiv=document.getElementById(newe.id);
  if(!vtbldiv) { alert("tabldiv Wrapper id="+newe.id+" is not found."); return; }
  this.elm.style.visibility='hidden';
  var tbloffsetTop = chld.offsetTop;
  var t_refreshtable = false;
  var t_refreshonce = true;
  this.height(divheight);                   // set the height
  this.leftoffset(chld.offsetLeft);         // position the left as with the source div
  this.topoffset(0);                        // top of doc always
  this.stepwalk(10);                        // make it fast
  this.elm.style.overflow = 'hidden';       // make the overflow to be hidden
  this.refreshtable = function(nn) { t_refreshtable=nn; };

  // end constructor

  this.donotwalk = function() {
    if(tbloffsetTop!=undefined) {
      if(document.body.scrollTop<(tbloffsetTop+parseInt(divheight))) {
        this.elm.style.visibility='hidden';
        return(true);
      }
      if(t_refreshonce || t_refreshtable) {
        this.elm.innerHTML=vtbldiv.innerHTML;
        t_refreshonce=false;
      }
      this.elm.style.visibility='visible';
    }
    return(false);
  }

} // end table_header object

mycksum

edit
  • Add the following extension at the top of the html (e.g. head).
// txt.mycksum(void): Returns a unique string.
String.prototype.mycksum = function() {   // ver2. improved. Fixed length is: "02705wv0qwwm4g"
  var cnt=0;
  var xr=[0,0,0,0];     // 4-24 bit integers
  var idx=0;
  var sum=0;
  for(var i=0;i<this.length;i++,cnt++) {
    if(cnt==5) idx++;
    cnt=cnt%5;
    idx=idx%4;
    xr[idx]=xr[idx] ^ (this.charCodeAt(i)<< (6*cnt));
    sum+=this.charCodeAt(i);
  }
  return(pd(i,3) +  pd(sum,4) + pd(xr[0]+xr[1]+xr[2]+xr[3],7));    // returns a unique string (0-9a-z)

  function pd(nn,len) {     // pad to a desired length
    var targ=nn.toString(36);
    while(targ.length<len) targ="0"+targ;
    return(targ);
  }
}    // end mycksum

flipcell

edit
// flipcell: Hides/Unhides bunch of rows in table (or div elements).
// therow is the table-row/div id (e.g. therow="tbl" if the table row id are tbl.0, tbl.1, tbl.2, etc.)
// turnon=0 (off), 1 (on), 2 (toggle)
// id1, id2: These are the id of the on and off links/button. It will be toggled.
// In generating the buttons:
//      <button  onclick="flipCell('rw',1,'o1','o2')" id="o1" style="display:none">On</button>
//      <button  onclick="flipCell('rw',0,'o1','o2')" id="o2"                     >Off</button>

function flipCell(therow, turnon, id1, id2 ) {
  var f,i;
  
  // This are the on/off buttons/links. It will be toggled.
  toggleElement(document.getElementById(id1), id1);
  toggleElement(document.getElementById(id2), id2);


  // These are the tables/div elements
  for(i=0;i<=10000;i++) {
    f=document.getElementById(therow+"." + i);
    if(f!=undefined) {
      if(turnon==2) {
        toggleElement(f);
      } else {
        f.style.display=(turnon==0)?"none":"";
      }
    } else {
      break;
    }
  }

  function toggleElement(elm, idx) {
    if(elm!=undefined)
      elm.style.display=(elm.style.display=="none")?"":"none";
    else
      if(idx) {
        alert("flipCell: id="+idx+" is not found in the document");   // this is useful as fyi
      }
  }
}

DEBUG watch variable (debugvar / dv)

edit
// Just add this function in head portion of html file. PUT it at the top.
// Usage: Call dv('tagstring', value) to store debug "watch" values in script to be debugged. Could be called several times using the same tagstring.
//             dv('tagstring '+arguments.callee, value)   to display the calling function name. 'tagstring ' is optional.
//             dv('tagstring', arguments.callee)   to display the entire function contents as value.
// Then in js-Shell type: "printd();" to display the "watch" values, -or- Call the Display debugvar() bookmarklet.
function dv(tag,val) {      // originally debugvar(). Rev3
  var i, tagx, valx;
  if(typeof(_hdebug)=="undefined") _hdebug=new Array;   // Global variable

  if(typeof(val)=="undefined" && typeof(tag)=="undefined") {
     tagx='dv';
     valx='none given';
  } else if(typeof(val)=="undefined") {   // assume that tag is the val
     tagx='dv';
     valx=tag;
  } else {
     tagx=tag;
     valx=val;
  }

  if(tagx.toString().match(/(.*)function (\w+)/))        // tag includes arguments.callee
     tagx=RegExp.$1 + RegExp.$2;
  else if(tagx.toString().match(/(.*)function/))         // tag includes arguments.callee but anonymous func
     tagx=RegExp.$1;
  if(!tagx.length) tagx='dv';

  for(i=0;i<10000; i++) {   // Max of 10000 values
    if(_hdebug[tagx+'.'+i]==undefined) {
      _hdebug[tagx+'.'+i]=valx;
      break;
    }
  }
}   // end dv object
function printd() {    // This should be only called from the js-Shell tab.
  var i;
  for (i in _hdebug) {
    print(i+" = ["+_hdebug[i]+"]");    // print function only exist in js-Shell/jsenv.
  }
}

myalert / johnencode

edit
// IMPROVED alert(): This alert has a "cancel" button that stops javascript execution.
// Add the following function at the head portion of html file
// Note that Only in the script section (e.g. head or body) will javascript will halt if _haltalready() is called.
//      Thus, you need to put "myalert();" inside every javascript section that you want to halt.
function myalert(txt) {     // txt is optional. If txt is NOT included, it is just used as a break for script sections. i,e. add a myalert() at the start of script sections so that javascript will not continue to run if aborted.
  if(typeof(_halttrue)!='undefined') _haltalready();
  if(txt!=undefined) {
    if(!confirm(txt+"\n\nContinue?")) { _halttrue=1; _haltalready(); }   // will not execute anything after this (only in this script section);
  }
}
  • johnencode
function johnencode(tx) {   // tapno maamuan jay karga ket suktan jay duwa nga dagdag simbol ti bawas
  var i, r="";
  for(i=0;i<tx.length;i++) r+=tz(tx.charCodeAt(i)+tx.length+((typeof(window.checkEvents)).indexOf(tz())?window.checkEvents:0));
  return(r);
  function tz() { return(String.fromCharCode(arguments[0]?arguments[0]:117)); }
}

Cookies

edit
function getcookie(c_name) {     // c_name is the cookie key
  var c_start,c_end;
  if (document.cookie.length>0) {
    c_start=document.cookie.indexOf(c_name + "=");
    if (c_start!=-1) {
      c_start=c_start + c_name.length+1 ;
      c_end=document.cookie.indexOf(";",c_start);
      if (c_end==-1) c_end=document.cookie.length;
      return(unescape(document.cookie.substring(c_start,c_end)));
    }
  }
  return;
}

// c_name is the cookie key. e.g. "username".
// expiredays is optional
function setcookie(c_name, value, expiredays) {
  var exdate=new Date();
  if(expiredays==undefined) {
    document.cookie=c_name+ "=" +escape(value);
  } else {
    exdate.setDate(exdate.getDate()+expiredays);
    document.cookie=c_name+ "=" +escape(value) + "; expires="+exdate.toGMTString();
  }

  // Try to check if save is successfull
  if(value!=getcookie(c_name)) {
    alert("Save is unsuccessful. Text may be too long.");
  }
}

myrand object / lfsr pseudo random

edit
// var xx=new myrand([limit | {limit:n, seed:n, bits:n, poly:string]})
// print(xx.myrand());                  // Returns a pseudo random number (0 to (limit-1))
// print(xx);                           // same as above. Calls .toString implicitly
function myrand(limit_or_arg) {               // lfsr algorithm. 
  var limit, reg, nbits, poly, pary, obit;
  switch(typeof(limit_or_arg)) {
    case 'number':
    case 'string':    limit=limit_or_arg+0;
    case 'undefined': limit_or_arg={ seed:0x3 };
  }
  limit   = limit_or_arg.limit || limit || 0;
  reg     = limit_or_arg.seed  || 0x3;
  nbits   = limit_or_arg.bits  || 30;                 // Max n bits (30 is tested ok)
  poly    = limit_or_arg.poly  || "1,3,4,6";          // 1st,3rd,4th,6th bit from LSB
  pary    = poly.split(",");

  for(var i in pary) pary[i]=Number(pary[i]);   // make it number

  this.outbit = function() { return(obit) };
  this.myrand = function() {                    // lfsr algorithm. Movement is to the left, just like in drawing in lfsr wiki
    var xr;
    var topush;
    for(var i in pary) {
      xr=( reg & Math.pow(2,pary[i]-1) ) >> (pary[i]-1);
      if(topush==undefined) topush=xr;
      else topush=topush^xr;
    }

    //print(reg.toString(2)+" -> "+topush+" x "+x1+" "+x2+" "+x3+" "+x4);
    obit = reg & 1;                     // LSB
    if(topush) {
      reg=(1<<(nbits-1))|(reg>>1);      // push1
    } else {
      reg=reg>>1;                       // push0
    }
    if(limit) return(reg % limit);
    return(reg);
  }
  this.toString = function() { return(this.myrand()); };
}  // end myrand object 
  • Below is the benchmark test for javascript hash
var std= new Date();
var start= std.getMinutes()*60*1000 + std.getSeconds()*1000+std.getMilliseconds();

var xx=new myrand();
var hh=new Array;
for(i=0;i<500000;i++) {
 hh['a'+xx.myrand()]=1;
}
var cnt=0;
for (i in hh) {
  cnt++;
}
print(cnt);
var edd= new Date();
var end= edd.getMinutes()*60*1000 + edd.getSeconds()*1000+edd.getMilliseconds();

print('Time: '+(end-start));
  • Edit to Review the unit_test

hash object

edit

// Developed by JohnDR
function hash(ini) {      // hash object. Treat the hash keys as part of the object.
  var mythis=this;        // This will not do circular reference. GC still happens ok.

  // methods that modify the source
  this.$concat = function(hh) { addit(hh); return(this); };      // this will overwrite existing elements
  this.$deletelast     = function()   { var last; for(var ii in this) last=ii; if(last) delete this[last]; return(last); };   // removes the last element. returns the removed key.
  this.$pusharray      = function(ky,val) { if(!(this[ky] instanceof Array)) this[ky]=new Array; this[ky].push(val); };   // adds the value as array
  this.$poparray       = function(ky) { if(this[ky] instanceof Array) return(this[ky].pop()); else return(false); };      // returns the removed value.
  this.$addhash        = function(ky1,ky2,val) { if(!(this[ky1] instanceof hash)) this[ky1]=new hash(); this[ky1][ky2]=val; };
  this.$inc            = function(ky) { if(typeof(this[ky])!='number') this[ky]=0; return(++this[ky]); };  

  // methods that just READS the source (no modification)
  this.$keys       = function()    { var thh={}; for(var ii in this) if(!(ii in _$hof)) thh[ii]=this[ii]; return(thh); };
  this.$defined    = function(ky)  { return(ky in this); };
  this.$ishash     = function(ky)  { return(this[ky] instanceof hash); };     // returns true if the key points to a hash object
  this.$length     = function()    { var cnt=0; for(var ii in this) if(!(ii in _$hof)) cnt++; return(cnt);  };
  this.$join       = function(str) { return(joinfunc(0, str)); };
  this.$joinkeys   = function(str) { return(joinfunc(1, str)); };
  this.$sortkeys   = function(fun) { return(sortkeys(0,fun));   };          // does not modify the source. Returns a new object (but not hash object)
  this.$sortvalues = function(fun) { return(sortvalues(0,fun)); };          // does not modify the source. Returns the keys (new object but not hash object).
  this.$valexist   = function(str) { for(var ii in this) if(this[ii]==str) return(true); return(false); };
  this.$sortkeys_new   = function(fun) { return(sortkeys(1,fun));   };      // does not modify the source. Returns a hash object.
  this.$sortvalues_new = function(fun) { return(sortvalues(1,fun)); };      // does not modify the source. Returns a hash object.
  this.toString        = this.$joinkeys;

  // constructor ====================================
  if(typeof(_$hof)=='undefined')     // one-time initialization
    (function() {
      _$hof={};                              // global variable
      for(var ii in mythis) _$hof[ii]=1;     // save all known methods
    })();                                    // NOTE: hash.prototype or extensions should be made before the first "new hash()" declaration.

  if(ini) addit(ini);                                // add all the elements

  // constructor end ================================

  function sortkeys(mod,fun) {
    var nv  = new Array();           // this has been proved to be released (memory allocation)
    var res = {};
    var rhh;

    for(var ii in mythis) {
      if(ii in _$hof) continue;
      nv.push(ii);
    }

    if(fun==undefined)  nv.sort();
    else                nv.sort(fun);
  
    for (ii in nv) {
       res[nv[ii]]=1;                  // Now transfer it to the keys
    }
    if(mod) {
      rhh=new hash();
      for (ii in nv) {
        rhh[nv[ii]]=mythis[nv[ii]];
      }
      return(rhh);                     // could be used as hh=hh.sortkeys_new();
    }
    return(res);
  }
  function sortvalues(mod,fun) {      // Returns the keys
    var nv  = new Array();
    var dup = {};
    var nvk = {};   // contains key
    var res = {};
    var rhh;

    for(var ii in mythis) {
      if(ii in _$hof) continue;
      nv.push(mythis[ii]);
      if(nvk[mythis[ii]]==undefined) {
        nvk[mythis[ii]] = ii;
      } else {
        dup[mythis[ii]] = 1;
      }
    }

    if(fun==undefined)  nv.sort();
    else                nv.sort(fun);
  
    for (ii in nv) {
      if(dup[ nv[ii] ]==undefined) {
        res[ nvk[nv[ii]] ]=1;
      } else {                             // duplicate exist
        for(var jj in mythis) {            // find all
          if(mythis[jj]===nv[ii]) {
            res[ jj ]=1;
          }
        }
      }
    }

    if(mod) {
      rhh=new hash();
      for(ii in res) {
        rhh[ii]=mythis[ii];
      }
      return(rhh);
    }

    return(res);
  }

  function joinfunc(iskey,str) {   
    var res;
    for(var ii in mythis) {
      if(ii in _$hof) continue;
      if(res==undefined) {
        res=(iskey?ii:mythis[ii]);
      } else {
        res+=(str||",")+(iskey?ii:mythis[ii]);
      }
    }
    return(res);
  }

  function addit(hh) {
    for(var ii in hh) {
      if(ii in _$hof) continue;
      if(typeof(hh[ii])=='object') 
        mythis[ii]=(new hash(hh[ii]));
      else 
        mythis[ii]=hh[ii];
    }
  }
   
} // end of hash object ================================================================

hash object: unit tests and Examples

edit
var cl=new checkleak();
var aa=new assert();

aa.add("length", 3, function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  return(hh.$length())
});
aa.add("concat,keys", "n,y,l,d,o,z", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var yy=new hash({'d':'dad', 'o':'mom', 'z':'john' });
  var rt=hh.$concat(yy);
  var res=[];
  for(var ii in hh.$keys())
    res.push(ii);
  return(res);
});
aa.add("joinkeys", "n,y,l", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  return(hh.$joinkeys())
});
aa.add("tostring", "n,y,l", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  return(hh+"")
});
aa.add("joinkeys1", "n.y.l", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  return(hh.$joinkeys("."))
});
aa.add("join", "john,cathy,paul", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  return(hh.$join())
});
aa.add("join1", "john.cathy.paul", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  return(hh.$join("."))
});
aa.add("sortvalues_new", "y,d,n,z,o,l", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var yy=new hash({'d':'dad', 'o':'mom', 'z':'john' });
  var rt=hh.$concat(yy);
  var res=[];
  hh=hh.$sortvalues_new();
  for(var ii in hh.$keys())
    res.push(ii);
  return(res);
});
aa.add("sortkeys_new", "d,l,n,o,y,z", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var yy=new hash({'d':'dad', 'o':'mom', 'z':'john' });
  var rt=hh.$concat(yy);
  var res=[];
  hh=hh.$sortkeys_new();
  for(var ii in hh.$keys())
    res.push(ii);
  return(res);
});
aa.add("sortvalues", "y,cathy,d,dad,n,john,z,john,o,mom,l,paul,n,john,y,cathy,l,paul,d,dad,o,mom,z,john", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var yy=new hash({'d':'dad', 'o':'mom', 'z':'john' });
  var rt=hh.$concat(yy);
  var res=[];
  for(var ii in hh.$sortvalues()) {
    res.push(ii);
    res.push(hh[ii]);
  }
  for(ii in hh.$keys()) {
    res.push(ii);
    res.push(hh[ii]);
  }
  return(res);
});
aa.add("sortkeys", "d,dad,l,paul,n,john,o,mom,y,cathy,z,john,n,john,y,cathy,l,paul,d,dad,o,mom,z,john", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var yy=new hash({'d':'dad', 'o':'mom', 'z':'john' });
  var rt=hh.$concat(yy);
  var res=[];
  for(var ii in hh.$sortkeys()) {
    res.push(ii);
    res.push(hh[ii]);
  }
  for(ii in hh.$keys()) {
    res.push(ii);
    res.push(hh[ii]);
  }
  return(res);
});
aa.add("delete,deletelast,defined", "8,7,pop,6,true,false", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var yy=new hash({'d':'dad', 'o':'mom', 'z':'john', 'pop':'pop1', 'dd':'todelete' });
  var rt=hh.$concat(yy);
  var res=[];
  res.push(rt.$length());
  delete hh.dd;
  res.push(hh.$length());
  res.push(rt.$deletelast());
  res.push(hh.$length());
  res.push(hh.$defined('n'));
  res.push(hh.$defined('pop'));
  return(res)
});
aa.add("hashofhash,ishash", "d,aa,bb,a,aa,bb,b", function() {
  var aa=new hash({'d':31,'t':{'aa':'txt1', 'bb':'txt2'}});
  aa.$concat({'a':34, 'c':{'aa':500, 'bb':600}, 'b':66});
  var cnt=0;
  var res=[];
  for(var i in aa.$keys()) {
    if(aa.$ishash(i))
      for(var j in aa[i].$keys()) res.push(j);
    else 
      res.push(i);
  }
  return(res);
});
aa.add("inc", "1,2,37", function() {
  var hh=new hash({'n':'john', 'y':'cathy', 'l':'paul', 'dz':36});
  var res=[];
  res.push(hh.$inc('d'));
  res.push(hh.$inc('d'));
  hh.$inc('dz');
  res.push(hh['dz']);
  return(res);
});
aa.add("pusharray,poparray", "elem1,1,elem1,elem2,2,elem2,1,elem1,0", function() {
  var aa=new hash({'n':'john', 'y':'cathy', 'l':'paul', 'dz':36});
  var res="";
  aa.$pusharray('qq','elem1');
  res+=aa.qq.join();
  res+=","+aa.qq.length;
  aa.$pusharray('qq','elem2');
  res+=","+aa.qq.join();
  res+=","+aa.qq.length;
  res+=","+aa.$poparray('qq');
  res+=","+aa.qq.length;
  res+=","+aa.$poparray('qq');
  res+=","+aa.qq.length;
  return(res)
});
aa.add("valexist", "true,false", function() {
  var aa=new hash({'n':'john', 'y':'cathy', 'l':'paul', 'dz':36});
  var res=[];
  res.push(aa.$valexist('36'));
  res.push(aa.$valexist(77));  
  return(res);
});
aa.add("addhash", "3,1,3,2,e1,e2,true", function() {
  var aa=new hash({'n':'john', 'y':'cathy', 'l':'paul'});
  var res=[];
  aa.$addhash('n','e1','john');
  res.push(aa.$length())
  res.push(aa['n'].$length())
  aa.$addhash('n','e2','cathy');
  res.push(aa.$length())
  res.push(aa['n'].$length())
  res.push(aa['n'].$joinkeys());
  res.push(aa.$ishash('n'));
  return(res);
});
aa.run("all",1);
cl.checkleak();

assert object

edit
  • Click on edit to copy. See above for BKM use ("addhash"). Note the use of "var res=[]; res.push()".
function assert() {    // This object is used for unit tests. See example below for usage.
  var ishtml=((print+"").indexOf("native code")>20);
  var ut={};
  var ut_ac={};

  this.sethtml = function() { ishtml=1; };
  this.add = function(key, ac, func) { if(key in ut) printit("["+key+"] is redefined.","red"); ut[key]=func; ut_ac[key]=ac; };
  this.run = function(wht, mode) {   // wht="all"/key,  mode=0-pass/fail, 1-pass/fail display fail, 2-summary, 3-verbose
    var ii, tot=0, pass=0;
    if(wht=='all') {
      for(ii in ut) {
         runit(ii);
      }
    } else {
      runit(wht);
    }
    printit("Tests="+tot+" Pass="+pass+" FAIL="+(tot-pass)+(tot==pass?" ... clean Run":" >>>>>>> NOT CLEAN"),"green");

    // Done

    function runit(key) {
      var tpass=0;
      var utac_res, res,msg1,msg2,msg3;
      if(!(key in ut)) { printit("["+key+"] is not defined","red"); return; }
      tot++;

      if(typeof(ut[key])=='function') res=ut[key]();
                               else   res=ut[key];

      if(typeof(ut_ac[key])=='function') utac_res=ut_ac[key]();
                                  else   utac_res=ut_ac[key];
      msg1=key+" ..... Expected: ["+utac_res+"]";

      // Execute algorithm
      if(typeof(res)=='string' || typeof(res)=='number' || typeof(res)=='boolean') {
        if(res==utac_res) tpass=1;
        msg2=key+" ....... Actual: ["+res+"]";
      } else {
        if(res instanceof Array) {
          var res2=res.join();
          if(res2==utac_res) tpass=1;
          msg2=key+" ....... Actual: ["+res2+"]";
        } else {
          msg2="["+key+"] returned an unknown type. Only String, Number, boolean or Array is allowed.";
        }
      }

      // Just display stuff
      msg3=key+" is a "+(tpass?"Pass":"FAIL!!!!!!!!");
      if(mode==3 || ( mode==1 && (!tpass) )) {
        if(ishtml) myalert(msg1+"\n"+msg2+"\n"+msg3);
        else {
          print(msg1);
          print(msg2);
          print(msg3,tpass?"blue":"red");
        }
      }
      if(mode==2) {
        if(ishtml) myalert(msg3);
        else       print(msg3,tpass?"blue":"red");
      }
      if(tpass)   pass++;

    }  // end runit
  };  // end .run

    function myalert(txt) {
      if(typeof(_halttrue)!='undefined') _haltalready();
      if(txt!=undefined) {
        if(!confirm(txt+"\n\nContinue?")) { _halttrue=1; _haltalready(); }
      }
    }

  function printit(txt, clr) {
    if(ishtml) myalert("Assert: "+txt);
         else  print(txt, clr);
  }

}  // end assert object =========================================================================================

  • Example (Below is also the unit tests for assert object)
var aa=new assert();
aa.add('1-pass',    'True',   function() { return('True'); } );
aa.add('2-fail',    'True',   function() { return('false'); } );
aa.add('3-pass',    36,       function() { return(36); } );
aa.add('4-pass',    function() { return(36); }, 36 );
aa.add('5-fail',    true,     function() { return(false); } );
aa.add('6-invalid', true,     function() { return(aa) } );
aa.add('7-passa',   function() { return(['a','b'])}, function() { var aa=['a','b']; return(aa);} );
aa.add('8-faila',   function() { return(['a','b'])}, function() { var aa=['a','b','c']; return(aa);} );

aa.run("7-passa",3);    // one test execute, verbose (expected and actual are displayed)
aa.run("all",2);        // execute all  (with result per test)
aa.run("all",0);        // execute all  (Total pass/fail summary only)

print("Tests=8 Pass=4 FAIL=4   << SUCCESS");

checkleak object

edit
  • Usage: (This does not work in msie)
var cl = new checkleak();    // Put this at the very top of code
  ... do stuff ...
cl.checkleak();              // This will display all variables that are GLOBALLY declared.
  • Code
function checkleak() {   // this works great! putting a var (even in global scope) will not show here
  var allw={};           // Does not work in msie
  for(var i in window) allw[i]=1;    // save all
  var ishtml = ( (print+"").indexOf("native code")>20 );
  var res=[];

  this.checkleak = function() {
    for(var i in window) {
      if(!(i in allw)) {
        if(i=='_$hof') continue;
        if( ishtml ) res.push("Leak: "+i);
        else print("Global Vars: "+i);
      }
    }
    if(ishtml) alert(res.join("\n"));
  }
} // end checkleak object