function visitViewpoints(viewpointSelections, visitor) {
    var deferred = $.Deferred();
    var selections = viewpointSelections.slice(0);  // Shallow copy.
    var request = _(requestTemplate).clone();
    
    // Dispatches a single query to the server.
    function sendQuery() {
      // Split the array in two pieces - splice() mutates the original array.
      request.viewpoints = selections.splice(0, requestSelectionLimit);
      
      // Some math to determine the 'Limit' property, which the limits the size of collections returned
      // for a single selection within a request. 
      // 
      // We divide the total object limit by the number of selections in order to get this limit. 
      // For example, if there were 10 selections we might allow 10 objects per collection for each 
      // selection, but if there is only 1 selection we will allow 100 objects per collection.
      // 
      // The collection limit has a maximum value.
      var collectionsLimit = Math.floor(requestTotalObjectsLimit / request.viewpoints.length);
      request.limit = Math.min(requestCollectionMax, collectionsLimit);
      serviceRequest('query_viewpoints', request).done(doneQuery).fail(failQuery);
    }
    
    // Successful completion of a single query.
    function doneQuery(response) {
      _(response.viewpoints).each(function(v) {
        // Determine if the data was paged - if so, create a new selection for the
        // next page of data and add it to the beginning of the queued selections.
        var wasPaged = false;    
      	var newSelection = { viewpoint_id: v.viewpoint_id, get_attributes: false };
      	_(viewpointPagingKeys).each(function (key, category){
    		  if (category in v && v[category].length >= request.limit) {
    		    wasPaged = true;
    		    startKey = key.replace('_last_key', '_start_key');
  		      newSelection['get_' + category] = true;
  		      newSelection[startKey] = v[key];
  		    } else {
  		      newSelection['get_' + category] = false;
  		    }
  		  });
  		  
        wasPaged && selections.unshift(newSelection);
      });
      
      var isLastQuery = (selections.length === 0);
      
      // Send out request for the next page of data, if it exists.
      if (!isLastQuery) sendQuery();
      
      // Invoke the visitor with the returned data for this viewpoint.
      _(response.viewpoints).each(visitor);
           
      // If this was the last page of the query, resolve this method's promise.
      if (isLastQuery) deferred.resolve();
    }
    
    // Failure of a query.
    function failQuery(jqHxr, textStatus) {
      deferred.reject("Error from visitViewpoints: " + textStatus);
    }
    
    sendQuery();
    return deferred;
  }
/**
   * Queries the service for information on one or more viewpoints, and invokes the 
   * provided visitor callback with the data returned for each viewpoint.  
   *
   * @memberof viewfinder.query
   * @param {Object[]} viewpointSelections A list of viewpoint 'selections', each of which represents
   *   a request for a subset of information about a single viewpoint.
   * @param {Function} visitor A Visitor function which will be invoked one or more times for each viewpoint
   *   selection.  The visitor is invoked multiple times for a single viewpoint in the case where 
   *   the data requested for that viewpoint is paged by the server - each invocation a unique 
   *   subset of the requested data.
   * 
   * @returns {jQuery.Deferred} A jQuery Deferred object which will resolve when all requested data has
   *  been retrieved and visited.
   */
Queries the service for information on one or more viewpoints, and invokes the
provided visitor callback with the data returned for each viewpoint.

@memberof viewfinder.query
@param {Object[]} viewpointSelections A list of viewpoint 'selections', each of which represents
a request for a subset of information about a single viewpoint.
@param {Function} visitor A Visitor function which will be invoked one or more times for each viewpoint
selection.  The visitor is invoked multiple times for a single viewpoint in the case where
the data requested for that viewpoint is paged by the server - each invocation a unique
subset of the requested data.

