 /*
  AGORA_RS.JS - Javascript for remote scripting support and visual enhancement in T.A.O. Agora.

  Dependencies: jQuery

  Implements:
   $().sIFR(), $().initCollapsibles (note: any sIFR should be called before initializing collapsibles, to avoid elements made invisible)


  Copyright©2007,2008 Eugene Arenhaus (http://chiseledrocks.com) for T.A.O. LLC
 */

//loadFirebugConsole();

var togglePrefixClosed = "[+]";
var togglePrefixOpen = "[-]";

function flashPresent() {
 var result = false;
 if (navigator.plugins && navigator.plugins.length > 0) // try the standard-compliant way first
  result = !! navigator.plugins['Shockwave Flash'];
 if (!result && navigator.mimeTypes) // slightly less standard
  result = navigator.mimeTypes["application/x-shockwave-flash"] && navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;
 if (!result && $.browser.msie) // still not detected, heaven forbid we are in the Exploder (shoo, shoo)
  try { result = window.ActiveXObject && !! new ActiveXObject('ShockwaveFlash.ShockwaveFlash.1'); } catch(x) { };
 // not present still? must be a limited browser, nothing we can do.
 return result;
}

function rgbToHex(rgb) {
  var color = rgb.substr(4, rgb.length-5).split(',');
  var hex = '';
  for (i=0; i <= 2; i++) {
    var hd = parseInt(color[i]).toString(16);
    if (hd.length < 2) hd = '0'+hd;
    var hex = hex + hd;
  }
  return '#' + hex;
}

function toggleCollapsed() { // toggles collapsible elements when the controlling link is pressed.
 var link = $(this);
 link.toggleClass('open');
 link.next().toggleClass('closed');
 link.html( '<span class="CollapsibleToggleIcon">' + (link.is('.open') ? togglePrefixOpen : togglePrefixClosed) + '</span>&nbsp;' + link.attr('original_text') );
}

function openStickyCollapsed() { // shows sticky-collapsible elements and hides the controlling link when it is pressed.
 var link = $(this);
 link.next().removeClass('closed');
 link.remove();
}

function tickSubmitDelay() {
//! var delayed = $('input:submit[@delay]');
 var delayed = $(':submit.delay');
 if (delayed.length) {
  delayed.each( function(i) {
   var isubmit = $(this);
   delay = isubmit.data('_delay')-1;
   if (delay > 0) {
    isubmit.data('_delay', delay);
    isubmit.val( isubmit.data('_value') + ' (' + delay + ')'  );
    setTimeout("tickSubmitDelay();", 1000);
   }
   else {  // when delay runs out, restore the text, enable the button, and remove the delay rig.
    isubmit.val(isubmit.data('_value'));
    isubmit.removeData('_value');
//!    isubmit.removeData('delay');
    isubmit.removeAttr('disabled');
    $('.delaynotice').hide();
  }
  });
 }
}

function toggleFormoptionals() {
 var controller = $(this);
 var t = controller.data('valtargets');
 if (t) {
  v = controller.val();
  for (var i in t) {
   for (var j in t[i]) {
    if (i == v) t[i][j].show(); else t[i][j].hide();
   }
  }
 }
 t = controller.data('classtargets');
 if (t) {
  var c = $(this.options[this.selectedIndex]);
  for (var i in t) {
   for (var j in t[i]) {
    if ( c.hasClass(i) ) t[i][j].show(); else t[i][j].hide();
   }
  }
 }
}

function rigFormoptionalController(controller, target, tvalue, tclass) {
 var valtargets = controller.data('valtargets'); // value-linked optionals
 var classtargets = controller.data('classtargets'); // class-linked optionals
 if ( valtargets == null && classtargets == null)  controller.bind('change', toggleFormoptionals); // the event
 if ( valtargets == null ) {   valtargets = new Object();   controller.data('valtargets', valtargets); }
 if ( classtargets == null ) { classtargets = new Object(); controller.data('classtargets', classtargets); }
 if (tvalue) {
  if (valtargets[tvalue] == null)  valtargets[tvalue] = new Array;
  valtargets[tvalue].push(target); // add the target to the list of controller's targets
  if (tvalue !== controller.val())  target.hide(); // hide the controlled elements that do not match the initial value
 }
 if (tclass) {
  if (classtargets[tclass] == null)  classtargets[tclass] = new Array;
  classtargets[tclass].push(target); // add the target to the list of controller's targets
  if (! $(controller.get(0).options[controller.get(0).selectedIndex]).hasClass(tclass) ) target.hide(); // hide the controlled elements that do not match the initial class
 }
}

/* sIFR support: $(...).sIFR() wraps the set elements in sIFR rigs and creates replacement Flash text for them. */
/*
 Note: sIFR uses 'flashvars' attribute to pass parameters to the replacement SWF. These vars are poorly documented, so here is a reference reverse-engineered from Davidson's code:
   w, h - width and height of the block
   txt - text content
   textcolor - color of text
   linkcolor - color of links
   hovercolor - color of mouse-over for links
   underline - underline drawing (for links only?) (boolean flag)
   textalign - seems to match the CSS "text-align" attribute
   offsetLeft, offsetTop - text offset. I have no idea why Davidson isn't content to use CSS padding.
 Also, 'wmode' attribute (set to "opaque" or "transparent" ) controls transparency of the background. Styles do not affect this. 'bgcolor' attribute sets background color.
*/

