/**
 * @fileOverview Script for EduDirect search widget
 * @date April 21, 2009
 * @author Nathan L Smith
 */

/*global window, document, edudirect_config, edudirect_html, edudirect_search_options */

CollegeDegreesWidget = (function () {

var https = (document.location.protocol == 'https:');
var baseURL = "http" + ((https)?'s':'') + "://search.collegedegrees.com",
	postURL = "https://search.collegedegrees.com",
    urls = {
        json : "/feeds/searchwidget/edudirect_search_options",
        css : "/css/widget/widget.css",
        html : "/search/widget_html/edudirect_html",
        submit : "/search"
    },
    jQueryVersion = "1.3.2",
    LazyLoad, jq, getScript;

/**
 * This will be executed when document is ready
 */
function onReady() {
    var config = {}, container, selects, button, html, searchOptions,
        options = {
            degree_level : {},
            category : {},
            subject : {}
        };

    /** Global scripts to get remote data */
    edudirect_html = function(h) { html = h || ""; };
    edudirect_search_options = function(o) { searchOptions = o || {}; };

    /**
     * Set up the container in the supplied element, or appended directly to
     * body
     */
    function getContainers() {
      var hasContainer = false, elements;

      // handle multiple containers
      if(!jq.isArray(config.element)) {
        elements = config.element.toString().split(",");
      } else {
        elements = config.element;
      }

      elements = jq.map(elements, function(el) { return '#' + el; });

      elements = jq(elements.join(","));

      if(elements.length > 0) {
        return elements;
      }
      return jq(document.body);
    }

    /**
     * Populate a single select element
     */
    function populateSelect(element, data, preselectVal) {
        element = jq(element);
        data = data || {};
        var options = element.attr("options"),
            first = options[0],
            index = 1;

        // Replace contents with first element only
        element.empty().append(first);
        
        options = element.attr("options");

        // This is not the fastest way to populate these, but it works in IE6.
        for (var i in data) { if (data.hasOwnProperty(i)) {
            options[index] = new Option(data[i].name, i,
                i === preselectVal);
            index += 1;
        }}
    }

    /**
     * Handle select events
     */
    function handleChange(e) {
      validate(jq(e.target).closest('form'));
    }

    function handleDegreeLevelChanged(e) {
      var target = jq(e.target);
      populateCategory(target.closest('form'), target[0]);
    }

    function handleCategoryChanged(e) {
      var target = jq(e.target);
      populateSubject(target.closest('form'), target[0]);
    }

    /**
     * Enable the search button when all fields are selected
     */
    function validate(container) {
        // var isValid = true;
        // jq.each(selects, function (index, s) { 
        //    if (jq(s).attr("selectedIndex") <= 0) { isValid = false; }
        // });

        var numSelected = 0, thisSelect, lastSelect;

        var selects = jq('select', container);

        jq.each(selects, function(index, s) {
            // if the previous select doesn't have a value, disable this one
            thisSelect = jq(s);
            lastSelect = selects[index - 1];

            if(!lastSelect || jq(lastSelect).attr("selectedIndex") > 0) {
                thisSelect.removeAttr("disabled");
            } else {
                thisSelect.attr("disabled", "disabled");
            }

            numSelected += thisSelect.attr("selectedIndex") > 0;
        });

        // if all the selects have values, enable the submit button
        var btn = jq('[type="sumbit"]', container);
        if(numSelected == selects.length) {
            btn.removeAttr("disabled");
        } else {
            btn.attr("disabled", "disabled");
        }
    }

    /**
     * Populate the subject select
     */
    function populateSubject(container, el) {
        var source = el.value,
            subjects = (options.category[source] || {}).subjects || [],
            subjectsForSelect = {},
            select = jq("select", container)[2]; // The 3rd select

        // Sort subjects
        subjects = subjects.sort(function (a, b) {
            return a.name >= b.name?1:-1;
        });

        var degreeLevel = jq("select", container)[0].value;
        
        jq.each(subjects, function(index, s) {
            var r = false;
            if(s.degree_levels){
            	jq.each(s.degree_levels, function(index, a){ if(a == degreeLevel) r = true;});
            } else {
				r = true;
            }
            if(r)
                subjectsForSelect[s.id] = { name : s.name };
        });
        populateSelect(select, subjectsForSelect);
    }

    /**
     * Populate the category select
     */
    function populateCategory(container, el) {
        var source = el.value,
            categories = (options.degree_level[source] || {}).categories || [],
            categoriesForSelect = {},
            select = jq("select", container)[1]; // The 2nd select

        // limit categories
        if(config.categories) {
          categories = jq.isArray(config.categories) ? config.categories : [ config.categories ];
        }

        // Sort categories
        categories.sort(function(a, b) {
            return (options.category[a].name >= options.category[b].name)?1:-1;
        });

        jq.each(categories, function(index, c) {
            categoriesForSelect[c] = options.category[c];
        });

        // if only one category, preselect it
        if(categories.length == 1) {
          populateSelect(select, categoriesForSelect, categories[0]);
          $(select).trigger('change');
        } else {
          populateSelect(select, categoriesForSelect);
        }

        populateSubject(container, select);
        jq(select).bind("change", handleCategoryChanged);
    }

    /**
     * Populate the degree level select
     */
    function populateDegreeLevel(container) {
        var select = jq("select", container)[0]; // This is the 1st select
        populateSelect(select, options.degree_level);
        jq(select).bind("change", handleDegreeLevelChanged);
    }

    /**
     * Load the data from the JSON feed into the options variable
     */
    function getData(container) {
        // This will populate the searchOptions variable
        getScript(urls.json, function(data) {
            data = searchOptions || {};
            // Reformat objects
            jq.each(data.degree_levels, function(index, i) {
                if(config.show_degree_levels.length == 0 || jq.inArray(i.id, config.show_degree_levels) > -1)
                    options.degree_level[i.id] = i;
            });
            jq.each(data.categories, function(index, i) {
                options.category[i.id] = i;
            });

            populateDegreeLevel(container);
        });
    }

    /**
     * Download the HTML template from the server and insert it into the
     * container
     */
    function getHTML() {
      // The JSONP call will assign the html to the html variable
      getScript(urls.html, function(data) {
        containers.append(html);

        jq.each(containers, function() {
          var container = this;

          // fire the afterRender callback if it's defined in config
          if(jq.isFunction(config.afterRender)) {
            config.afterRender(container);
          }

          var selects = jq("select", container);
          // Validate when selects change
          selects.bind("change", handleChange);

          // Set form action
          jq("form", container).attr("action", urls.submit);

          // Disable search button
          button = jq("input[type=submit], input[type=image], button", container)[0];
          validate(container);

          // Set publisher id
          var publisherId = jq("input[name=publisher_id]", container);

          if(config.publisher_id &&
             !isNaN(Number(config.publisher_id)) &&
             Number(config.publisher_id) > 0
          ) {
              publisherId.attr("value", config.publisher_id);
          } else {
              var hp = window.location.hostname.split('.');
              publisherId.attr("value", hp.slice(hp.length-2,hp.length).join('.'));
          }
          
          showDegreeLevels = jq("input[name=show_degree_level_ids]", container);
          if(jq.isArray(config.show_degree_levels)){
	          showDegreeLevels.attr("value", config.show_degree_levels.join(','));
          }

          getData(container);
        });
      });
    }

    // Set up configuration.
    if (typeof edudirect_config === "object") { config = edudirect_config; }
	
	urls.css = config.style_url || urls.css;
	urls.html = config.template_url || urls.html;
	
    // Prepend base URL to all URLs.
    baseURL = config.base_url || baseURL;
    for (var u in urls) { if (urls.hasOwnProperty(u)) {
        if((u !== 'submit') && u.indexOf('http') != 0){
            urls[u] = baseURL + urls[u];
        }
    }}
    postURL = config.post_url || postURL;
    config.show_degree_levels = config.show_degree_levels || [];
    if(!jq.isArray(config.show_degree_levels)){
    	config.show_degree_levels = [config.show_degree_levels];
    }
    
    urls.submit = postURL + urls.submit;

    // Insert the stylesheet.
    jq('head').append('<link rel="stylesheet" type="text/css" href="' +
        urls.css + '" />');

    // Set up container
    containers = getContainers();

    // Get template and proceed
    getHTML();
}


/******************************************************************************/
/* Here we load up the LazyLoad library to remote script loading.             */

/*
Class: LazyLoad

LazyLoad makes it easy and painless to lazily load one or more JavaScript
files on demand after a web page has been rendered.

Supported browsers include Firefox 2.x, Firefox 3.x, Internet Explorer 6.x,
Internet Explorer 7.x, Safari 3.x (including iPhone), and Opera 9.x. Other
browsers may or may not work and are not officially supported.

Author:
  Ryan Grove (ryan@wonko.com)

Copyright:
  Copyright (c) 2008 Ryan Grove (ryan@wonko.com). All rights reserved.

License:
  BSD License (http://www.opensource.org/licenses/bsd-license.html)

URL:
  http://wonko.com/post/painless_javascript_lazy_loading_with_lazyload

Version:
  1.0.4 (2008-07-24)
*/
LazyLoad = function() {

  // -- Group: Private Variables -----------------------------------------------

  /*
  Object: d
  Shorthand reference to the browser's *document* object.
  */
  var d = document,

  /*
  Object: pending
  Pending request object, or null if no request is in progress.
  */
  pending = null,

  /*
  Array: queue
  Array of queued load requests.
  */
  queue = [],

  /*
  Object: ua
  User agent information.
  */
  ua;

  // -- Group: Private Methods -------------------------------------------------

  /*
  Method: getUserAgent
  Populates the *ua* variable with user agent information. Uses a paraphrased
  version of the YUI user agent detection code.
  */
  function getUserAgent() {
    // No need to run again if ua is already populated.
    if (ua) {
      return;
    }

    var nua = window.navigator.userAgent, m;

    ua = {
      gecko : 0,
      ie    : 0,
      webkit: 0
    };

    m = nua.match(/AppleWebKit\/(\S*)/);

    if (m && m[1]) {
      ua.webkit = parseFloat(m[1]);
    } else {
      m = nua.match(/MSIE\s([^;]*)/);

      if (m && m[1]) {
        ua.ie = parseFloat(m[1]);
      } else if ((/Gecko\/(\S*)/).test(nua)) {
        ua.gecko = 1;

        m = nua.match(/rv:([^\s\)]*)/);

        if (m && m[1]) {
          ua.gecko = parseFloat(m[1]);
        }
      }
    }
  }

  return {
    // -- Group: Public Methods ------------------------------------------------

    /*
    Method: load
    Loads the specified script(s) and runs the specified callback function
    when all scripts have been completely loaded.

    Parameters:
      urls     - URL or array of URLs of scripts to load
      callback - function to call when loading is complete
      obj      - (optional) object to pass to the callback function
      scope    - (optional) if true, *callback* will be executed in the scope
                 of *obj* instead of receiving *obj* as an argument.
    */
    load: function load(urls, callback, obj, scope) {
      var head = d.getElementsByTagName('head')[0],
          i, script;

      if (urls) {
        // Cast urls to an Array.
        urls = urls.constructor === Array ? urls : [urls];

        // Create a request object for each URL. If multiple URLs are specified,
        // the callback will only be executed after the last URL is loaded.
        for (i = 0; i < urls.length; ++i) {
          queue.push({
            'url'     : urls[i],
            'callback': i === urls.length - 1 ? callback : null,
            'obj'     : obj,
            'scope'   : scope
          });
        }
      }

      // If a previous load request is currently in progress, we'll wait our
      // turn. Otherwise, grab the first request object off the top of the
      // queue.
      if (pending || !(pending = queue.shift())) {
        return;
      }

      // Determine browser type and version for later use.
      getUserAgent();

      // Load the script.
      script = d.createElement('script');
      script.src = pending.url;

      if (ua.ie) {
        // If this is IE, watch the last script's ready state.
        script.onreadystatechange = function() {
          if (this.readyState === 'loaded' ||
              this.readyState === 'complete') {
            LazyLoad.requestComplete();
          }
        };
      } else if (ua.gecko || ua.webkit >= 420) {
        // Firefox and Safari 3.0+ support the load/error events on script
        // nodes.
        script.onload  = LazyLoad.requestComplete;
        script.onerror = LazyLoad.requestComplete;
      }

      head.appendChild(script);

      if (!ua.ie && !ua.gecko && !(ua.webkit >= 420)) {
        // Try to use script node blocking to figure out when things have
        // loaded. This works well in Opera, but may or may not be reliable in
        // other browsers. It definitely doesn't work in Safari 2.x.
        script = d.createElement('script');
        script.appendChild(d.createTextNode('LazyLoad.requestComplete();'));
        head.appendChild(script);
      }
    },

    /*
    Method: requestComplete
    Handles callback execution and cleanup after a request is completed. This
    method should not be called manually.
    */
    requestComplete: function requestComplete() {
      // Execute the callback.
      if (pending.callback) {
        if (pending.obj) {
          if (pending.scope) {
            pending.callback.call(pending.obj);
          } else {
            pending.callback.call(window, pending.obj);
          }
        } else {
          pending.callback.call();
        }
      }

      pending = null;

      // Execute the next load request on the queue (if any).
      if (queue.length) {
        LazyLoad.load();
      }
    }
  };
}();
/******************************************************************************/

/**
 * Insert jQuery if needed and and make it private to the jq variable
 */
(function loadJQuery() {
    var url = "http" + ((https)?'s':'') + "://ajax.googleapis.com/ajax/libs/jquery/" + jQueryVersion +
            "/jquery.min.js",
            f = onReady, // Always execute
            jQueryExists = typeof window.jQuery === "function",
            existingVersion = "0", jqTmp;
    if (jQueryExists) {
        existingVersion = window.jQuery.fn.version;
        // The correct version is already present
        if (existingVersion === jQueryVersion) {
            jq = window.jQuery;
            f();
        } else { // A different version is there, load a new one
            jqTmp = window.jQuery;
            LazyLoad.load(url, function() {
                jq = window.jQuery;
                window.jQuery = window.$ = jqTmp;
                f();
            });
        }
    } else { // Load our jquery and leave it
        LazyLoad.load(url, function() {
            jq = window.jQuery;
            f();
        });
    }

    // Create a modified getScript method to ignore the cache buster
    getScript = function(url, callback) {
		return jq.ajax({
			type: "GET",
			url: url,
			data: null,
			success: callback,
			dataType: "script",
            cache : true
		});
    };
})();

// Return a public API
return {
    getData: function () {},
    getDegreeLevels: function () {},
    getCategories: function (degreeLevelId) {},
    getSubjects: function (categoryId, degreeLevelId) {}
};

})();
