
/** fcnylight core javascript functions
 *  Copyright 2005 Fund for the City of New York
 *  Please see http://www.fcny.org/fcnylight
 *
 *  ... we should make this an object
 *  Also note, that in many cases functionality here is duplicated in MochiKit.
 *  The MochiKit variants are preferred for portability.
 */



// gebid is an alias of document.getElementById() that is much easier to type
function gebid( id ) {
  return document.getElementById( id );
}

// introspect drops a handy property dump into a popup window
function introspect( obj ) {
  if ( !obj ) return;
  var result = "introspection of " + String(obj) + " " + obj.id + "<pre>{\n";
  if ( 1 || obj.id ) {
    for (var ix in obj) {
        if ( ix != 'selectionStart' && ix != 'selectionEnd' ) {
          result +=  '.' + ix + " = " + obj[ix] + "\n";
        }
        else {
          result +=  '.' + ix + " = unprintable by introspect() \n";
        }
    }
  }
  
  result = result + "}\n</pre>";
  inspector = window.open("","inspector", "width=420,height=640,resizable,scrollbars");
  if ( !inspector ) {
    alert("Popup windows are blocked!" );
  }
  else {
    inspector.document.writeln( result );
  }
}

function debug( mystring, eleId ) {
  try {
    if( eleId == null ) eleId = 'debugOutput';
    var output = gebid(eleId);
    if( output == null ) {
      output = document.createElement( "div" );
      output.id = eleId;
      document.appendChild(output);
    }

    output.innerHTML += "\n<pre>"+mystring+"</pre>";
    
    //    var t = document.createTextNode(mystring);
    //    t.innerHTML += "\n<br/>";
    //    output.appendChild(t);
  }
  catch( e ) {
    
  }
}

// will print out to textarea
function debug2( mystring, eleId ) {
  if( eleId == null ) eleId = 'debugOutput2';
  var output = gebid(eleId);
  if( output != null ) {
    if( output.value == null ) output.value = '';
    output.value += "\n"+mystring;
  }
}

// debug alias from this core file
var cdebug = debug;

// escapes HTML entities
function h( mystring ) {
  var div = document.createElement( "div" );
  var text = document.createTextNode( mystring );
  div.appendChild( text );
  return div.innerHTML;
}

// snaps one object to another
function snap( subject, target ) {
  subject.style.position = 'absolute';
  subject.style.zIndex = 99;
  offsetTop = target.offsetTop;
  offsetLeft = target.offsetLeft;
  parent = target.offsetParent;
  while(parent) {
    offsetTop = offsetTop + parent.offsetTop;
    offsetLeft = offsetLeft + parent.offsetLeft;
    parent = parent.offsetParent;
  }
  subject.style.top = offsetTop + 'px';
  subject.style.left = offsetLeft + 'px';
}

function snapSize( subject, target ) {
  snap( subject, target );
  subject.style.width = target.offsetWidth + 'px';
  subject.style.height = target.offsetHeight + 'px';
}

// toggle style.display between block and none (show/hide)
function toggle( id ) {
  myobj = gebid( id );
  if ( myobj && ( myobj.style.display == 'none' || myobj.style.display == '' ) ) {
    myobj.style.display = 'block';
  }
  else if ( myobj ) {
    myobj.style.display = 'none';
  }
}

// toggle an object's src attribute between src and newsrc
function swapsrc( myobj, newsrc ) {
  if ( myobj.swapped ) {
    newsrc = myobj.swapped;
    myobj.swapped = myobj.src;
    myobj.src = newsrc;
  }
  else {
    myobj.swapped = myobj.src;
    myobj.src = newsrc;
  }
}

// label pasting
function pasteLabel( labelname, targetid ) {
  current = gebid( targetid ).value.split( ' ' );
  if ( !in_array( labelname, current ) ) {
    gebid( targetid ).value = gebid( targetid ).value + labelname + ' ';
  }
}

// declick stops the bubbling of onclick events
function declick( obj, e ) {
  if ( !e ) e = window.event;
  e.cancelBubble = true;
  //alert('click stopped at '+obj.id);
}


// toggleDisplay is an alias of toggle() above
function toggleDisplay( id ) {
  toggle( id );
}

// save a value to a cookie field (escaping it first!)
function setCookieValue( field, value, av ) {
  if ( !av ) av = null;
  document.cookie = field + '=' + escape( value ) + '; ' + av;
}

