form validation and completion with mqlread

[ permalink ] [ download ]
/**
 * Add an onchange event handler to the specified textfield object
 * to validate the user's input and autocomplete it if necessary.
 * The type argument is the Metaweb type, such as "/music/artist"
 * for which autocompletion should be done.
 *
 * The optional message argument specifies a document element into
 * which error messages (such as "invalid input" or "incomplete input"
 * should be displayed) The optional constraints argument is an object
 * that contains additional MQL properties that should be added to the
 * query. This can be use to further constrain the autocompletion
 * beyond simple type-based autocompletion.
 *
 * If the callback handler determines that the user's input is invalid or
 * incomplete, it sets the cssClass property to "invalid" or "incomplete" 
 * (overwriting any other class values specified by that property).
 * You can define these CSS classes to set background colors or otherwise
 * highlight fields that require the user's attention.
 *
 * Validity and autocompletion is done using the ~= pattern matching
 * operator to ask for results of the specified type that begin with
 * the specified string. The query requests that results are sorted by
 * name and that only the first two are returned.  If no results are
 * returned, this means that the user's input is invalid.  If exactly
 * one result is returned, then the user's input is unique and
 * autocompletion is performed. If two results are returned, then the
 * user's input is not unique, but may still be valid, if the input
 * matches the first result (because the results are sorted), and that
 * result is a prefix of the second.  If two results are returned and
 * the first result is not the same as the user's input, then the
 * input is incomplete.
 *
 * This function sets the onchange and class attributes of the text
 * field element and should not be used with HTML elements that set
 * these attributes themselves.  Also, this function alters the
 * visibility and content of the optional message element.
 */
Metaweb.addValidationAndCompletion = function(textfield, type,
                                              message, constraints) 
{
    // Ensure that the message element, if any, is hidden
    if (message) message.style.visibility = "hidden";

    // And add an event handler to the text field.
    textfield.onchange = function() {
        // Get the user's input and convert to lowercase.
        // Metaweb does case-insensitive pattern matching.
        var input = this.value.toLowerCase();

        // This is the MQL query we use for autocompletion
        var query = [{
            type: type,                   // Find objects of this type
            name: null,                   // We want to know object name 
            "name~=": "^" + input + "*",  // ^ and * make this a prefix match
            sort: "name",                 // Shortest completions first
            limit: 2                      // We only care about the first 2
        }];

        // Add any additional constraints to the query
        if (constraints) for (c in constraints) query[0][c] = constraints[c];

        // Now submit the query and pass the results to the nested function
        Metaweb.read(query, function(results) {
                         // If there are no results, input is invalid
                         if (results.length == 0) {
                             // Set invalid class and display invalid message
                             textfield.className = "invalid";
                             if (message) {
                                 message.innerHTML = "invalid input";
                                 message.style.visibility = "visible";
                             }
                         }
                         // If there is one result or if the first result
                         // matches exactly, input is valid.
                         else if (results.length == 1 ||
                                  results[0].name.toLowerCase() == input) {
                             // Autocomplete the value.
                             // Use capitalization Metaweb returns to us
                             textfield.value = results[0].name; 
                             // Clear class and message
                             textfield.className = "";
                             if (message) {
                                 message.innerHTML = "";
                                 message.style.visibility = "hidden";
                             }
                         }
                         // If there is more than one result and no match
                         // then the user's input is incomplete
                         else {
                             // Set incomplete class and message
                             textfield.className = "incomplete";
                             if (message) {
                                 message.innerHTML="incomplete input";
                                 message.style.visibility = "visible";
                             }
                         }
                     });
    }
}
hits counter