Mega-refactoring and docs
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js b/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
index cd467ce..fc93875 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
@@ -13,19 +13,43 @@
 * same basic functionality. 
 */
 function AsterixDBConnection(configuration) {
+    // Initialize AsterixDBConnection properties
     this._properties = {};
+    
+    // Set dataverse as null for now, this needs to be set by the user.
     this._properties["dataverse"] = "";
+    
+    // By default, we will wait for calls to the REST API to complete. The query method
+    // sends a different setting when executed asynchronously. Calls that do not specify a mode
+    // will be executed synchronously.
     this._properties["mode"] = "synchronous";
-    this._properties["error"] = function (e) {
-        // TODO Needs to be more informative
-        //alert(e);
-    }
+    
+    // These are the default error behaviors for Asterix and ajax errors, respectively. 
+    // They can be overridden by calling initializing your AsterixDBConnection like so:
+    // adb = new AsterixDBConnection({
+    //                                  "error" : function(data) {
+    //                                              // override here...
+    //                                  });
+    // and similarly for ajax_error, just pass in the configuration as a json object.
+    this._properties["error"] = function(data) {
+        alert("Asterix REST API Error:\n" + data["error-code"][0] + "\n" + data["error-code"][1]);
+    };
+    
+    this._properties["ajax_error"] = function(message) {
+        alert("[Ajax Error]\n" + message);
+    };
 
-    // This is a demo setup related fix. Enabled by proxy to Asterix REST API.
-    this._properties["endpoint_root"] = "/";
+    // This is the default path to the local Asterix REST API. Can be overwritten for remote configurations
+    // or for demo setup purposes (such as with a proxy handler with Python or PHP.
+    this._properties["endpoint_root"] = "http://localhost:19002/";
     
-    var configuration = arguments || {};
-    
+    // If we have passed in a configuration, we will update the internal properties
+    // using that configuration. You can do things such as include a new endpoint_root,
+    // a new error function, a new dataverse, etc. You can even store extra info.
+    //
+    // NOTE Long-term, this should have more strict limits.
+    var configuration = configuration || {};
+
     for (var key in configuration) {
         this._properties[key] = configuration[key];
     }
@@ -199,31 +223,93 @@
 * http://asterix.ics.uci.edu/documentation/api.html
 *
 * This is treated as an internal method for making the actual call to the API.
-* TODO Fix jquery dependency
 */
 AsterixDBConnection.prototype._api = function(json, onSuccess, endpoint) {
+
+    // The success function is called if the response is successful and returns data,
+    // or is just OK.
     var success_fn = onSuccess;
+    
+    // This is the error function. Called if something breaks either on the Asterix side
+    // or in the Ajax call.
     var error_fn = this._properties["error"];
+    var ajax_error_fn = this._properties["ajax_error"];
+    
+    // This is the target endpoint from the REST api, called as a string.
     var endpoint_url = this._properties["endpoint_root"] + endpoint;    
 
-    $.ajax({
-        type: 'GET',
-        url: endpoint_url,
-        data : json,
-        dataType: "json",
-        success: function(data) {
-            success_fn(data);
-        },
-        error: function(xhr, status, error) {
-            error_fn(error);
+    // This SDK does not rely on jQuery, but utilizes its Ajax capabilities when present.
+    if (window.jQuery) {
+        $.ajax({
+        
+            // The Asterix API does not accept post requests.
+            type        : 'GET',
+            
+            // This is the endpoint url provided by combining the default
+            // or reconfigured endpoint root along with the appropriate api endpoint
+            // such as "query" or "update".
+            url         : endpoint_url,
+            
+            // This is the data in the format specified on the API documentation.
+            data        : json,
+            
+            // We send out the json datatype to make sure our data is parsed correctly. 
+            dataType    : "json",
+            
+            // The success option calls a function on success, which in this case means
+            // something was returned from the API. However, this does not mean the call succeeded
+            // on the REST API side, it just means we got something back. This also contains the
+            // error return codes, which need to be handled before we call th success function.
+            success     : function(data) {
+                
+                // Check Asterix Response for errors
+                // See http://asterix.ics.uci.edu/documentation/api.html#ErrorCodes
+                if (data["error-code"]) { 
+                    error_fn(data);
+                    
+                // Otherwise, run our provided success function
+                } else {
+                    success_fn(data);
+                }
+            },
+            
+            // This is the function that gets called if there is an ajax-related (non-Asterix)
+            // error. Network errors, empty response bodies, syntax errors, and a number of others
+            // can pop up. 
+            error       : function(data) {
+                // Some of the Asterix API endpoints return empty responses on success.
+                // However, the ajax function treats these as errors while reporting a
+                // 200 OK code with no payload. So we will check for that, otherwise 
+                // alert of an error. An example response is as follows:
+                // {"readyState":4,"responseText":"","status":200,"statusText":"OK"}
+                if (data["status"] == 200 && data["responseText"] == "") {
+                    success_fn(data);
+                } else {
+                    thi
+                     alert("[Ajax Error]\n" + JSON.stringify(data));
+                }
+            }
+        });
+    } else {
+        alert("no jquery");
+        var xmlhttp;
+    }
+    // XML Http Request
+    /*var xmlhttp;
+    
+    xmlhttp = new XMLHttpRequest();
+    xmlhttp.onreadystatechange = function(){
+        if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
+            alert(xmlhttp.responseText);
+            //callback(xmlhttp.responseText);
         }
-    });
+    }
+    xmlhttp.open("GET", endpoint_url, true);
+    xmlhttp.send();*/
     
     return this;
 };
 
-// TODO Better documentation below here.
-
 // Asterix Expressions - Base
 function AExpression () {
 
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
index fb83406..2d42251 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
@@ -1,7 +1,40 @@
 $(function() {	
 	
-	// Connection to AsterixDB - Just one needed!   
-	A = new AsterixDBConnection().dataverse("twitter");
+	// Initialize connection to AsterixDB. Just one connection is needed and contains
+	// logic for connecting to each API endpoint. This object A is reused throughout the
+	// code but does not store information about any individual API call.  
+	A = new AsterixDBConnection({
+	
+	    // We will be using the twitter dataverse, which we can configure either like this
+	    // or by following our AsterixDBConnection with a dataverse call, like so:
+	    // A = new AsterixDBConnection().dataverse("twitter");
+	    "dataverse" : "twitter",
+	    
+	    // Due to the setup of this demo using the Bottle server, it is necessary to change the
+	    // default endpoint of API calls. The proxy server handles the call to http://localhost:19002
+	    // for us, and we reconfigure this connection to connect to the proxy server.
+	    "endpoint_root" : "/",
+	  
+	    // Finally, we want to make our error function nicer so that we show errors with a call to the
+	    // reportUserMessage function. Update the "error" property to do that.
+	    "error" : function(data) {
+	                // For an error, data will look like this:
+	                // {
+	                //     "error-code" : [error-number, error-text]
+	                //     "stacktrace" : ...stack trace...
+	                //     "summary"    : ...summary of error...
+	                // }
+	                // We will report this as an Asterix REST API Error, an error code, and a reason message.
+	                // Note the method signature: reportUserMessage(message, isPositiveMessage, target). We will provide
+	                // an error message to display, a positivity value (false in this case, errors are bad), and a 
+	                // target html element in which to report the message.
+	                var showErrorMessage = "Asterix Error #" + data["error-code"][0] + ": " + data["error-code"][1];
+	                var isPositive = false;
+	                var showReportAt = "report-message";
+	        
+	                reportUserMessage(showErrorMessage, isPositive, showReportAt);
+	              }
+	});
 	
     // Following this is some stuff specific to the Black Cherry demo
     // This is not necessary for working with AsterixDB
@@ -29,7 +62,7 @@
     $("#clear-button").button().click(function () {
         mapWidgetResetMap();
         
-        $('#explore-report-message').html('');
+        $('#report-message').html('');
         $('#query-preview-window').html('');
         $("#metatweetzone").html('');
     });
@@ -187,10 +220,10 @@
     	
         var kwterm = $("#keyword-textbox").val();
         if (kwterm == "") {
-            addFailureBlock("Please enter a search term!", "explore-report-message");
+            reportUserMessage("Please enter a search term!", false, "report-message");
         } else {
         
-            $("#explore-report-message").html('');
+            $("#report-message").html('');
             $("#submit-button").attr("disabled", true);
     	
             var startdp = $("#start-date").datepicker("getDate");
@@ -258,7 +291,6 @@
     });
 });
 
-
 function buildAQLQueryFromForm(parameters) {
 
     var bounds = {
@@ -295,7 +327,6 @@
     return aql;
 }
 
-
 /**
 * getAllDataverseTweetbooks
 * 
@@ -339,7 +370,6 @@
     
 }
 
-
 /** Asynchronous Query Management **/
 
 /**
@@ -353,7 +383,6 @@
     }
 }
 
-
 /**
 * Returns current time interval to check for asynchronous query readiness
 * @returns  {number}    milliseconds between asychronous query checks
@@ -363,7 +392,6 @@
     return seconds * 1000;
 }
 
-
 /**
 * Retrieves status of an asynchronous query, using an opaque result handle from API
 * @param    {Object}    handle, an object previously returned from an async call
@@ -388,7 +416,6 @@
      );
 }
 
-
 /**
 * On-success callback after async API query
 * @param    {object}    res, a result object containing an opaque result handle to Asterix
@@ -464,7 +491,6 @@
     $("#submit-button").attr("disabled", false);
 }
 
-
 /**
 * returns a json object with keys: weight, latSW, lngSW, latNE, lngNE
 *
@@ -486,7 +512,6 @@
             .replace("int64", '"int64"');
 }
 
-
 /**
 * A spatial data cleaning and mapping call
 * @param    {Object}    res, a result object from a cherry geospatial query
@@ -666,7 +691,6 @@
     return drillDown;
 }
 
-
 function onDrillDownAtLocation(tO) {
 
     var tweetId = tO["tweetEntryId"];
@@ -719,6 +743,7 @@
         $("#modal-save-tweet-panel").show();
         
         // Now, when adding a comment on an available tweet to a tweetbook
+        $('#save-comment-tweetbook-modal').unbind('click');
         $("#save-comment-tweetbook-modal").on('click', function(e) {
         
             // Stuff to save about new comment
@@ -728,12 +753,12 @@
         
             // Make sure content is entered, and then save this comment.
             if ($("#modal-body-add-note").val() == "") {
-            
-                addFailureBlock("Please enter a comment.", "modal-body-message-holder");
+
+                reportUserMessage("Please enter a comment about the tweet", false, "report-message");
             
             } else if ($("#modal-body-add-to").val() == "") {
             
-                addFailureBlock("Please enter a tweetbook.", "modal-body-message-holder");
+                reportUserMessage("Please enter a tweetbook.", false, "report-message");
             
             } else {
         
@@ -749,21 +774,24 @@
                         "comment-text" : save_metacomment_target_comment 
                     }
                 );
-                A.update(toInsert.val(), function () {});
+                
+                A.update(toInsert.val(), function () {
+                    var successMessage = "Saved comment on <b>Tweet #" + tweetId + 
+                        "</b> in dataset <b>" + save_metacomment_target_tweetbook + "</b>.";
+                    reportUserMessage(successMessage, true, "report-message");
             
-                var successMessage = "Saved comment on <b>Tweet #" + tweetId + 
-                    "</b> in dataset <b>" + save_metacomment_target_tweetbook + "</b>.";
-                addSuccessBlock(successMessage, "modal-body-message-holder");
-            
-                $("#modal-body-add-to").val('');
-                $("#modal-body-add-note").val('');
-                $("#modal-body-message-holder").html('');
+                    $("#modal-body-add-to").val('');
+                    $("#modal-body-add-note").val('');
+                    $('#save-comment-tweetbook-modal').unbind('click');
+                    
+                    // Close modal
+                    $('#drilldown_modal').modal('hide');
+                });
             }   
         });
     }
 }
 
-
 /**
 * Adds a new tweetbook entry to the menu and creates a dataset of type TweetbookEntry.
 */
@@ -782,7 +810,6 @@
     }
 }
 
-
 function onDropTweetBook(tweetbook_title) {
 
     // AQL Call
@@ -802,7 +829,6 @@
     }
 }
 
-
 function addTweetBookDropdownItem(tweetbook) {
     // Add placeholder for this tweetbook
     $('<div/>')
@@ -829,7 +855,6 @@
     );
 }
 
-
 function onPlotTweetbook(tweetbook) {
     
     // Clear map for this one
@@ -856,7 +881,6 @@
     A.query(plotTweetQuery.val(), onTweetbookQuerySuccessPlot);     
 }
 
-
 function onTweetbookQuerySuccessPlot (res) {
 
     var records = res["results"];
@@ -913,7 +937,6 @@
     });
 }
 