// returns the (unescaped) value of field from document.cookie
function getCookieValue( field ) {
  vals = document.cookie.split( ';' );
  for ( pair in vals ) {
    pairvals = vals[pair].split( '=' );
    thisfield = pairvals[0];
    thisval = pairvals[1];
    if ( thisfield.charAt(0) == ' ' ) {
      thisfield = thisfield.substr(1);
    }
    if ( thisfield == field ) {
      return unescape( thisval );
    }
  }
  return;
}

// exapands a textarea
function expandArea( id ) {
  tarea = gebid( id );
  // height
  if( window.innerHeight && window.innerHeight > 660 ) {
    newheight = window.innerHeight - 200;
  }
  else {
    newheight = 480;
  }
  oldheight = tarea.style.height;
  tarea.style.height = newheight + 'px';
  // width
  if( window.innerWidth && window.innerWidth > 800 ) {
    newwidth = window.innerWidth - 300;
  }
  else {
    newwidth = 640;
  }
  oldwidth = tarea.style.width;
  tarea.style.width = newwidth + 'px';
  exer = gebid( id + '_expander' );
  if ( exer ) {
    exer.innerHTML = 'contract';
    exer.href = 'javascript: contractArea(\'' + id + '\');';
  }
}

function contractArea( id ) {
  tarea = gebid( id );
  tarea.style.height = oldheight;
  tarea.style.width = oldwidth;
  exer = gebid( id + '_expander' );
  if ( exer ) {
    exer.innerHTML = 'expand editor';
    exer.href = 'javascript: expandArea(\'' + id + '\');';
  }
}

// restyle allows you to add rules to the document stylesheet
// returns -1 if there was an error
function restyle( selector, rule ) {
  if( !document.styleSheets ) return -1;
  if( document.styleSheets.length <= 0 ) return -1;
  css = document.styleSheets[document.styleSheets.length-1];
  index = css.cssRules.length;
  css.insertRule( selector+' {'+rule+'}', index );
  return index;
}

// will make element ele use newClass class, and stop using oldClass class
// @param Element ele HTML object
function swapClass( ele, oldClass, newClass ) {
  var items = ele.className.split(' ');
  var oldPos = array_search(oldClass, items);

  // delete all oldClass
  while( oldPos > -1 ) {
    delete items[oldPos];
    oldPos = array_search(oldClass, items, oldPos+1);
  }

  var newPos = array_search(newClass, items);
  // new class not there, add it
  if( newPos < 0 ) {
    items.push(newClass);
  }

  var old = ele.className;
  ele.className = items.join(' ');
}

// Drag and Drop code
//   use onmouseover="useDrag(this);" to make stuff mooooove
// 
var dragOffsetX, dragOffsetY, dragDragging, indrag, metadiv;
var inmeta = true;

function px(p) {
  return p.split("px", 1)[0];
}

function giveDrag(id) {
  gonnadrag = gebid(id);
  if ( indrag ) {
    dropme(1);
    indrag.onmousedown = giveDrag( indrag.id );
    indrag = gonnadrag;
  }
  else {
    indrag = gonnadrag;
  }
  indrag.onmousedown = dragme;
  indrag.onmouseup = dropme;

  metaid = id+'Meta';
  metadiv = gebid( metaid );
  if ( metadiv ) {
    metadiv.style.visibility = 'visible';
    metatop = id+'Top';
    metaleft = id+'Left';
    gebid( metatop ).value = indrag.style.top;
    gebid( metaleft ).value = indrag.style.left;
  }

  window.status = 'top: '+ indrag.style.top +'; left: '+indrag.style.left+';';
}

function useDrag(obj) {
  indrag = obj;
  
  if ( !indrag.style.top ) {
    indrag.style.top = indrag.offsetTop + 'px';
  }
  
  if ( !indrag.style.left ) {
    indrag.style.left = indrag.offsetLeft + 'px';
  }
  
  indrag.onmousedown = dragme;
  indrag.onmouseup = dropme;

  id = indrag.id;

  metaid = id+'Meta';
  metadiv = gebid( metaid );
  if ( metadiv ) {
    metadiv.style.visibility = 'visible';
    metatop = id+'Top';
    metaleft = id+'Left';
    gebid( metatop ).value = indrag.style.top;
    gebid( metaleft ).value = indrag.style.left;
  }

  window.status = 'top: '+ indrag.style.top +'; left: '+indrag.style.left+';';
}

