/**************************************** W3C mobileOK Checker -------------------- Javascript methods to handle user interaction in a report ****************************************/ /** The checker variable contains all the functions needed to handle the behavior of the mobileOK Checker in the index, the intermediary, and the report pages. */ var checker = { /** Registers the function that gets executed when the document is ready. Please note that the report "page" is actually the intermediary page completed with XHTML data retrieved asynchronously. As such, no initialization occurs when the report page is displayed, and any initialization that needs to be applied to the retrieved data must be done by hand when the DOM tree is updated. */ init : function() { $(document).ready(function () { checker.ready(); } ); }, /** Initializes the document. In particular, active the "w3c_javascript" class on the body element so that corresponding CSS styles get used. */ ready : function() { // Activate script-related CSS styles. $("body").addClass("w3c_javascript"); // Prepare settings for asynchronous processing checker.async.ready(); // Prepare report page checker.report.ready(); }, /** Tree-like methods to handle expandable/collapsible lists */ tree : { /** Initializes the behavior of expandable sections. Parameters: - sectionName: class name to use to identify the sections to initialize - sectionTitle: class name to use to identify the title (or first line) of the expandable section */ init : function(sectionName, sectionTitle) { // Apply the initialize to each $("." + sectionName).each(function() { // Expandable sections are closed by default $(this).addClass("closed"); // Attach click handlers to the title line of // the expandable section to toggle the whole section $(this).children("." + sectionTitle).each(function() { $(this).click(function(event) { event.preventDefault(); checker.tree.toggle($(this).parents("." + sectionName)); }); }); }); }, /** Toggles the given element, using the "closed" CSS style. */ toggle : function(oEl) { $(oEl).toggleClass("closed"); // Handle the } }, /** Asynchronous methods to handle the intermediary page TODO: note this needs to be JQuery-zed. */ async : { /** Initializes asynchronous processing. */ ready : function() { // Set the async parameter if possible $("#async").each(function() { var enoughSupport = checker.async.newXMLHttpRequest() && window.setInterval && document.createElement && ((document.importNode && document.documentElement.replaceChild) || document.documentElement.outerHTML); if (enoughSupport) { $(this).val("true"); } }); // Start polling if processing is running if ($("#still_running").size() > 0) { checker.async.startPolling(); }; }, /** Global variables. */ polling : false, // true when an HTTP request is being processed. windowTimer : null, // Window's timer for auto-refresh. /** List of browser-dependent factories to create the XMLHttpRequest object. */ XMLHttpFactories : [ function () {return new XMLHttpRequest()}, function () {return new ActiveXObject("Msxml2.XMLHTTP")}, function () {return new ActiveXObject("Msxml3.XMLHTTP")}, function () {return new ActiveXObject("Microsoft.XMLHTTP")}, ], /** Creates a new XMLHttpRequest object. */ newXMLHttpRequest : function() { var a = checker.async; var req = false; for (var i=0; i< a.XMLHttpFactories.length; i++) { try { req = a.XMLHttpFactories[i](); } catch (e) { continue; } break; } return req; }, /** Sends an HTTP request asynchronously. The method returns immediately, the callback method is called when the request is over. Returns true when the request could be sent, false when an error occurred. */ sendRequest : function(url, callback, postdata) { var req = checker.async.newXMLHttpRequest(); if (!req) return false; var method = (postdata) ? "POST" : "GET"; try { req.open(method, url, true); req.setRequestHeader("User-Agent", "W3C-mobileOK/XMLHttp1.0"); if (postdata) req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // Force text/xml because MSXML implementations prior than 6.0 // do not set responseXML when the Content-Type is not "text/xml". req.setRequestHeader("Accept", "text/xml"); req.onreadystatechange = function () { if (req.readyState != 4) return; callback(req); } req.send(postdata); } catch(e) { return false; } return true; }, /** Polls the server for progress. */ pollStatus : function() { var a = checker.async; if (!a.polling) { a.polling = true; var data = "task=" + document.getElementById("task").value; a.sendRequest("check", a.handlePollResponse, data); } }, /** Processes the response sent by the server and updates the document's content consequently. */ handlePollResponse : function(req) { var a = checker.async; try { if (req.status != 200) { // An error occurred a.stopPolling(); alert("Sorry, the server returned an error."); } else if (!req.responseXML) { // Empty or non-XML response received a.stopPolling(); alert("Sorry, the server returned an empty or invalid response.\n"); //alert(req.responseText); } else { var task; var status; taskid = document.getElementById("task").value; tasks = req.responseXML.getElementsByTagName("task"); for (var i = 0, il = tasks.length; i < il; i++) { if (tasks[i].getAttribute("id") == taskid) { task = tasks[i]; break; } } if (task) { statuses = task.getElementsByTagName("status"); if (statuses.length > 0) { status = statuses[0].firstChild.nodeValue; } } if (!status) { // Unknown response format. a.stopPolling(); if (task) { alert("task found but no status!"); } else { alert("Sorry, the server returned an unexpected response."); } alert(req.responseText); } else if (status == "done") { // Done, let's redirect the user to the results page a.stopPolling(); var href = document.location.href; if (href.indexOf("?") != -1) { href = href.substring(0, href.indexOf("?")); } $("#docAddr").removeAttr("disabled"); href += "?" + $("input").not("#cancel").serialize(); $("#docAddr").attr("disabled", "disabled"); document.location.href = href; } else if (status == "error") { // An error occurred a.stopPolling(); alert("An internal error occurred. Please try again in a moment."); alert(req.responseText); } } } catch (e) { alert("Sorry, an unexpected error occurred."); alert(e.message); a.stopPolling(); } a.polling = false; }, /** Starts polling for progress every 5 second. */ startPolling : function() { checker.async.windowTimer = window.setInterval("checker.async.pollStatus()", 5000); }, /** Stops polling for progress. */ stopPolling : function() { if (checker.async.windowTimer) { window.clearInterval(checker.async.windowTimer); // At the end of the polling period, there should be a report, // so let's prepare it! checker.report.ready(); } } }, /** Report's page specific methods */ report : { /** Initializes the report for scripting needs */ ready : function() { // Prepare expandable sections checker.tree.init("expand_section", "expand_title"); // Add expand all / collapse all links $("#details").after("
"); $("#expand_all").click(function(event) { event.preventDefault(); $("#results .expand_section").filter(".closed").each(function() { checker.tree.toggle($(this)); }); }); $("#collapse_all").click(function(event) { event.preventDefault(); $("#results .expand_section").filter(":not(.closed)").each(function() { checker.tree.toggle($(this)); }); }); // Make sure that errors linked from the "Where to start..." section // are expanded automatically when user follows the links in that section $("#inshort ul li a").click(function(event) { href = event.target.href; fragment = href.substring(href.indexOf("#")); $(fragment).filter(".closed").each(function() { checker.tree.toggle($(this)); }); }); // Expand critical errors by default $(".expand_section").filter(".err_critical").filter(".closed").each(function () { checker.tree.toggle($(this)); }); } } }; checker.init();