-
 function existsTweetbook(tweetbook) {
     if (parseInt($.inArray(tweetbook, review_mode_tweetbooks)) == -1) {
         return false;
@@ -922,7 +945,6 @@
     }
 }
 
-
 function onCleanPlotTweetbook(records) {
     var toPlot = [];
 
@@ -946,7 +968,6 @@
     return toPlot;
 }
 
-
 function onCleanTweetbookDrilldown (rec) {
 
     var drilldown_cleaned = [];
@@ -968,7 +989,6 @@
     return drilldown_cleaned;
 }
 
-
 function onClickTweetbookMapMarker(tweet_arr) {
     // Clear existing display
     $.each(tweet_arr, function (t, valueT) {
@@ -981,7 +1001,6 @@
 
 /** Toggling Review and Explore Modes **/
 
-
 /**
 * Explore mode: Initial map creation and screen alignment
 */
@@ -997,7 +1016,6 @@
     $('#right-col').height(explore_column_height + "px");
 }
 
-
 /**
 * Launching explore mode: clear windows/variables, show correct sidebar
 */
@@ -1009,14 +1027,12 @@
     $('#review-active').removeClass('active');
     $('#review-well').hide();
     
-    
     $('#explore-active').addClass('active'); 
     $('#explore-well').show();
     
     $("#clear-button").trigger("click");
 }
 
-
 /**
 * Launching review mode: clear windows/variables, show correct sidebar
 */
@@ -1034,7 +1050,6 @@
     $("#clear-button").trigger("click");
 }
 