$.fn.extend({
 sIFR: function (src) {
  if (!flashPresent()) return this;
  return this.each( function() {
    var r = $(this);
    var text = r.html();
    var w = r.width(); var h = r.height();
    var style = 'width: ' + w + 'px; height: ' + h + 'px;'; //...
    var style = ''; //...
    var color = r.css("color");
    if (color.indexOf('rgb') != -1) color = rgbToHex(color); // Mozilla returns an RGB triad not supported by Flash
    var textalign = r.css('textAlign');
    var vars = 'txt=' + text + '&amp;textalign=' + textalign + '&amp;textcolor=' + color + '&amp;w=' + w + '&amp;h=' + h;
    var tag = '<embed class="sIFR-flash" type="application/x-shockwave-flash" style="' + style + '" quality="best" sifr="true" wmode="transparent" flashvars="' + vars + '" src="' + src + '" width="' + w + '" height="' + h + '" />';
    r.addClass('sIFR-replaced').html( '<span class="sIFR-alternate">' + text + '</span>' ).prepend( tag );
   } );
 },

 initCollapsibles: function() {
   // Collapsible elements

   var collapsibles = $($('.collapsible').not('.inputerror').get().reverse()); // reverse, because jQuery plays with object copies. This ensures that collapsibles within collapsibles are processed first.
   collapsibles.each( function(i) {
    var e = $(this);
    var sticky = e.hasClass('sticky');
    var tclass = sticky? 'CollapsibleSwitch' : 'CollapsibleToggle';
    // Variant 1: the collapsible element already has a controller link before it.
    var toggle = e.prev('.'+tclass); // the controller must be preceding and marked with "CollapsibleSwitch"  (for sticky elements) or "CollapsibleToggle" class
    // - no controller link, have to create one.
    if (!toggle.size()) {
     var ttext = '';
     // Variant 2: the collapsible element is a fieldset - use legend as controller link.
     if (e.is('fieldset')) {
      toggle = sticky? e.find('legend') : e.find('legend').remove();
     }
     else { // Variant 3: the collapsible element is freestanding, create a toggle using "title" attribute or submit button when present, or default text when not.
      ttext = e.attr('title'); // Use title attribute on the element for the toggle text.
      if (!ttext)  ttext = e.find('input[@type=submit]').attr('value'); // for forms, can reuse their submit button text;
      if (!ttext)  ttext = 'More...';
      toggle = $('<a class="'+tclass+'">'+ttext+'</a>')
      var css = e.attr('class').replace(/collapsible/, ''); // transfer all classes except "collapsible" from the target to the container
      e.attr('class', 'collapsible');
      e.wrap('<div class="'+css+' CollapsibleContainer"></div>');
      toggle.insertBefore(e);
     }
    }
    toggle.addClass(tclass).attr('original_text', toggle.html());
   });

   var toggles = $('.CollapsibleToggle');
   toggles.bind('click', toggleCollapsed); // bind the event afterwards, because jQuery in the above rigging code manipulates object copies
   toggles.next('.open').prev('.CollapsibleToggle').prepend('<span class="CollapsibleToggleIcon">'+togglePrefixOpen+'</span>&nbsp;'); // add the toggle icons and mark closed divs as closed.
   toggles.next().not('.open').addClass('closed').prev('.CollapsibleToggle').prepend('<span class="CollapsibleToggleIcon">'+togglePrefixClosed+'</span>&nbsp;');

   var switches = $('.CollapsibleSwitch');
   switches.bind('click', openStickyCollapsed);
   switches.next().addClass('closed');

   // Submission delay on form buttons

   var delayed = $(':submit.delay');
   if (delayed.length) {
    var moniker = "__tick_";
    var filter = function(x) { return x.match(moniker); };
    delayed.each( function(i) {
     var isubmit = $(this);
     // get the delay from special class name: this would work more naturally with a custom attribute, but custom attributes are disliked by W3C validators.
     delay = parseInt( jQuery.grep(isubmit.attr("class").split(" "), filter).pop().substr(moniker.length) );
     if (delay > 0) {
      isubmit.attr('disabled', true);
      isubmit.data('_delay', delay);
      isubmit.data('_value', isubmit.val());
      isubmit.val( isubmit.data('_value') + ' (' + delay + ')' );
     }
    });
    setTimeout("tickSubmitDelay();", 1000);
   }

   // Switch-dependent form components. The switch may be a selector field.

   var formoptionals = $('.dependent');
   formoptionals.each( function(i) {
    var e = $(this);
    cname = ctvalue = ctclass = null;
    // invert = false;
    ec = e.attr( 'class' ).split(' ');
    for (var n in ec) {
//     if ( !invert && (ec[i] == 'invert-dependence' ) {
//      invert = true;
//      continue;
//     }
     if ( !cname && (ec[n].indexOf('controller-') > -1) ) {
      cname = ec[n].substr(11);
      continue;
     }
     if ( !ctvalue && (ec[n].indexOf('linkedvalue-') > -1) ) {
      ctvalue = ec[n].substr(12);
     }
     if ( !ctvalue && (ec[i].indexOf('linkedclass-') > -1) ) {
      ctclass = ec[n].substr(12);
     }
    }
    if ( cname && (ctvalue || ctclass) ) {
     controller = $("[name='" + cname + "']" );
     if (controller.length)
       rigFormoptionalController(controller, e, ctvalue, ctclass/*, invert*/);
    }
   });

  }

});

