initial commit
[dbsrgits/dbix-class-introduction-presentation.git] / ui / graphic_support / fixed.js
1 // fixed.js: fix fixed positioning and fixed backgrounds in IE/Win\r// version 1.8, 08-Aug-2003\r// written by Andrew Clover <and@doxdesk.com>, use freely\r\r/*@cc_on\r@if (@_win32 && @_jscript_version>4)\r\rvar fixed_positions= new Array();\rvar fixed_backgrounds= new Array();\rvar fixed_viewport;\r\r// Initialisation. Called when the <body> tag arrives. Set up viewport so the\r// rest of the script knows we're going, and add a measurer div, used to detect\r// font size changes and measure image sizes for backgrounds later   \r\rfunction fixed_init() {\r  fixed_viewport= (document.compatMode=='CSS1Compat') ?\r    document.documentElement : document.body;\r  var el= document.createElement('div');\r  el.setAttribute('id', 'fixed-measure');\r  el.style.position= 'absolute';\r  el.style.top= '0'; el.style.left= '0';\r  el.style.overflow= 'hidden'; el.style.visibility= 'hidden';\r  el.style.fontSize= 'xx-large'; el.style.height= '5em';\r  el.style.setExpression('width', 'fixed_measureFont()');\r  document.body.insertBefore(el, document.body.firstChild);\r}\r\r// Binding. Called every time an element is added to the document, check it\r// for fixed features, if found add to our lists and set initial props   \r\rfunction fixed_bind(el) {\r  var needLayout= false;\r  var tag= el.tagName.toLowerCase();\r  var st= el.style;\r  var cst= el.currentStyle;\r  var anc;\r\r  // find fixed-position elements\r  if (cst.position=='fixed') {\r    needLayout= true;\r    fixed_positions[fixed_positions.length]= el;\r    // store original positioning as we'll overwrite it\r    st.position= 'absolute';\r    st.fixedPLeft=   cst.left;\r    st.fixedPTop=    cst.top;\r    st.fixedPRight=  cst.right;\r    st.fixedPBottom= cst.bottom;\r    st.fixedPWidth=  fixed_parseLength(cst.width);\r    st.fixedPHeight= fixed_parseLength(cst.height);\r    // find element that will act as containing box, for convenience later\r    st.fixedCB= null;\r    for (anc= el; (anc= anc.parentElement).parentElement;) {\r      if (anc.currentStyle.position!='static') {\r        st.fixedCB= anc;\r        break;\r    } }\r    // detect nested fixed positioning (only ancestor need move)\r    st.fixedNest= false;\r    for (anc= el; anc= anc.parentElement;) {\r      if (anc.style.fixedNest!=null)\r        st.fixedNest= true;\r        break;\r    }\r  }\r\r  // find fixed-background elements (not body/html which IE already gets right)\r  if (cst.backgroundAttachment=='fixed' && tag!='body' && tag!='html') {\r    needLayout= true;\r    fixed_backgrounds[fixed_backgrounds.length]= el;\r    // get background offset, converting from keyword if necessary\r    st.fixedBLeft= fixed_parseLength(cst.backgroundPositionX);\r    st.fixedBTop=  fixed_parseLength(cst.backgroundPositionY);\r    // if it's a non-zero %age, need to know size of image for layout\r    if (st.fixedBLeft[1]=='%' || st.fixedBTop[1]=='%') {\r      st.fixedBWidth= 0; st.fixedBHeight= 0;\r      fixed_measureBack(el);\r    }\r  }\r  if (needLayout) fixed_layout();\r}\r\r// Layout. On every window or font size change, recalculate positioning   \r\r// Request re-layout at next free moment\rvar fixed_delaying= false;\rfunction fixed_delayout() {\r  if (fixed_delaying) return;\r  fixed_delaying= true;\r  window.setTimeout(fixed_layout, 0);\r}\r\rvar fixed_ARBITRARY= 200;\r\rfunction fixed_layout() {\r  fixed_delaying= false;\r  if (!fixed_viewport) return;\r  var i, el, st, j, pr, tmp, A= 'auto';\r  var cb, cbLeft, cbTop, cbRight, cbBottom, oLeft, oTop, oRight, oBottom;\r  var vpWidth=fixed_viewport.clientWidth, vpHeight=fixed_viewport.clientHeight;\r\r  // calculate initial position for fixed-position elements [black magic]\r  for (i= fixed_positions.length; i-->0;) {\r    el= fixed_positions[i]; st= el.style;\r    // find positioning of containing block\r    cb= st.fixedCB; if (!cb) cb= fixed_viewport;\r    cbLeft= fixed_pageLeft(cb); cbTop= fixed_pageTop(cb);\r    if (cb!=fixed_viewport) { cbLeft+= cb.clientLeft; cbTop+= cb.clientTop; }\r    cbRight= fixed_viewport.clientWidth-cbLeft-cb.clientWidth;\r    cbBottom= fixed_viewport.clientHeight-cbTop-cb.clientHeight;\r    // if size is in %, must recalculate relative to viewport\r    if (st.fixedPWidth[1]=='%')\r      st.width= Math.round(vpWidth*st.fixedPWidth[0]/100)+'px';\r    if (st.fixedPHeight[1]=='%')\r      st.height= Math.round(vpHeight*st.fixedPHeight[0]/100)+'px';\r    // find out offset values at max size, to account for margins\r    st.left= A; st.right= '0'; st.top= A; st.bottom= '0';\r    oRight= el.offsetLeft+el.offsetWidth; oBottom= el.offsetTop+el.offsetHeight;\r    st.left= '0'; st.right= A; st.top= '0'; st.bottom= A;\r    oLeft= el.offsetLeft; oTop= el.offsetTop;\r    // use this to convert all edges to pixels\r    st.left= A; st.right= st.fixedPRight;\r    st.top= A; st.bottom= st.fixedPBottom;\r    oRight-= el.offsetLeft+el.offsetWidth;\r    oBottom-= el.offsetTop+el.offsetHeight;\r    st.left= st.fixedPLeft; st.top= st.fixedPTop;\r    oLeft= el.offsetLeft-oLeft; oTop= el.offsetTop-oTop;\r    // edge positioning fix\r    if (st.fixedPWidth[1]==A && st.fixedPLeft!=A && st.fixedPRight!=A) {\r      tmp= el.offsetLeft; st.left= A; st.width= fixed_ARBITRARY+'px';\r      tmp= fixed_ARBITRARY+el.offsetLeft-tmp+cbLeft+cbRight;\r      st.left= st.fixedPLeft; st.width= ((tmp<1)?1:tmp)+'px';\r    }\r    if (st.fixedPHeight[1]==A && st.fixedPTop!=A && st.fixedPBottom!=A) {\r      tmp= el.offsetTop; st.top= A; st.height= fixed_ARBITRARY+'px';\r      tmp= fixed_ARBITRARY+el.offsetTop-tmp+cbTop+cbBottom;\r      st.top= st.fixedPTop; st.height= ((tmp<1)?1:tmp)+'px';\r    }\r    // move all non-auto edges relative to the viewport\r    st.fixedCLeft= (st.fixedPLeft=='auto') ? oLeft : oLeft-cbLeft;\r    st.fixedCTop= (st.fixedPTop=='auto') ? oTop : oTop-cbTop;\r    st.fixedCRight= (st.fixedPRight=='auto') ? oRight : oRight-cbRight;\r    st.fixedCBottom= (st.fixedPBottom=='auto') ? oBottom : oBottom-cbBottom;\r    // remove left-positioning of right-positioned elements\r    if (st.fixedPLeft=='auto' && st.fixedPRight!='auto') st.fixedCLeft= 'auto';\r    if (st.fixedPTop=='auto' && st.fixedPBottom!='auto') st.fixedCTop= 'auto';\r  }\r\r\r  // calculate initial positioning of fixed backgrounds\r  for (i= fixed_backgrounds.length; i-->0;) {\r    el= fixed_backgrounds[i]; st= el.style;\r    tmp= st.fixedBImage;\r    if (tmp) {\r      if (tmp.readyState!='uninitialized') {\r        st.fixedBWidth= tmp.offsetWidth;\r        st.fixedBHeight= tmp.offsetHeight;\r        st.fixedBImage= window.undefined;\r      }\r    }\r    st.fixedBX= fixed_length(el, st.fixedBLeft, vpWidth-st.fixedBWidth);\r    st.fixedBY= fixed_length(el, st.fixedBTop, vpHeight-st.fixedBHeight);\r  }\r\r  // now call scroll() to set the positions from the values just calculated\r  fixed_scroll();\r}\r\r// Scrolling. Offset fixed elements relative to viewport scrollness\r\rvar fixed_lastX, fixed_lastY;\rvar fixed_PATCHDELAY= 300;\rvar fixed_patching= false;\r\r// callback function after a scroll, because incorrect scroll position is\r// often reported first go!\rfunction fixed_patch() {\r  fixed_patching= false;\r  var scrollX= fixed_viewport.scrollLeft, scrollY= fixed_viewport.scrollTop;\r  if (scrollX!=fixed_lastX && scrollY!=fixed_lastY) fixed_scroll();\r}\r\rfunction fixed_scroll() {\r  if (!fixed_viewport) return;\r  var i, el, st, viewportX, viewportY;\r  var scrollX= fixed_viewport.scrollLeft, scrollY= fixed_viewport.scrollTop;\r  fixed_lastX= scrollX; fixed_lastY= scrollY;\r\r  // move non-nested fixed-position elements\r  for (i= fixed_positions.length; i-->0;) {\r    st= fixed_positions[i].style;\r    viewportX= (st.fixedNest) ? 0 : scrollX;\r    viewportY= (st.fixedNest) ? 0 : scrollY;\r    if (st.fixedCLeft!='auto') st.left= (st.fixedCLeft+viewportX)+'px';\r    if (st.fixedCTop!='auto') st.top= (st.fixedCTop+viewportY)+'px';\r    viewportX= (st.fixedCB==null || st.fixedCB==fixed_viewport) ? 0 : viewportX;\r    viewportY= (st.fixedCB==null || st.fixedCB==fixed_viewport) ? 0 : viewportY;\r    st.right= (st.fixedCRight-viewportX+1)+'px'; st.right= (st.fixedCRight-viewportX)+'px';\r    st.bottom= (st.fixedCBottom-viewportY+1)+'px'; st.bottom= (st.fixedCBottom-viewportY)+'px';\r  }\r\r  // align fixed backgrounds to viewport\r  for (i= fixed_backgrounds.length; i-->0;) {\r    el= fixed_backgrounds[i]; st= el.style;\r    viewportX= scrollX;\r    viewportY= scrollY;\r    while (el.offsetParent) {\r      viewportX-= el.offsetLeft+el.clientLeft;\r      viewportY-= el.offsetTop +el.clientTop;\r      el= el.offsetParent;\r    }\r    st.backgroundPositionX= (st.fixedBX+viewportX)+'px';\r    st.backgroundPositionY= (st.fixedBY+viewportY)+'px';\r  }\r\r  // call back again in a tic\r  if (!fixed_patching) {\r    fixed_patching= true;\r    window.setTimeout(fixed_patch, fixed_PATCHDELAY);\r  }\r}\r\r// Measurement. Load bg-image into an invisible element on the page, when\r// loaded write the width/height to an element's style for layout use; detect\r// when font size changes\r\rfunction fixed_measureBack(el) {\r  var measure= document.getElementById('fixed-measure');\r  var img= document.createElement('img');\r  img.setAttribute('src', fixed_parseURL(el.currentStyle.backgroundImage));\r  measure.appendChild(img);\r  el.style.fixedBImage= img;\r  if (img.readyState=='uninitialized')\r    img.attachEvent('onreadystatechange', fixed_measureBackImage_ready);\r}\r\rfunction fixed_measureBackImage_ready() {\r  var img= event.srcElement;\r  if (img && img.readyState!='uninitialized') {\r    img.detachEvent('onreadystatechange', fixed_measureBackImage_ready);\r    fixed_layout();\r  }\r}\r\rvar fixed_fontsize= 0;\rfunction fixed_measureFont() {\r  var fs= document.getElementById('fixed-measure').offsetHeight;\r  if (fixed_fontsize!=fs && fixed_fontsize!=0)\r    fixed_delayout();\r  fixed_fontsize= fs;\r  return '5em';\r}\r\r// Utility. General-purpose functions\r\r// parse url() to get value inside\r\rfunction fixed_parseURL(v) {\r  v= v.substring(4, v.length-1);\r  if (v.charAt(0)=='"' && v.charAt(v.length-1)=='"' ||\r      v.charAt(0)=="'" && v.charAt(v.length-1)=="'")\r    return v.substring(1, v.length-1);\r  else return v;\r}\r\r// parse length or auto or background-position keyword into number and unit\r\rvar fixed_numberChars= '+-0123456789.';\rvar fixed_ZERO= new Array(0, 'px');\rvar fixed_50PC= new Array(50, '%');\rvar fixed_100PC= new Array(100, '%');\rvar fixed_AUTO= new Array(0, 'auto');\r\rfunction fixed_parseLength(v) {\r  var num, i;\r  if (v=='left'  || v=='top')    return fixed_ZERO;\r  if (v=='right' || v=='bottom') return fixed_100PC;\r  if (v=='center') return fixed_50PC;\r  if (v=='auto')   return fixed_AUTO;\r  i= 0;\r  while (i<v.length && fixed_numberChars.indexOf(v.charAt(i))!=-1)\r    i++;\r  num= parseFloat(v.substring(0, i));\r  if (num==0) return fixed_ZERO;\r  else return new Array(num, v.substring(i));\r}\r\r// convert parsed (number, unit) into a number of pixels\r\rfunction fixed_length(el, l, full) {\r  var tmp, x;\r  if (l[1]=='px') return l[0];\r  if (l[1]=='%')  return Math.round(full*l[0]/100);\r  // other units - measure by setting position; this is rather inefficient\r  // but then these units are used for background-position so seldom...\r  tmp= el.currentStyle.left;\r  el.style.left= '0';\r  x= el.offsetLeft;\r  el.style.left= l[0]+l[1];\r  x= el.offsetLeft-x;\r  el.style.left= tmp;\r  return x;\r}\r\r// convert stupid IE offsetLeft/Top to page-relative values\r\rfunction fixed_pageLeft(el) {\r  var v= 0;\r  while (el.offsetParent) {\r    v+= el.offsetLeft;\r    el= el.offsetParent;\r  }\r  return v;\r}\rfunction fixed_pageTop(el) {\r  var v= 0;\r  while (el.offsetParent) {\r    v+= el.offsetTop;\r    el= el.offsetParent;\r  }\r  return v;\r}\r\r// Scanning. Check document every so often until it has finished loading. Do\r// nothing until <body> arrives, then call main init. Pass any new elements\r// found on each scan to be bound   \r\rvar fixed_SCANDELAY= 500;\r\rfunction fixed_scan() {\r  if (!document.body) return;\r  if (!fixed_viewport) fixed_init();\r  var el;\r  for (var i= 0; i<document.all.length; i++) {\r    el= document.all[i];\r    if (!el.fixed_bound) {\r      el.fixed_bound= true;\r      fixed_bind(el);\r  } }\r}\r\rvar fixed_scanner;\rfunction fixed_stop() {\r  window.clearInterval(fixed_scanner);\r  fixed_scan();\r}\r\rfixed_scan();\rfixed_scanner= window.setInterval(fixed_scan, fixed_SCANDELAY);\rwindow.attachEvent('onload', fixed_stop);\rwindow.attachEvent('onresize', fixed_delayout);\rwindow.attachEvent('onscroll', fixed_scroll);\r\r@end @*/\r