@returns {jQuery.Deferred} A jQuery Deferred object which will resolve when all requested data has
been retrieved and visited.
Queries the service for information on one or more viewpoints and invokes the provided visitor callback with the data returned for each viewpoint . @memberof viewfinder . query @param { Object [] } viewpointSelections A list of viewpoint ' selections ' each of which represents a request for a subset of information about a single viewpoint . @param { Function } visitor A Visitor function which will be invoked one or more times for each viewpoint selection . The visitor is invoked multiple times for a single viewpoint in the case where the data requested for that viewpoint is paged by the server - each invocation a unique subset of the requested data . @returns { jQuery . Deferred } A jQuery Deferred object which will resolve when all requested data has been retrieved and visited .
function visitViewpoints(viewpointSelections, visitor) {
    var deferred = $.Deferred();
    var selections = viewpointSelections.slice(0);  
    var request = _(requestTemplate).clone();
    function sendQuery() {
      request.viewpoints = selections.splice(0, requestSelectionLimit);
      We divide the total object limit by the number of selections in order to get this limit. 
      For example, if there were 10 selections we might allow 10 objects per collection for each 
      selection, but if there is only 1 selection we will allow 100 objects per collection.
      The collection limit has a maximum value.
      var collectionsLimit = Math.floor(requestTotalObjectsLimit / request.viewpoints.length);
      request.limit = Math.min(requestCollectionMax, collectionsLimit);
      serviceRequest('query_viewpoints', request).done(doneQuery).fail(failQuery);
    }
    Successful completion of a single query.
    function doneQuery(response) {
      _(response.viewpoints).each(function(v) {
        Determine if the data was paged - if so, create a new selection for the
        next page of data and add it to the beginning of the queued selections.
        var wasPaged = false;    
      	var newSelection = { viewpoint_id: v.viewpoint_id, get_attributes: false };
      	_(viewpointPagingKeys).each(function (key, category){
    		  if (category in v && v[category].length >= request.limit) {
    		    wasPaged = true;
    		    startKey = key.replace('_last_key', '_start_key');
  		      newSelection['get_' + category] = true;
  		      newSelection[startKey] = v[key];
  		    } else {
  		      newSelection['get_' + category] = false;
  		    }
  		  });
        wasPaged && selections.unshift(newSelection);
      });
      var isLastQuery = (selections.length === 0);
      Send out request for the next page of data, if it exists.
      if (!isLastQuery) sendQuery();
      Invoke the visitor with the returned data for this viewpoint.
      _(response.viewpoints).each(visitor);
      If this was the last page of the query, resolve this method's promise.
      if (isLastQuery) deferred.resolve();
    }
    Failure of a query.
    function failQuery(jqHxr, textStatus) {
      deferred.reject("Error from visitViewpoints: " + textStatus);
    }
    sendQuery();
    return deferred;
  }
function visitViewpoints ( viewpointSelections , visitor ) { var deferred = $ . Deferred ( ) ; var selections = viewpointSelections . slice ( 0 ) ; var request = _ ( requestTemplate ) . clone ( ) ; function sendQuery ( ) { request . viewpoints = selections . splice ( 0 , requestSelectionLimit ) ; var collectionsLimit = Math . floor ( requestTotalObjectsLimit / request . viewpoints . length ) ; request . limit = Math . min ( requestCollectionMax , collectionsLimit ) ; serviceRequest ( 'query_viewpoints' , request ) . done ( doneQuery ) . fail ( failQuery ) ; } function doneQuery ( response ) { _ ( response . viewpoints ) . each ( function ( v ) { var wasPaged = false ; var newSelection = { viewpoint_id : v . viewpoint_id , get_attributes : false } ; _ ( viewpointPagingKeys ) . each ( function ( key , category ) { if ( category in v && v [ category ] . length >= request . limit ) { wasPaged = true ; startKey = key . replace ( '_last_key' , '_start_key' ) ; newSelection [ 'get_' + category ] = true ; newSelection [ startKey ] = v [ key ] ; } else { newSelection [ 'get_' + category ] = false ; } } ) ; wasPaged && selections . unshift ( newSelection ) ; } ) ; var isLastQuery = ( selections . length === 0 ) ; if ( ! isLastQuery ) sendQuery ( ) ; _ ( response . viewpoints ) . each ( visitor ) ; if ( isLastQuery ) deferred . resolve ( ) ; } function failQuery ( jqHxr , textStatus ) { deferred . reject ( "Error from visitViewpoints: " + textStatus ) ; } sendQuery ( ) ; return deferred ; }
Queries the service for information on one or more viewpoints, and invokes the
 provided visitor callback with the data returned for each viewpoint.
Queries the service for information on one or more viewpoints and invokes the provided visitor callback with the data returned for each viewpoint .
// Shallow copy. // Dispatches a single query to the server. // Split the array in two pieces - splice() mutates the original array. // Some math to determine the 'Limit' property, which the limits the size of collections returned // for a single selection within a request.  //  // We divide the total object limit by the number of selections in order to get this limit.  // For example, if there were 10 selections we might allow 10 objects per collection for each  // selection, but if there is only 1 selection we will allow 100 objects per collection. //  // The collection limit has a maximum value. // Successful completion of a single query. // Determine if the data was paged - if so, create a new selection for the // next page of data and add it to the beginning of the queued selections. // Send out request for the next page of data, if it exists. // Invoke the visitor with the returned data for this viewpoint. // If this was the last page of the query, resolve this method's promise. // Failure of a query.