Library
edittooltip
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
editfunction 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- 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 ================================================================
hash object: unit tests and Examples
editvar 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