User:JohnDR/js
From Wikipedia, the free encyclopedia
[edit] Library
[edit] tooltip
- 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.
[edit] tooltip code
- 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
[edit] sortable
// 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
[edit] addEvent
- 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");
}
}
[edit] animated_div
- 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.
[edit] animated_div code
- 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
[edit] mycksum
- 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
[edit] flipcell
// 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
}
}
}
[edit] DEBUG watch variable (debugvar / dv)
// 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.
}
}
[edit] myalert / johnencode
// 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)); }
}
[edit] Cookies
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.");
}
}
[edit] myrand object / lfsr pseudo random
// 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
[edit] hash object
- Usage on Hash Reference
- Code (Click on Edit to Copy)
// 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 ================================================================
[edit] hash object: unit tests and Examples
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();
[edit] assert object
- 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");
[edit] checkleak object
- 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

