/**
 * Javascript module for FBML
 *
 * This will get included whenever a page is rendering any FBML
 * @author    ccheever
 */
var FBML = (function () {

  /**
   * Attaches an event listener to an object
   * @param    DOMElement    obj
   * @param    string        eventName
   * @param    function      fun
   * 
   * @author   ccheever
   */
  var _addEventListener;
  // Firefox and IE deal differently with attaching events
  // so we need some hackiness to abstract the browser away
  if (window.addEventListener) {
    // Firefox
    _addEventListener = function(obj, eventName, fun) {
      obj.addEventListener(eventName, fun, false);
    }
  } else {
    // IE
    _addEventListener = function(obj, eventName, fun) {
      obj.attachEvent("on" + eventName, fun);
    }
  }

  /**
   * A version of the friend selector that generates a hidden input
   * with the name of the value of the "idname" attribute of the 
   * friend selector text field
   */
  if (typeof typeaheadpro != 'undefined') {
    function friendSelector(obj, source, idFieldName) {

      // Create the hidden input
      var idInput = document.createElement('INPUT');
      idInput.name = obj.getAttribute('idname');
      idInput.type = 'hidden';

      // and add it to the form that this friend selector is in
      // if one exists
      if (obj.form) {
        obj.form.appendChild(idInput);
      }

      // and also attach it to this
      this._idInput = idInput;

      // Set the onselect event handler
      this._onselect = function(e) {
        this.parent._onselect(e);
        idInput.value = e.i;
      }

      // Call the parent constructor
      return this.parent.construct(this, obj, source);

    }
    friendSelector.extend(typeaheadpro);
  } else {
    friendSelector = null;
  }

  //
  // Mock AJAX stuff
  //

  // mapping of hashed context sigs -> serialized contexts
  var Contexts = new Object();

  /**
   * Converts all the values of a form into an object
   * @param    FORM DOM element     form
   * @return   Object
   */
  function formValues(form) {
    var vals = new Object();
    var formElements = form.elements;
    for (var i = 0; i < formElements.length; i++) {
      var e = formElements.item(i);
      if (!(e.type == "radio" || e.type == "checkbox") || e.checked)
        vals[e.name] = e.value;
    }
    return vals;
  }

  /**
   * Returns false and logs an error message to Firebug if it can
   * @param    string    msg   The error message
   * @return   bool      false
   */
  function err(msg) {
    if (window.console) {
      // This will work in Safari and Firefox
      window.console.log('Facebook FBML Mock AJAX ERROR: ' + msg);
    }
    return false;
  }

  function attachCurlFromObject(params) {

    if (!params['url']) {
      return err("no input with id url in form");
    }
    if (!params['fb_sig_api_key']) {
      return err("no input with id fb_api_key in form");
    }
    
    var attachment_container = megaboxx.add_attachment_element(true);
    attachCurlFromFormValues(params, attachment_container);
    return;
  }

  function attachCurlFromFormValues(formValues, element_to_fill) {
    
    var curl_params = formValues;    
    var ajax = new Ajax;
    ajax.onDone = function(junk, responseText) {
      element_to_fill.innerHTML = responseText; // HTML returned, not FBML
    }

    curl_params['action'] = 'edit';
    
    ajax.post('/fbml/ajax/message_attach.php', curl_params);
  }

  function attachFromPreview() {
  
    // extend this if we ever have multiple attachments
    // will have to change fb:message_preview tag as well to pass context
    var parent = ge('attachment');
    var inputs = parent.getElementsByTagName('input');
    
    var params = Object();
    for (var i = 0; i < inputs.length; i++) {
      params[inputs[i].name] = inputs[i].value;
    }
    
    attachCurlFromFormValues(params, parent);
  }

  /**
   * Rewrites a DOM element based on FBML rendered from a remote URL proxied through a Facebook FBML renderer
   * @param   bool     loggedIn     Whether the person is logged in already
   * @param   string   targetId     ID of the DOM element to rewrite the innerHTML of
   * @param   string   url          The remote URL to fetch the FBML from
   * @param   string   formId       The ID of the FORM element to POST to the remote URL
   */
  function clickRewriteAjax(loggedIn, targetId, url, formId, loadingHTML) {
    if (loggedIn || (!attrBool(formId, "requirelogin", true))) {
      return _clickRewriteAjax(targetId, url, formId, loadingHTML);
    } else {
      confirmTos(function () {
          return _clickRewriteAjax(targetId, url, formId, loadingHTML);
          });
      return false;
    }
  }

  function _clickRewriteAjax(targetId, url, formId, loadingHTML) {
    var target = ge(targetId);
    
    if (!target) {
      return err("target " + targetId + " not found");
    }

    var hContext = target.getAttribute("fbcontext");
    var sContext = FBML.Contexts[hContext];

    // The user can either specify the form explicitly by id
    // or else we try to fall back on the current enclosing form
    var form = null;
    if (typeof formId == "string") {
      form = ge(formId);
    } else {
      form = formId;
    }

    if (!form) {
      return err("You must either specify a clickrewriteform (an id) or use the clickrewrite attribute inside a form");
    }

    var post = formValues(form);
    post["fb_mockajax_context"] = sContext;
    post["fb_mockajax_context_hash"] = hContext;
    post["fb_mockajax_url"] = url;

    var a = new Ajax(
      // onDone
      function(_e, responseText) {

        // the mock_ajax_proxy.php script returns a JSON description
        // of an object
        var response = eval("(" + responseText + ")");

        if (response.ok) {

          // replace the innerHTML of the target with our new content!!
          target.innerHTML = response.html;

          // now we have to manually run all the scripts that are in the 
          // response, since they won't be evaluated the way they would
          // at the time of the initial document.write
          var scripts = target.getElementsByTagName("SCRIPT");
          for (var i = 0; i < scripts.length; i++) {
            try {
              eval(scripts.item(i).innerHTML);
            } catch (e) {
              err(e);
            }
          }

        } else {
          return err(response["error_message"]);
        }

        // for debugging
        FBML.mockAjaxResponse = response;

        return response.ok;
      }, 

      // onFail
      function(_e, _responseText) {
        // This error likely indicates a problem with the connection to 
        // Facebook, not a problem with the remote site
        return err("failed to successfully retrieve data from Facebook when making mock AJAX call to rewrite id " + targetId);
      });

    // Make the actual AJAX request to the Facebook FBML proxy
    a.post(FBML._mockAjaxProxyUrl, post);

    if (loadingHTML) {
      target.innerHTML = loadingHTML;
    }

    // We've handled the click event on this element
    return false;

  }

  //
  // Simple show and hide stuff
  //

  function clickToShow(targetId) {  
    return clickToSetDisplay(targetId, "");
  }

  function clickToHide(targetId) {
    return clickToSetDisplay(targetId, "none");
  }

  function clickToToggle(targetId) {
    var target = ge(targetId);
    if (!target) {
      return err("Could not find target " + targetId);
    } else {
      target.style.display = ( target.style.display == "none" ) ? '' : 'none';
      return false;
    }
  }

  function clickToSetDisplay(targetId, disp) {
    var target = ge(targetId);
    if (!target) {
      return err("Could not find target " + targetId);
    } else {
      target.style.display = disp;
      return false;
    }
  }

  function confirmTos(continuation) {
    var dialog = new pop_dialog();
    //var contents = '<p>The information you have entered, along with some of your profile information, is to be sent to a third party.  Are you sure you want to continue sending this information?</p><p>By clicking \'Sure\', you agree to the <a href="/terms.php">terms of service</a>.</p>';
    var contents = '<p>The information you have entered and your Facebook id will be submitted to a third party.  The application you are submiting this form to will know who you are on Facebook.  Are you sure want to continue?</p>';
    var fade = function(dialog) {
      generic_dialog.get_dialog(dialog).fade_out(100);
    };
    dialog.show_choice('Sending data outside Facebook', contents,
      'Sure, that\'s fine', function() { continuation(); fade(this); },
      'Cancel', function() { fade(this); });
  }

  /**
   * Gets the boolean attribute of a tag using FBML semantics
   * @param    DOMElement|string    element
   * @param    string               attr
   * @param    bool                 defaultValue
   * @return   bool                 true|false|defaultValue
   *
   * @author  ccheever
   *
   * false|no|0                  -->  false
   * true|yes|<any # not zero>   -->  true
   * 
   * Comparisons are case insensitive>
   */
  function attrBool(element, attr, defaultValue) {
    // The meta-default is false
    if (!defaultValue) {
      defaultValue = false;
    }

    var el = ge(element);
    if (el.hasAttribute(attr)) {
      var val = el.getAttribute(attr).toLowerCase();
      switch (val) {

        case "false":
          case "no":
          case "0":
          return false;

        case "true":
          case "yes":
          return true;

        default:
        var intval = parseInt(val);
        if ((intval < 0) || (intval > 0)) {
          return true;
        }
        return defaultValue;
      }

    }

  }


  //
  // Export the public interface of this module
  //
  return {
      friendSelector: friendSelector,
      Contexts: Contexts,
      formValues: formValues,
      attachCurlFromObject: attachCurlFromObject,
      attachFromPreview: attachFromPreview,
      clickRewriteAjax: clickRewriteAjax,
      clickToShow: clickToShow,
      clickToHide: clickToHide,
      clickToToggle: clickToToggle,
      confirmTos: confirmTos
  };

})();