function takeDrag() {
  if ( indrag ){
    dropme(1);
    indrag.onmousedown = giveDrag( indrag.id );
    indrag.onmouseup = '';
    indrag = '';
  }
  if ( metadiv ) {
    metadiv.style.visibility = 'hidden';
  }
}   

function dragme(e) {
  if ( !e ) {  // not e but IE
    e = window.event;
    e.pageX = e.clientX;
    e.pageY = e.clientY;
    e.which = 1;
  }

  if (e.which == 1) {
    indrag.onmousemove=DRAG_drag;

    curwidth = indrag.offsetWidth;
    curheight = indrag.offsetHeight;
    sizeOffsetW = e.pageX;
    sizeOffsetH = e.pageY;
    curleft = px(indrag.style.left);
    curtop = px(indrag.style.top);
    dragOffsetX = e.pageX - curleft;
    dragOffsetY = e.pageY - curtop;
    dragDragging=true;
    
    if ( metadiv ) {
      metadiv.style.visibility = 'visible';
    }

    //return false;
  }
}

function dropme(e) {
  indrag.onmousemove=null;
  dragDragging=false;

  if ( metadiv ) {
    metadiv.style.visibility = 'hidden';
  }

  //return false;
}

function DRAG_drag(e) {
  if ( !e ) {  // not e but IE
    e = window.event;
    e.pageX = e.clientX;
    e.pageY = e.clientY;
    e.which = 1;
  }

  if (dragDragging) {
    newleft = ( e.pageX - dragOffsetX );
    newtop = ( e.pageY - dragOffsetY );

    indrag.style.left = newleft + 'px';
    indrag.style.top = newtop + 'px';

    if ( metadiv ) {
      gebid( metatop ).value = indrag.style.top;
      gebid( metaleft ).value = indrag.style.left;
    }

    window.status = 'top: '+ indrag.style.top +'; left: '+indrag.style.left+';';

    return false;
  }
  else {
    return true;
  }
}

function sizeDrag(e) {
  if ( !e ) {  // not e but IE
    e = window.event;
    e.pageX = e.clientX;
    e.pageY = e.clientY;
    e.which = 1;
  }

  if (dragDragging) {
    deltaW = ( e.pageX - sizeOffsetW );
    deltaH = ( e.pageY - sizeOffsetH );
    newwidth = curwidth + deltaW;
    newheight = curheight + deltaH;
    if ( newwidth < minw ) newwidth = minwidth;
    if ( newheight < minh ) newheight = minheight;

    indrag.style.width = newwidth + 'px';
    indrag.style.height = newheight + 'px'
    return false;
  }
  else {
    return true;
  }
}



function visToggle( id, highlight ) {
  state = gebid(id).style.visibility;
  if ( state == 'visible' ){
    gebid(id).style.visibility = 'hidden';
    gebid(highlight).style.backgroundColor = 'transparent';
  }
  else {
    gebid(id).style.visibility = 'visible';
    gebid(highlight).style.backgroundColor = '#WIFED';
  }
}


/**
 * @param Element ele The element
 * @param int op Opacity from 0 to 100. 0 being completely transparent.
 *  set to null if want to retrieve the opacity only
 *
 * @return int The current opacity
 */
function opacity( ele, op ) {
  var ie = (document.all) ? 1 : 0;
  var p = (ie) ? "filter" : "MozOpacity";
  if( op != null ) {
    op = (ie? "alpha(opacity="+op+")" : op/100.0);
    ele.style[p] = op;
  }
  alert(ele);
  return parseFloat(ele.style[p])*100;
}

//
// object timers
//
var __TIMERS = new Array();
var __TIMERS_COUNTER = 0;


/**
 * @param Element ele The element
 * @param int op Opacity from 0 to 100. 0 being completely transparent.
 *  set to null if want to retrieve the opacity only
 *
 * @return int The current opacity
 */
