docs: function (type, para) {
  
  		// avoid flooding if SR_PUBLISH is sending streaming data
  		SR.Log('[' + type + '] received');
  		switch (type) {
  			//
  			// pubsub related
  			//
  			// when a new published message arrives 
  			case 'SR_MSG':
  			// handle server-published messages
  			case 'SR_PUBLISH':
  				if (onChannelMessages.hasOwnProperty(para.channel)) {
  					if (typeof onChannelMessages[para.channel] !== 'function')
  						SR.Error('channel [' + para.channel + '] handler is not a function');
  					else
  						onChannelMessages[para.channel](para.msg, para.channel);
  				}
  				else
  					SR.Error('cannot find channel [' + para.channel + '] to publish'); 
  				return true;
  				
  			// when a list of messages arrive (in array)
  			case 'SR_MSGLIST':	
  				var msg_list = para.msgs;			
  				if (msg_list && msg_list.length > 0 && onChannelMessages.hasOwnProperty(para.channel)) {
  					
  					for (var i=0; i < msg_list.length; i++)
  						onChannelMessages[para.channel](msg_list[i], para.channel);
  				}				
  				return true;
  				
  			// redirect to another webpage
  			case 'SR_REDIRECT':
  				window.location.href = para.url;
  				return true;
  			case "SR_NOTIFY" :
  				SR.Warn('SR_NOTIFY para: ');
  				SR.Warn(para);
  				console.log(onChannelMessages);
  				if (onChannelMessages.hasOwnProperty('notify'))
  					onChannelMessages['notify'](para, 'notify');
  				return true;
  			//
  			// login related
  			//
  			case "SR_LOGIN_RESPONSE":
  			case "SR_LOGOUT_RESPONSE":
  				replyLogin(para);
  				return true;
  			case "SR_MESSAGE":
  				alert('SR_MESSAGE: ' + para.msg);
  				return true;
  			case "SR_WARNING":
  				alert('SR_WARNING: ' + para.msg);
  				return true;
  			case "SR_ERROR":
  				alert('SR_ERROR: ' + para.msg);
                  return true;
  				
  			default:
  				// check if custom handlers exist and can handle it
  				if (responseHandlers.hasOwnProperty(type)) {
  					
  					var callbacks = responseHandlers[type];
  					
  					// extract rid if available 
  					var rid = undefined;					
  					if (para.hasOwnProperty('_rid') === true) {				
  						rid = para['_rid'];
  						delete para['_rid'];
  					}
  										
  					if (rid) {
  						callbacks[rid](para, type);	
  						
  						// remove callback once done
  						if (rid !== 'keep') {
  							delete callbacks[rid];
  						}
  					}
  					// otherwise ALL registered callbacks will be called
  					else {
  						if (Object.keys(callbacks).length > 1)
  							SR.Warn('[' + type + '] no rid in update, dispatching to first of ' +  Object.keys(callbacks).length + ' callbacks');
  						
  						// call the first in callbacks then remove it
  						// so only one callback is called unless it's registered via the 'keep_callback' flag
  						for (var key in callbacks) {
  							callbacks[key](para, type);
  							if (key !== 'keep') {
  								delete callbacks[key];
  								break;
  							}
  						}
  					}
  
  					return true;
  				}
  	
  				// still un-handled
  				console.error('onResponse: unrecongized type: ' + type);
  				return false;
  		}
  	}