-
 /**
 * Lauching about mode: hides all windows, shows row containing about info
 */
@@ -1063,34 +1078,29 @@
     $('#' + iconId).on('click', onClick);
 }
 
-
 /**
-* Creates a success message and attaches it to a div with provided ID.
+* Creates a message and attaches it to data management area.
 * @param    {String}    message, a message to post
-* @param    {String}    appendTarget, a target div to which to append the alert
+* @param    {Boolean}   isPositiveMessage, whether or not this is a positive message.
+* @param    {String}    target, the target div to attach this message.
 */
-function addSuccessBlock(message, appendTarget) {
-    $('#' + appendTarget).html('');
-    $('<div/>')
-        .attr("class", "alert alert-success")
-        .html('<button type="button" class="close" data-dismiss="alert">&times;</button>' + message)
-        .appendTo('#' + appendTarget);
-}
-
-/**
-* Creates a failure mesage and attaches it to a div with provided id.
-* @param    {String}    message, a message to post
-* @param    {String}    target, a target div to append the message
-*/
-function addFailureBlock(message, target) {
+function reportUserMessage(message, isPositiveMessage, target) {
+    // Clear out any existing messages
     $('#' + target).html('');
+    
+    // Select appropriate alert-type
+    var alertType = "alert-success";
+    if (!isPositiveMessage) {
+        alertType = "alert-danger";
+    }
+    
+    // Append the appropriate message
     $('<div/>')
-        .attr("class", "alert alert-danger")
+        .attr("class", "alert " + alertType)
         .html('<button type="button" class="close" data-dismiss="alert">&times;</button>' + message)
         .appendTo('#' + target);
 }
 
-
 /**
 * mapWidgetResetMap
 *