function opacity( ele, op ) {
  var ie = (document.all) ? 1 : 0;
  var p = (ie) ? 'filter' : 'MozOpacity';
  var propStr = "alpha(opacity=";
  if( op != null ) {
    op = (ie? propStr+op+")" : op/100.0);
    ele.style[p] = op;
  }
  var val = ele.style[p];
  if( !ie ) {
    val = parseFloat(val)*100;
  }
  else if( val.length > 0 ) {
    val = parseFloat(val.substring(propStr.length));
  }
  return val;
}



// Fades object to alphaEnd
//
// @param string id The id of the element to fade
// @param int alphaRate Changing alpha. Set to positive to fade-in, set to negative to fade-out
// @param int alphaEnd The amount of alpha to get to
// @param int timeRate The time rate. The smaller, the smoother.
//  Note: It is recomened not to set too low (~<80), as some browsers may be slow.
// @param string callBack Will eval callBack when alpha reaches alphaEnd
// @param int timerId Unique id for this fade. Use when calling endFade.
//  Note: Leave blank, or set to null if desired
function fade( id, alphaRate, alphaEnd, timeRate, callBack, timerId ) {
  // unique counter for each individual call to this function
  var obj = gebid( id );
  if( obj == null || alphaRate == 0 || timeRate <= 0 ) return;

  if( __TIMERS[timerId] == null ) {
    if( timerId == null || timerId == '' ) {
      timerId = __TIMERS_COUNTER;
    __TIMERS_COUNTER++;
    }

    __TIMERS[timerId] = new Object();
    __TIMERS[timerId].alphaRate == alphaRate
  }
  clearTimeout(__TIMERS[timerId].timer);

  var myfunc = "fade('"+id+"',"+alphaRate+","+alphaEnd+","+timeRate+",'"+callBack+"', "+timerId+")";

  if( __TIMERS[timerId].alphaRate == alphaRate ) {
    current = opacity(obj);
    current += alphaRate;

    // run timer
    if( ( alphaRate > 0.0 && current < alphaEnd ) || ( alphaRate < 0.0 && current > alphaEnd ) ) {
      opacity(obj, current);
      __TIMERS[timerId].timer = setTimeout(myfunc, timeRate);
    }
    // reached end
    else {
      delete __TIMERS[timerId];
      opacity(obj, alphaEnd);

      if( callBack != null && callBack != '' ) {
	eval(callBack);
      }
    }
  }
  // reset, and fade other way
  else {
    __TIMERS[timerId].alphaRate = alphaRate;
    
    eval(myfunc);
  }
}

/*
function fade( id, alphaRate, alphaEnd, timeRate, callBack, timerId ) {
  // unique counter for each individual call to this function
  var obj = document.getElementById( id );
  if( obj == null || alphaRate == 0 || timeRate <= 0 ) return;

  if( __TIMERS[timerId] == null ) {
    timerId = __TIMERS_COUNTER;
    __TIMERS_COUNTER++;
    __TIMERS[timerId] = new Object();

    if( isNaN(parseInt(obj.style.opacity)) ) {
      if( alphaRate < 0.0 ) obj.style.opacity = 1.0;
      else obj.style.opacity = 0.0;
    }
    
    __TIMERS[timerId].opacity = parseInt(obj.style.opacity);
    __TIMERS[timerId].alphaRate = alphaRate;
  }
  clearTimeout(__TIMERS[timerId].timer);

  var myfunc = "fade('"+id+"',"+alphaRate+","+alphaEnd+","+timeRate+",'"+callBack+"', "+timerId+")";

  if( __TIMERS[timerId].alphaRate == alphaRate ) {
    current = __TIMERS[timerId].opacity;
    current += alphaRate;

    // run timer
    if( ( alphaRate > 0.0 && current < alphaEnd ) || ( alphaRate < 0.0 && current > alphaEnd ) ) {
      __TIMERS[timerId].opacity = current;
      obj.style.opacity = current;
      __TIMERS[timerId].timer = setTimeout(myfunc, timeRate);
    }
    // reached end
    else {
      delete __TIMERS[timerId];

      obj.style.opacity = alphaEnd;
      if( callBack != null && callBack != '' ) {
	eval(callBack);
      }
    }
  }
  // reset, and fade other way
  else {
    if( isNaN(parseInt(obj.style.opacity)) ) {
      if( alphaRate < 0.0 ) obj.style.opacity = 1.0;
      else obj.style.opacity = 0.0;
    }
    __TIMERS[timerId].opacity = parseInt(obj.style.opacity);
    __TIMERS[timerId].alphaRate = alphaRate;

    __TIMERS[timerId].timer = setTimeout(myfunc, timeRate);
  }
}
*/


