This file contains a collection of miscellaneous functions with a global scope. They are mostly very simple but create a necessary foundation for the YGWM code and provide useful functions for other modules.
The coding style is "compact" and comments can be removed by a module. Compaction may also change (shorten) some names so single-character variable names are widely used.
Let's start with a couple of shortcuts. They are used a lot in YGWM and in user modules, they contain lines that are quite cumbersome to write in full.
function dcE(a){ return document.createElement(a); } function rC(e){ e.parentNode.removeChild(e); }
The classic functions are not enough, so I wrapped them with some personal code, that can restrict and accelerate execution.
// Beware : getById and getByClass are different ! function getById(id,tag,base) { base=base||document; tag=tag||"*"; var i, t=base.getElementsByTagName(tag); for (i=0; i<t.length; i++) { if (t[i].id && (t[i].id == id)) return t[i]; } return null; } function getByClass(className,tag,base) { base=base||document; tag=tag||"*"; var i, t=base.getElementsByTagName(tag); for (i=0; i<t.length; i++) { if(t[i].className && (t[i].className==className)) return t[i]; } return null; }
The following code locates a DIV, and either creates or removes an element inside it.
var container=getById("container","DIV"); var counter=1; getById("create").onclick=function() { var d=dcE("BUTTON"); d.innerHTML=counter++; container.appendChild(d); } getById("erase").onclick=function() { rC(container.firstChild); } ""
The following function is one of the most used User Interface gadget: it switches an element from hidden to visible (or vice versa). Note that it is different from making the element visible and still take its space on the screen.
function toggle(a){ a=a.style; return a.display =(a.display=="none")?"block":"none"; }
getById("showhide").onclick=function() { toggle(getById("showme")); } ""
Those functions read coordinates and dimensions in a mostly portable way.
function mousePos(e) { e = e || window.event; var t = document.body || document.documentElement; return { x: e.pageX || e.clientX + t.scrollLeft, y: e.pageY || e.clientY + t.scrollTop }; }
See the delay() example for how to use mousePos().
function getHeight(){ return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; } function getWidth(){ return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; }
"Window size: "+getHeight()+"x"+getWidth()
Some places need to specify a callback function but it is not always desired or available. Writing emptyFunc is shorter than function(){}...
function emptyFunc(a){ // empty, as the name says return a; // yeah... but useful anyway here and there }
In certain cases, you need to let the display be refreshed before doing something else. The display is updated when there is nothing left to do, so just stop doing anything, terminate all the functions. But how to then do "something else" ? The solution is to register it as a function and call redraw() with this function as argument.
I've had some issues with some browsers, I hope that it works well now. Webkit-based browsers might still be problematic...
i="requestAnimationFrame"; redraw=window[i] ||window["moz"+i] ||window["webkit"+i] ||window["ms"+i] ||function(f){ setTimeout(f,0); };
The current code thread (like processing an event) must end before the next action (indicated as an argument) can be executed, thus it does not matter where exactly you call this function, but beware anyway.
The following code is another important feature that helps to keep the user interface responsive. It executes a callback function when the delay function has not been called for a certain time, or when it has been called a certain number of times. If you have graphic elements to update, for example, they can be updated less often and with more relevant informations.
// Generic anti-congestion for the UI : // gets an object and performs the action after a delay or until a counter reached 0 function delay(o){ if (o.timer){ clearTimeout(o.timer); o.timer=null; // necessary ? } if (o.max) { if (o.counter){ o.timer=setTimeout(function(){ o.counter=o.max; o.timer=null; // necessary ? o.func(); },o.delay); o.counter--; } else { o.func(); o.counter=o.max; } } else { // max is absent : just delay. o.timer=setTimeout(function(){ o.timer=null; // necessary ? o.func(); },o.delay); } }
The argument is an object with the following elements :
delayObj:{ delay:300, // ms max:10, // number of calls until the function is forcefully called counter:10, // you can initialise the counter timer:null, // The return value of setTimeout is preserved so it can be cancelled by clearTimeout func:function(){ // do something here } }
There are two ways to use this tool :
This behaves like a retriggerable monostable : as long as you call this function often enough, it will not be executed. Only the last call will be executed after a set delay. All the previous calls will be lost.
This is used by User Interface elements, such as when moving windows, to prevent events from overloading the computer with heavy graphical operations. In this case, omit the max element in delayObj
Providing user feedback from the UI sometimes requires that one out of every N events is normally processed. This N can be given in the max element of the delay object.
You may even control how soon the release is called by setting the counter element to 0 for example)
var div=null; var mode=0; var coord={}; function move_div(){ if (div) { // div might have been erased when setTimeout executes div.style.top=coord.y+2; div.style.left=coord.x+2; } } var mouse_delay={ delay:300, // ms max:10, // number of calls until the function is forcefully called counter:10, // you can initialise the counter func: move_div } function MouseMov(e){ coord=mousePos(e); if (mode==0) move_div(); else delay(mouse_delay); } getById("normal").onclick=function() { mode=0; } getById("blocked").onclick=function() { mode=1; mouse_delay.max=0; mouse_delay.counter=0; } getById("released").onclick=function() { mode=2; mouse_delay.max=10; mouse_delay.counter=10; } getById("startstop").onclick=function() { if (div==null) { div=dcE("DIV"); var s=div.style; s.position="absolute"; s.height= s.width="50"; s.backgroundColor="#CD2"; document.body.appendChild(div); document.onmousemove = MouseMov; } else { document.onmousemove = null; rC(div); div=null; } } null
When you import HTML code, the DOM can be cluttered with extra elements that are more a disturbance than anything else. So I wrote some code to clean tables and remove the parasites.
////////////////////////////////////////////////////// // cleanup a table, remove any #text and #comment // nodes inbetween the TR TH and TDs (proved to be necessary......) function cleanup_table(id) { var t=id.firstChild, t2; // scan the tbody while (t) { // preload the next sibling because we might remove t t2=t.nextSibling; switch(t.nodeName) { case "TBODY": // recurse case "TR": // recurse //alert("recursing in "+t.nodeName); cleanup_table (t); // fold ! case "TH": case "TD": break; // ne fait rien, passe au suivant default : // enlève le tag (c'est un screugneugneu de #text) //alert("removing "+t.nodeName); rC(t); } t=t2; } }
function explore_DOM(e) { var s=""; e=e.firstChild; while(e) { s+=e.nodeName; if (e.textContent!="") s+=':"'+e.textContent+'"'; if (e.firstChild) s+="["+explore_DOM(e)+"]"; s+=','; e=e.nextSibling; } return s; } var test=getById("table_test","TABLE"); var m=explore_DOM(test); m+="\n\n Now let's clean this up :\n\n"; cleanup_table(test); // note: this is destructive m+=explore_DOM(test); m;table_test:
This | is | a |
---|---|---|
simple | test | table |
The following code is used extensively by modules that reside in windows. For example, when you click on an element, the code needs some context, that is often associated to the YGWM window object. parentWin navigates the DOM to find the parent windows.
function parentWin(w){ // get back to the root of the tree of a window, searching a content_id while (w) { if (w.contents_id) break; w=w.parentNode; } return w; }
Examples will be shown in later pages.
Random numbers are used and this function makes them shorter to write and get:
function intrnd(x) { return Math.floor(Math.random()*x); }
Generated numbers range from 0 to x-1.
var m=[], i; for (i=0; i<10; i++) m.push(intrnd(10)); m;
Those are quite handy too : they add spaces so text is aligned.
function pad_right(s, n) { n-=(s+"").length; while (n-- > 0) s+=" "; return s; } function pad_left(s, n) { n-=(s+"").length; while (n-- > 0) s=" "+s; return s; }
var m="", n="", i; for (i=0; i<12; i++){ m+=pad_left(i,2)+":"+pad_right("foo",i)+"↲\n"; n+=pad_left(i,2)+":"+pad_left ("bar",i)+"↲\n"; } m+'\n'+n;