// Scrolls object to finpos horizontally
//
// @param string id The id of the element to scroll
// @param int amount Velocity of scroll (pixels per timeRate)
// @param int findPos The new horizontal position
// @param int offset The starting position of element with id "id" (before any scrolling is applied)
// @param int timeRate The time rate. The smaller, the smoother.
//  Note: It is recomened not to set too low (~<80), as some browsers may be slow.
// @param string callBack Will eval callBack when reaches destination
// @param int timerId Used by function. Leave blank, or null set to
function timedHScroll ( id, amount, finpos, offset, timeRate, callBack, timerId ){
  // unique counter for each individual call to this function
  if( __TIMERS[timerId] == null ) {
    if( timerId == null || timerId == '' ) {
      timerId = __TIMERS_COUNTER;
      __TIMERS_COUNTER++;
    }
    __TIMERS[timerId] = new Object();
  }
  clearTimeout(__TIMERS[timerId].timer);

  var obj = document.getElementById( id );
  
  var sl = parseInt(obj.style.left);
  if( isNaN(sl) ) sl = 0;

  var curpos = parseInt(obj.offsetLeft);
  finpos += offset;

  var inspos, nextpos;

  // which direction?
  if ( finpos < curpos ) {
    inspos = sl - amount;
    nextpos = inspos + offset;
  }
  else {
    inspos = sl + amount;
    nextpos = inspos + offset;
  }

  // debug
  //  debug("f: "+finpos+", c: "+curpos+", n: "+nextpos+", l: "+sl+", a: "+amount+", o: "+offset+", nl: "+inspos);

  // loop?
  if ( ( finpos < curpos && finpos < nextpos && nextpos < curpos  ) || ( finpos > curpos && finpos > nextpos && nextpos > curpos ) ){
    finpos -= offset;
    var myfunc = "timedHScroll('"+id+"',"+amount+","+finpos+","+offset+","+timeRate+",\""+callBack+"\", "+timerId+")";
    obj.style.left = inspos + 'px';
    __TIMERS[timerId].timer = setTimeout(myfunc, timeRate);
  }
  else {
    finpos -= offset;
    obj.style.left = finpos + 'px';
    if( callBack != null && callBack != '' ) {
      eval(callBack);
    }
  }
}
 
// needs fix
//
function timedVScroll ( id, amount, finpos ){
  obj = document.getElementById( id );
  curpos = parseInt(obj.offsetTop);

  // which direction?
  if ( finpos < curpos ) {
    nextpos = curpos - amount;
  }
  else {
    nextpos = curpos + amount;
  }

  // loop?
  if ( ( finpos < curpos && finpos < nextpos && nextpos < curpos  ) || ( finpos > curpos && finpos > nextpos && nextpos > curpos ) ){
    myfunc = "timedVScroll('"+id+"',"+amount+","+finpos+")";
    scroller = setTimeout(myfunc, 50);
    obj.style.top = nextpos + 'px';
  }
  else {
    obj.style.top = finpos + 'px';
  }
}

function httpClientFactory() {
  var xmlhttp=false;
  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
  // JScript gives us Conditional compilation, we can cope with old IE versions.
  // and security blocked creation of the objects.
   try {
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
   } catch (e) {
    try {
     xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (E) {
     xmlhttp = false;
    }
   }
  @end @*/
  if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
     xmlhttp = new XMLHttpRequest();
  }
  return xmlhttp;
}

function httpFill( url, id ) {
  xmlhttp1 = httpClientFactory();
  xmlhttp1.open("GET", url, true);
  xmlhttp1.onreadystatechange=function() {
    if (xmlhttp1.readyState==4) {
      gebid(id).innerHTML = xmlhttp1.responseText;
      xmlhttp1 = false;
    }
  }
  xmlhttp1.send(null);
}

//
// http://www.phpied.com/javascript-include/     (include js and css)
//

var included_files = new Array();

function include_once(script_filename) {
  if (!in_array(script_filename, included_files)) {
    included_files[included_files.length] = script_filename;
    include_dom(script_filename);
  }
}

function include_dom(script_filename) {
  var html_doc = document.getElementsByTagName('head').item(0);
  t=script_filename.substring(script_filename.lastIndexOf('.')+1);
  if (t=='js'){
    var file = document.createElement('script');
    file.setAttribute('language','javascript');
    file.setAttribute('type','text/javascript');
    file.setAttribute('src',script_filename);
  }else if (t=='css'){
    var file = document.createElement('link');
    file.setAttribute('rel','stylesheet');
    file.setAttribute('type','text/css');
    file.setAttribute('href',script_filename);
  }
  html_doc.appendChild(file);
  return false;
}

/**
 * Searches in the array haysack for needle starting at offset.
 * Will use the function compFunc to compare if provided, otherwise will use equality "=="
 *
 * @param mixed needle
 * @param array haysack
 * @param int offset
 * @param function compFunc Function that takes two arguments (A, B) (items from the array) and compares them.
 *  Returns < 0 if A is less than B, and > 0 if A is greater than B, zero if they are equal.
 * @return int Offset of needle in haysack. Otherwise -1 if not found.
 */
function array_search(needle, haysack, offset, compFunc ) {
  if( offset == null ) offset = 0;
  if( offset < 0 || offset >= haysack.length ) return -1;
  for (var i = offset; i < haysack.length; i++) {
    if( compFunc && compFunc(haysack[i], needle) == 0 || haysack[i] == needle) {
      return i;
    }
  }
  return -1;
}

/**
 * Searches in the array haysack for needle.
 * Will use the function compFunc to compare if provided, otherwise will use equality "=="
 *
 * @param mixed needle
 * @param array haysack
 * @param function compFunc Function that takes two arguments (A, B) (items from the array) and compares them.
 *  Returns < 0 if A is less than B, and > 0 if A is greater than B, zero if they are equal.
 * @return bool True if found, false otherwise
 */
function in_array(needle, haysack, compFunc) {
  var pos = array_search(needle, haysack, 0, compFunc);
  return (pos > -1);
}

/**
 * @return array A copy of the given array
 */
function array_copy( arr ) {
  var newArr = new Array();
  for( key in arr ) {
    newArr[key] = arr[key];
  }
  return newArr;
}

function is_array( obj ) {
	var tmp = new Object();
	return (tmp.push = obj.push && tmp.shift == obj.shift && tmp.constructor == obj.constructor);
}

/**
 * Pulls out the given needle from haysack compared by compFunc (if provided)
 * @return array The new array without the needle.
 */
function array_pull( needle, haysack, compFunc ) {
	var i = array_search(needle, haysack, 0, compFunc);
	if( i < 0 ) return haysack;
	var obj = haysack[i];

	var newArr = new Array();
	for( key in haysack ) {
		if( key != i ) newArr.push(haysack[key]);
	}
	return newArr;
}

// appends an object of type 'type' to parent with id 'parentId'
// returns actuall object created, otherwise null if it fails
function appendObject(parentId, type) {
  parent = gebid(parentId);
  if( parent == null ) return null;

  try {
    obj = document.createElement(type.toUpperCase());
    parent.appendChild(obj);
  }
  catch(x) {
    return null;
  }
  return obj;
}

// remove object from parent with id 'parentId'
// returns reference to object that was removed, otherwise returns null if child not in parent
function removeChild(parentId, obj) {
  parent = gebid(parentId);
  if( parent == null ) return null;

  try {
    result = parent.removeChild(obj);
    return result;
  }
  catch( x ) {
    return null;
  }
}



// for use with other input under select/multiselect
function otherselect(obj) {
  var otherid = obj.id+"_other";
  if (obj.value==-2) {
    gebid( otherid ).style.width = ( obj.offsetWidth - 10 ) + "px";
    gebid( otherid ).style.display = "block";
    gebid( otherid ).focus();
  }
  else {
    gebid( otherid ).style.display = "none";
  }
}


// opens up a new window in full screen.
// @param mixed url The url to open, or an object
//  The following are types of objects that can be passed
//   fcnyMedia     Will open in the current displaying image url
// @param string appendStr A string to append to the end of the url
function fullScreen( url, appendStr ) {
  if( typeof url == 'object' ) {
    switch( url.type.toLowerCase() ) {
    case 'fcnymedia':
      var obj = url.getObject(url.currentId);
      if( obj != null ) {
	url = obj.url+'/?fullscreen=1';
      }
    }
  }

  var IE = false;

  var prop = "directories=0,location=0,menubar=0,resizable=no,scrollbars=0,status=0,toolbar=0,personalbar=0,";
  if( IE ) prop += "fullscreen=1";
  //  else prop += "dependent=1";

  var page = window.open(url, 'window', prop);
 
  var h = screen.availHeight;
  var w = screen.availWidth;
  var lb = w - page.innerWidth;
  var tb = h - page.innerHeight;

  if( !IE ) {
    if( page.resizeTo ) page.resizeTo(w, h);
    if( page.moveTo ) page.moveTo(0, 0);
  }

  page.focus();
}


// browser compability fix for DOM Node object
if( !Node ) {
  var Node = new Object();
  Node.ELEMENT_NODE = 1;
}


//
// fcnylight object representations
//

//
// Representation of a fcnylight object
//
// @param mixed objId The object id
// @param Element ele The Element that is a representation to this object (i.e. image)
//
function fcnyObject( objId, ele ) {
    this.id = objId;
    this.object = ele;
    this.selected = false;
    this.dragging = false;
    this.selector = this.object;
}

//
// fcnylight core functions
//

// Return the position of the object with that id in the array arr.
// Returns -1 if not found
function getObjPos( id, arr ) {
  for( var i = 0; i < arr.length; i++ ) {
    if( arr[i] != null && id == arr[i].id ) return i;
  }
  return -1;
}

// Return the object with that id in the array arr.
// Return null if not found
function getObj( id, arr ) {
  var pos = getObjPos(id, arr);
  if( pos < 0 ) return null;
  return arr[pos];
}

// returns all elements under element ele ... recursively
function getAllItems( ele ) {
  var arr = new Array();
  if( ele.hasChildNodes() ) {
    for( var i = 0; i < ele.childNodes.length; i++ ) {
      arr.push(ele.childNodes.item(i));
      arr = arr.concat(getAllItems(ele.childNodes.item(i)));
    }
  }
  return arr;
}


// handles listeners, and events
// should really use MochiKit signal system instead...
var fcnyEvents = {
  /**
   * Registers a type of event to the object with id objId
   *
   * @param array arr An array of objects where to find the object with id objId
   * @param mixed objId The object's id that will listen for this type of event.
   *  Note: Any object can be passed in as well.
   * @param string type The type of event
   * @param Function func The function that will handle event. Takes 1 argument:
   *  1. Event (the actual event that happened)
   *     Event.source will be actual object that trigered the event (source argument on doEvent method)
   * @param object source An object that is related to the event.
   *  By setting this parametter, will try to add an event listener to the object, where it will call doEvent with this source
   */ 
  register : function( arr, objId, type, func, source ) {
    var obj;
    if( typeof objId == 'object' ) obj = objId;
    else obj = getObj(objId, arr);
    if( obj == null ) return;

    if( obj.addEventListener ) {
      var wraperFunc = function() {
	fcnyEvents.doEvent(null, null, obj, type, source);
      };
      obj.addEventListener(type, wraperFunc, false);
    }

    if( obj._listeners == null ) obj._listeners = new Object();
    if( obj._listeners[type] == null ) obj._listeners[type] = new Array();
    obj._listeners[type].push(func);
  },
  /**
   * Unregisters a type of event to the object with id objId
   *
   * @param array arr An array of objects where to find the object with id objId
   * @param mixed objId The object's id that will listen for this type of event.
   *  Note: Any object can be passed in as well.
   * @param string type The type of event
   * @param Function func The function that will handle event.
   *
   * @return Function Returns the function that was unregistered. Null if function was not registered
   */ 
  unregister : function( arr, objId, type, func ) {
    var obj;
    if( typeof objId == 'object' ) obj = objId;
    else obj = getObj(objId, arr);
    if( obj == null ) return;

    var tmp = null;
    if( obj._listeners && obj._listeners[type] && obj._listeners[type][func] ) {
      tmp = obj._listeners[type][func];
      delete obj._listeners[type][func];
    }
    return tmp;
  },
  /**
   * Checks to see if that event was registered to that particular object
   *
   * @param array arr An array of objects where to find the object with id objId
   * @param mixed objId The object's id that will listen for this type of event.
   *  Note: Any object can be passed in as well.
   * @param string type The type of event
   *
   * @return bool True if it was register, false otherwise
   */
  isRegister : function( arr, objId, type ) {
    var obj;
    if( typeof objId == 'object' ) obj = objId;
    else obj = getObj(objId, arr);

    return (obj != null && obj._listeners != null && obj._listeners[type] != null);
  },
  /**
   * Will fire up the event handlers for this 'type' of event
   *
   * @param array arr An array of objects where to find the object with id objId
   * @param object e The event that happened
   * @param mixed objId The object's id that will listen for this type of event.
   *  Note: Any object can be passed in as well.
   * @param string type The type of event
   * @param object source An object that is related to the event
   *
   * @return bool True if it was register, false otherwise
   */
  doEvent : function( arr, e, objId, type, source ) {
    var obj;
    if( typeof objId == 'object' ) obj = objId;
    else obj = getObj(objId, arr);

    if( obj && obj._listeners && obj._listeners[type] ) {
      if( !e ) {
	if( window.event ) e = window.event;
	else e = new Object();
      }
      if( source ) {
	e.source = source;
	if( source.offsetHeight != null ) e.height = e.source.offsetHeight;
	if( source.offsetWidth != null ) e.height = e.source.offsetWidth;
      }
      if( e.type == null || e.type == '' ) {
	e.type = type;
      }

      for( var i = 0; i < obj._listeners[type].length; i++ ) {
	obj._listeners[type][i].apply(e, new Array(e));
      }
    }
  }
}


/**
 * See getdef function in fcnyCore.php; This works the same way.
 * See also http://www.activsoftware.com/code_samples/code.cfm/CodeID/59/JavaScript/Get_Query_String_variables_in_JavaScript
 */
function getdef( key, defaultval ) {
  var query = window.location.search.substring(1);
  var vars = query.split("&");
  for ( var i=0; i < vars.length; i++ ) {
    var pair = vars[i].split("=");
    if ( pair[0] == key ) {
      return pair[1];
    }
  } 
  return defaultval;
}

/**
 * Following is courtesy of Walter Higgins
 * See http://sxoop.wordpress.com/2006/08/30/javascript-templating-with-sxooptemplate/ for details
 */
templater = {};
templater.map = function(array, func) {
   var result = [];
   for (var i = 0;i < array.length; i++) { 
       var mapped = func(array[i]);
       for (var j = 0; j < mapped.length; j++) {
           result.push(mapped[j]);
       }
   }
   return result;
};
templater.parseJSON = function (str) {
  try {
    if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(str)) {
      return eval('(' + str + ')');
    }
  } catch (e) {
    // intentionally blank
  }
  throw new SyntaxError("parseJSON encountered invalid object notation");
};
/**
 * Parse a templater (supplied as a string), substituting
 * the supplied object ($_) 
 * The $_ variable refers to the object which was passed into the parse function
 * Of course, all other global variables/functions are accessible too.
 */
templater.parse = function(str,$_) {
    var singleLine = str.replace(/[\n\r]/g,"");
    // innerHTML automatically converts < to &lt; and > to &gt;
    singleLine = singleLine.replace(/&lt;/g,"<");
    singleLine = singleLine.replace(/&gt;/g,">");
    /**
     * The include function facilitates inclusion of inner templaters
     */
    var include = function(elementId) {
        var included = gebid(elementId).innerHTML;
        return templater.parse(included,$_);
    };
    /**
     * Split the templater into parts
     */
    var parts = templater.map(singleLine.split("[:"),function (part) {
        var result = [];
        if (part.match(/:\]/)){
            result = part.split(/:\]/g);
            result[0] = "[:" + result[0];
        }
        else{
            result = [part];
        }
        return result;
    });
    /**
     * Process each part
     */
    var result = templater.map(parts,function (part){
        var result = "";
        if (part.match(/\[:=/)) {
            var inner = part.replace(/^\[:=\s*/,"");
            return ["theArray.push(" + inner + ");"];
        }
        if (part.match(/^\[:/)) {
            var inner = part.replace(/^\[:/,"");
            return [inner];
        }
        else {
            part = part.replace(/\"/g,"\\\"");
            return ["theArray.push(\"" + part + "\");"];
        }
    });
    var theArray = [];
    result.push("theArray.join('');");
    var javascript = result.join("\n");
    return eval(javascript);
};

var fcnyfp = false;