/*
 * 	vwd Streamer, CopyRight(c) 2009 vwd group
 * 
 *  Author: Sander Moelands, Ruben Prins
 */

function vwdStreamer() {
	this.version = "1.0.3";
	this.initialized = false;
	this.initializationStarted = false;
    this.connected = false;
    this.reconnected = false;
    this.requestFormAvailable = false;
	this.functionQueue = new vwdStreamerQueue();
    this.heartbeatInterval = 5 * 1000; // 5 seconds
    this.reconnectInterval = 15 * 60 * 1000 // 15 minute

/*******************************************************************************************************
 * Methods accessed by the implementing client
 *******************************************************************************************************
 */
	this.initialize = function(domain, host, port) {
		// Make sure our streamer instance is known within the document
        // This is used for communicating between the streamer instance and the backing datapump
		document.vwdStreamerInstance = this;	

		this.domain = domain;
		this.host = host;
		this.port = port;

		this.mappings = new Array();
        this.reversedMappings = new Array();
		this.subscribers = new Array();
		this.logListeners = new Array();
		this.cachedValues = new Array();
		this.triggers = new Array();
		this.parsers = new Array();
		this.subscribedFields = new Array();
		
		this._initializeTriggers();
		this._initializeParsers();
				
		if (this.domain != null) {
			document.domain = this.domain;
		}
		
		var _self = this;
		
		// Attach the initialization to the onLoad event
		if (document.addEventListener) {
			window.addEventListener("load", function(){
				_self._initializeComponents();
			}, false);
		} else if (document.attachEvent) {
			window.attachEvent("onload", function(){
				_self._initializeComponents();
			});
		} 
		// Also initialize the components with a timer. If additional resources on the page take a long time to load, we do not want
		// to wait on the onLoad event.
		setTimeout( function() { _self._initializeComponents(); }, 1000);
		
		// Start the function handler, used for making sure that the streamer is initialized
		setInterval( function() { if (_self.initialized) {_self.functionQueue.flush();} }, 100);
        // Start the heartbeat check and reconnect interval timer
        setInterval( function() { _self._checkHeartbeatAndReconnect(); }, this.heartbeatInterval);
	}
	
	this.connect = function(sessionId) {
        this._log(vwdStreamerLogLevel.TRACE, "Connecting with SessionId: " + sessionId);

        this.lastConnectTime = new Date();
        
        this.sessionId = null;
		var _self = this;
		this.functionQueue.enqueue( function() { 
			_self.responseFrame.src = _self._getResponseURL(sessionId); 
		} );
	}
		
	this.getSubscription = function(postbackMethod) {
		var newSubscription = new vwdStreamerSubscription(this, postbackMethod);
		this.subscribers[this.subscribers.length] = newSubscription;
		return newSubscription;
	}
		
	this.removeSubscription = function(subscriber) {
		var subscriberIndexToRemove = -1
		for (var subscriberIndex in this.subscribers) {
			if (this.subscribers[subscriberIndex] == subscriber) {
				subscriberIndexToRemove = subscriberIndex;
				break;
			}
		}
		
        subscriber.unsubscribeAllFields();
		this.subscribers.splice(subscriberIndexToRemove, 1);
	}
	
	this.addLogListener = function(listener, level) {
		var logListener = {listener: listener, logLevel: level};
		this.logListeners[this.logListeners.length] = logListener;
	}
	
	this.removeLogListener = function(listener) {
		var logListenerIndexToRemove = -1
		for (var logListenerIndex in this.logListeners) {
			if (this.logListeners[logListenerIndex] == listener) {
				listenerIndexToRemove = logListenerIndex;
				break;
			}
		}
		
		this.logListeners.splice(logListenerIndexToRemove, 1);
	}

    this.isConnected = function() {
        return this.connected;
    }
    
    this.getVersion = function() {
    	return this.version;
    }
	

/*******************************************************************************************************
 * Methods used for receiving updates from the server.
 *******************************************************************************************************
 */		
	this.setSessionId = function(sessionId) {
        this._log(vwdStreamerLogLevel.TRACE, "SessionId received: " + sessionId);
		this.sessionId = sessionId;
	}

    this.setRequestFormAvailable = function(isAvailable) {
        this.requestFormAvailable = isAvailable;
    }
	
	this.heartbeatReceived = function() {
		this.lastDataReceived = new Date();
	}

	this.ackSubscribe = function(externalDataName, dataId) {
		this.lastDataReceived = new Date();
        this.mappings[externalDataName] = dataId;
        this.reversedMappings[dataId] = externalDataName;
	}
	
	this.ackUnsubscribe = function(externalDataName) {
		this.lastDataReceived = new Date();
        delete this.reversedMappings[this.mappings[externalDataName]];
        delete this.mappings[externalDataName];

		// Don't forget to remove the cached value
		this._removeCachedValue(externalDataName)
	}
	
	this.update = function(dataId, value, dataType) {
		this.lastDataReceived = new Date();
		var dataName = this._getDataNameById(dataId);
		
		if (dataName == null)
			return;
		
		dataType = vwdStreamerDataType.parseDataType(dataType, value);
		
		if (dataType == vwdStreamerDataType.TIME)
			value = Date.parseVwdStreamerTime(value);
		else if (dataType == vwdStreamerDataType.DATE)
			value = Date.parseVwdStreamerDate(value);
		else if (dataType == vwdStreamerDataType.DATETIME)
			value = Date.parseVwdStreamerDateTime(value);
		
		this._update(dataName, value, dataType);
	}
	
	this.accessDenied = function(dataName, reason) {
		this.lastDataReceived = new Date();
		this._log(vwdStreamerLogLevel.WARNING, dataName + " not subsribed. Reason: " + reason);
	}

    this.subscriptionRefreshReceived = function() {
		this.lastDataReceived = new Date();
    	if (this.reconnected) {
	        this._log(vwdStreamerLogLevel.TRACE, "Refreshing subscription set...");
			var queryString = "";
			for (var subscribedField in this.subscribedFields) {
				queryString += this._getFieldRequestAsString(subscribedField);
			}
	
			this._sendDataToServer(queryString);
    	}
    }
		
/*******************************************************************************************************
 * Internal methods
 *******************************************************************************************************
 */	
	this._initializeTriggers = function() {
        var trigger;
		if (typeof AbsoluteDifferenceTrigger == "function") {
            trigger = new AbsoluteDifferenceTrigger(this);
            this.triggers[trigger.getFieldName()] = trigger; 
        }
		if (typeof RelativeDifferenceTrigger == "function") {
            trigger = new RelativeDifferenceTrigger(this);
            this.triggers[trigger.getFieldName()] = trigger;
        }
	}
	
	this._initializeParsers = function() {
        var parser;
		if (typeof TExParser == "function") {
            parser = new TExParser();
            this.parsers[parser.getFieldName()] = parser;		
        }
		if (typeof ZExParser == "function") {
            parser = new ZExParser();
            this.parsers[parser.getFieldName()] = parser;
        }
		if (typeof BlockExParser == "function") {
            parser = new BlockExParser();
            this.parsers[parser.getFieldName()] = parser;
        }
	}
		
	this._initializeComponents = function() {
		if (this.initializationStarted)
			return; // Don't initialize twice
		
		this.initializationStarted = true;
		
		try { // Fix for stupid IE iframe naming bug
			this.requestFrame = document.createElement("<iframe name=\"vwdStreamerRequestFrame\">");
		} catch (ex) {
			this.requestFrame = document.createElement("iframe");
		}

		this.requestFrame.id = "vwdStreamerRequestFrame";
		this.requestFrame.name = "vwdStreamerRequestFrame";
		this.requestFrame.height = 0;
		this.requestFrame.width = 0;
		this.requestFrame.style.visibility = "hidden";
        this.requestFrame.style.position = "absolute";
		document.body.appendChild(this.requestFrame);
		this.requestFrame.src = this._getRequestFormURL();
        

		try { // Fix for stupid IE iframe naming bug
			this.responseFrame = document.createElement("<iframe name=\"vwdStreamerResponseFrame\">");
		} catch (ex) {
			this.responseFrame = document.createElement("iframe");
		}
		this.responseFrame.id = "vwdStreamerResponseFrame";
		this.responseFrame.name = "vwdStreamerResponseFrame";
		this.responseFrame.height = 0;
		this.responseFrame.width = 0;
		this.responseFrame.style.visibility = "hidden";
        this.responseFrame.style.position = "absolute";
		document.body.appendChild(this.responseFrame);
				
		// Make sure our streamer instance is known within the document
		document.vwdStreamerInstance = this;	
		this.initialized = true;
	}
	
	this._subscribe = function(fields) {
		var queryString = "";
		for (var fieldIndex in fields) {
			queryString += this._subscribeField(fields[fieldIndex]);
			
			if (this._isTriggeredField(this._getFieldName(fields[fieldIndex]))) {
				queryString += this._subscribeToRequiredTriggerSubscriptions(fields[fieldIndex]);
			}
		}
		
		if (queryString.length > 0)
			this._sendDataToServer(queryString);
	}
	
	this._unsubscribe = function(fields) {		
		var queryString = "";
		for (var fieldIndex in fields) {	
			queryString += this._unsubscribeField(fields[fieldIndex]);
			
			if (this._isTriggeredField(this._getFieldName(fields[fieldIndex]))) {
				queryString += this._unsubscribeFromRequiredTriggerSubscriptions(fields[fieldIndex]);
			}
		}
		
		if (queryString.length > 0)
			this._sendDataToServer(queryString);
	}		
		
	this._update = function(dataName, value, dataType, singleSubscriber) {
		// Get the old cached value before the cached value has been updated
		var oldCachedValue = this._getCachedValue(dataName);
		oldCachedValue = oldCachedValue!=null?oldCachedValue.value:null;
		// Make sure we always update the internal cached element
		this._updateCachedValue(dataName, value, dataType)
		
		var updatePacket = new Object();
		updatePacket.dataName = dataName;
		updatePacket.rawValue = value;
		updatePacket.oldRawValue = oldCachedValue;
		
		var fieldParser = this._getFieldParser(this._getFieldName(dataName));
		if (fieldParser == null) {
			updatePacket.type = dataType;
			updatePacket.value = value;
			updatePacket.values = null;
			updatePacket.oldValue = oldCachedValue;
			updatePacket.oldValues = null;
		} else {
			var parsedValues = fieldParser.getParsedValues(updatePacket.rawValue);
			updatePacket.type = parsedValues.type;
			updatePacket.value = parsedValues.value;
			updatePacket.values = parsedValues.values;
			
			var oldParsedValues = fieldParser.getParsedValues(updatePacket.oldRawValue);				
			updatePacket.oldValue = oldParsedValues.value;
			updatePacket.oldValues = oldParsedValues.values;
		}
				
		if (singleSubscriber != null) {
			singleSubscriber.update(updatePacket);
			return; // Don't perform trigger updates if only retrieving a cached value for a singleSubscriber
		} else {
			for (var listenerIndex in this.subscribers) {
				this.subscribers[listenerIndex].update(updatePacket);
			}
		}
		
		for (var triggerIndex in this.triggers) {
			var triggerFields = this.triggers[triggerIndex].getFields();
			for (var triggerFieldIndex in triggerFields) {
				if (triggerFields[triggerFieldIndex] == this._getFieldName(dataName)) {
					this.triggers[triggerIndex].fire(this._getIdentifier(dataName));
				}
			}
		}
	}
	
	this._log = function(level, message) {
		for (var logListenerIndex in this.logListeners) {
			var currentLogListener = this.logListeners[logListenerIndex];
			if (level >= currentLogListener.logLevel)
				currentLogListener.listener(level, message);
		}
	}
		
	this._getDataNameById = function(dataId) {
        return this.reversedMappings[dataId];
	}

	this._getDataIdByName = function(dataName) {
        return this.mappings[dataName];
	}
	
	this._updateCachedValue = function(dataName, value, dataType) {
		if (value == "")
			value = null;

        if (this.cachedValues[dataName] != null) {
            if (value == null)
                delete this.cachedValues[dataName];
            else {
                this.cachedValues[dataName].value = value;
                this.cachedValues[dataName].dataType = dataType;
            }
        } else if (value != null) {
            var newCachedValue = new Object();
            newCachedValue.dataName = dataName;
            newCachedValue.value = value;
            newCachedValue.dataType = dataType;
            this.cachedValues[dataName] = newCachedValue;            
        }
	}
	
	this._getCachedValue = function(dataName) {
        return this.cachedValues[dataName];
	}
	
	this._removeCachedValue = function(dataName) {
        delete this.cachedValues[dataName];
	}
	
	this._sendDataToServer = function(queryString) {
		var _self = this;
		var _queryString = queryString;
		if (this.sessionId == null || this.requestFormAvailable == false) {
			/* Make sure we don't request field subscriptions if the sessionId is not yet available.
			 * The sessionId is required for requesting subscriptions.
			 */
			setTimeout(
					function() {
						_self._sendDataToServer(_queryString);
					}, 100);
			return;
		}

        // The requestform should not be available when performing a request. This will be reset after the response
        // of the request has been received.
        this.requestFormAvailable = false;

		var requestForm = this.requestFrame.contentWindow.document.getElementById("requestForm");
		requestForm.action = this._getRequestURL(this.sessionId);

		var formInputElement = this.requestFrame.contentWindow.document.getElementById("requestData");
		formInputElement.value = queryString;

		requestForm.submit();
	}	 	
	
	this._getIdentifier = function(dataName) {
		if (dataName != null) {
			var splitIndex = dataName.indexOf(".");
			if (splitIndex > -1)
				return dataName.substring(0, splitIndex);
			else
				return dataName;				
		}
		
		return null;
	}
	
	this._getFieldName = function(dataName) {
		if (dataName != null) {
			var splitIndex = dataName.indexOf(".");
			if (splitIndex > -1)
				return dataName.substring(splitIndex + 1);
			else
				return null;
		}
		
		return null;
	}

	this._subscribeField = function(dataName) {
        if (this.subscribedFields[dataName] != null) {
            this.subscribedFields[dataName]++;
            return "";
        }

        this.subscribedFields[dataName] = 1;
		if (!this._isTriggeredField(this._getFieldName(dataName))) {
			return this._getFieldRequestAsString(dataName);
		} else {
			return "";
		}		
	}
	
	this._unsubscribeField = function(dataName) {
        if (this.subscribedFields[dataName] != null) {
            this.subscribedFields[dataName]--;

            if (this.subscribedFields[dataName] < 1) {
                delete this.subscribedFields[dataName];
                this._removeCachedValue(dataName);

                if (!this._isTriggeredField(this._getFieldName(dataName)))
                    return this.__getFieldReleaseAsString(dataName);
            }
        }

        return "";
	}
	
	this._subscribeToRequiredTriggerSubscriptions = function(dataName) {
		var result = "";
        var currentTrigger = this.triggers[this._getFieldName(dataName)];
        if (currentTrigger != null) {
            var triggerFields = currentTrigger.getFields();
            for (var triggerFieldIndex in triggerFields) {
                var triggerField = this._getIdentifier(dataName) + "." + triggerFields[triggerFieldIndex];
                result += this._subscribeField(triggerField);
            }
        }

		return result;
	}
	
	this._unsubscribeFromRequiredTriggerSubscriptions = function(dataName) {
		var result = "";
        var currentTrigger = this.triggers[this._getFieldName(dataName)];
        if (currentTrigger != null) {
            var triggerFields = currentTrigger.getFields();
            for (var triggerFieldIndex in triggerFields) {
                var triggerField = this._getIdentifier(dataName) + "." + triggerFields[triggerFieldIndex];
                result += this._unsubscribeField(triggerField);
            }
        }

		return result;
	}	
	
	this._isTriggeredField = function(fieldIdentifier) {
        return this.triggers[fieldIdentifier] != null;
	}
	
	this._getFieldParser = function(fieldIdentifier) {
        return this.parsers[fieldIdentifier];
	}
	
	this._reconnect = function() {		
        this._log(vwdStreamerLogLevel.INFO, "Performing reconnect...");
        this.heartbeatReceived(); // Trigger heartbeat received function to cross the gap when reconnecting to the server
		this.connect(this.sessionId);
        this.reconnected = true;
	}

    this._checkHeartbeatAndReconnect = function() {
        var currentTime = new Date();
        if (currentTime - this.lastConnectTime > this.reconnectInterval) {
            this._log(vwdStreamerLogLevel.INFO, "Reconnect interval passed, performing auto reconnect...");
            this._reconnect();
            return; // Don't check the heartbeat when reconnecting to make sure we don't get a never ending reconnect
        }
        if (currentTime - this.lastDataReceived > this.heartbeatInterval * 3) {
            this.connected = false;
            this._log(vwdStreamerLogLevel.INFO, "No data received for " + ((currentTime - this.lastDataReceived) / 1000) + " seconds, reconnecting...");
            this._reconnect();
            return;
        }

        // Update the connected status
        this.connected = this.lastDataReceived != null;
    }

	this._getResponseURL = function(sessionId) {
		var requestURL = "";
		if (this.host != null) { requestURL += "http://" + this.host; }
		if (this.port != null) { requestURL += ":" + this.port; }
		requestURL += "/STREAMING/";
		if (sessionId != null) { requestURL += escape(sessionId); }
		if (this.domain != null) { requestURL += "?domain=" + escape(this.domain);  }

		return requestURL;
	}
	
	this._getRequestURL = function(sessionId) {
		var requestURL = "";
		if (this.host != null) { requestURL += "http://" + this.host; }
		if (this.port != null) { requestURL += ":" + this.port; }
		requestURL += "/STREAMING/";
		if (sessionId != null) { requestURL += escape(sessionId); }
        if (this.domain != null) { requestURL += "?domain=" + escape(this.domain);  }
		
		return requestURL;
	}	
	
	this._getRequestFormURL = function() {
		var requestFormURL = "";
		if (this.host != null) { requestFormURL += "http://" + this.host; }
		if (this.port != null) { requestFormURL += ":" + this.port; }
		requestFormURL += "/STREAMING/streamerResponseForm.html";
		if (this.domain != null) { requestFormURL += "?domain=" + escape(this.domain);  }		
		
		return requestFormURL;
	}

    this._getFieldRequestAsString = function(field) {
        return "req(" + field + ");";
    }

    this._getFieldReleaseAsString = function(field) {
        return "rel(" + field + ");";
    }
};
/*
 * 	vwd Streamer, CopyRight(c) 2009 vwd group
 * 
 *  Author: Sander Moelands, Ruben Prins
 */

var vwdStreamerTick = function(tickType, dateTime, price, indication, lastVolume, cummulativeVolume) { 
	this.tickType = tickType;
	this.dateTime = dateTime;
	this.price = price;
	this.indication = indication;
	this.lastVolume = lastVolume;
	this.cummulativeVolume = cummulativeVolume;
};

var TExParser = function() {
	this.fieldName = "T!";
	
	this.getFieldName = function() { return this.fieldName; }
	this.getParsedValues = function(rawValue) {
		var result = new Object();
		
		if (rawValue != null && rawValue.length > 0) {
			var splitValues = rawValue.split("|");

			result.values = new vwdStreamerTick(splitValues[0]!=""?splitValues[0].substr(0,1):null,
					splitValues[0]!=""?splitValues[0].substr(1):null,
					splitValues[1]!=""?Number(splitValues[1]):null,
					splitValues[2]!=""?splitValues[2]:null,
					splitValues[3]!=""?Number(splitValues[3]):null,
					splitValues[4]!=""?Number(splitValues[4]):null);
			
			result.value = result.values.price;
			result.type = vwdStreamerDataType.NUMBER;
		}

		return result;
	}	
};

var ZExParser = function() {
	this.fieldName = "Z!";
	
	this.getFieldName = function() { return this.fieldName; }
	this.getParsedValues = function(rawValue) {
		var result = new Object();
		
		if (rawValue != null && rawValue.length > 0) {
			var splitValues = rawValue.split("|");

			result.values = new vwdStreamerTick(splitValues[0]!=""?splitValues[0].substr(0,1):null,
					splitValues[0]!=""?splitValues[0].substr(1):null,
					splitValues[1]!=""?Number(splitValues[1]):null,
					splitValues[2]!=""?splitValues[2]:null,
					splitValues[3]!=""?Number(splitValues[3]):null,
					splitValues[4]!=""?Number(splitValues[4]):null);
			
			result.value = result.values.price;
			result.type = vwdStreamerDataType.NUMBER;
		}

		return result;
	}	
};

var BlockExParser = function() {
	this.fieldName = "Block!";
	
	this.getFieldName = function() { return this.fieldName; }
	this.getParsedValues = function(rawValue) {
		var result = new Object();
		
		if (rawValue != null && rawValue.length > 0) {
			var splitValues = rawValue.split("|");

			result.values = new vwdStreamerTick(splitValues[0]!=""?splitValues[0].substr(0,1):null,
					splitValues[0]!=""?splitValues[0].substr(1):null,
					splitValues[1]!=""?Number(splitValues[1]):null,
					null,
					splitValues[3]!=""?Number(splitValues[3]):null,
					splitValues[4]!=""?Number(splitValues[4]):null);

			result.value = result.values.price;
			result.type = vwdStreamerDataType.NUMBER;
		}

		return result;
	}	
};/*
 * 	vwd Streamer, CopyRight(c) 2009 vwd group
 * 
 *  Author: Sander Moelands
 */

function vwdStreamerQueue() {
    this.elements = new Array();

    this.enqueue = function(newElement) {
        this.elements.push(newElement);
    }

    this.dequeue = function() {
        return this.elements.shift()();
    }

    this.flush = function() {
        while (!this.isEmpty()) {
            this.dequeue();
        }
    }

    this.isEmpty = function() {
        return this.elements.length == 0;
    }
};
/*
 * 	vwd Streamer, CopyRight(c) 2009 vwd group
 * 
 *  Author: Sander Moelands, Ruben Prins
 */

var vwdStreamerSubscription = function (streamerInstance, postbackMethod) {
	this.streamerInstance = streamerInstance;
	this.postbackMethod = postbackMethod;
	this.subscribedFields = [];
	
	this.subscribe = function(fields) {
		var duplicateSubscriptions = [];
		for (var fieldIndex in fields) {
			if (!this._isSubscribedToField(fields[fieldIndex])) {
                this.subscribedFields[fields[fieldIndex]] = true;
			} else {
				duplicateSubscriptions[duplicateSubscriptions.length] = fieldIndex;
			}
			
			var cachedValue = this.streamerInstance._getCachedValue(fields[fieldIndex]);			
			if (cachedValue != null) {
				this.streamerInstance._update(cachedValue.dataName, cachedValue.value, cachedValue.dataType, this);
			}
		}
				
		for (var i = duplicateSubscriptions.length - 1; i > 0; i--) {
			fields.splice(duplicateSubscriptions[i], 1);
		}
		
		this.streamerInstance._subscribe(fields);
	}
	
	this.unsubscribe = function(fields) {
		var invalidFields = [];
		for (var fieldIndex in fields) {
			if (!this._isSubscribedToField(fields[fieldIndex])) {
				invalidFields[invalidFields.length] = fieldIndex;
			} else {
				this._removeFieldSubscription(fields[fieldIndex]);
			}
		}
				
		for (var i = invalidFields.length - 1; i > 0; i--) {
			fields.splice(invalidFields[i], 1);
		}
		
		this.streamerInstance._unsubscribe(fields);				
	}

    this.unsubscribeAllFields = function() {
        for (var fieldIndex in this.subscribedFields) {
            this.streamerInstance._unsubscribe(this.subscribedFields[fieldIndex]);
        }

        this.subscribedFields = [];
    }

	this.update = function(updatePacket) {
		if (this._isSubscribedToField(updatePacket.dataName)) {
			this.postbackMethod(updatePacket);
		}
	}
	
	this._isSubscribedToField = function(fieldName) {
        return this.subscribedFields[fieldName];
	}
	
	this._removeFieldSubscription = function(fieldName) {
        delete this.subscribedFields[fieldName];
    }
};
/*
 * 	vwd Streamer, CopyRight(c) 2009 vwd group
 * 
 *  Author: Sander Moelands, Ruben Prins
 */

var AbsoluteDifferenceTrigger = function(streamerInstance) {
	this.streamerInstance = streamerInstance;
	this.fieldName = "AbsoluteDifference";
	this.fields = new Array("LastDate", "PreviousCloseDate", "LastPrice", "ClosePrice", "PreviousClosePrice");
	
	this.getFieldName = function() { return this.fieldName; }
	this.getFields = function() { return this.fields; }	
	this.fire = function(identifier) {
		var previousAbsoluteDifference = this.streamerInstance._getCachedValue(identifier + "." + this.fieldName);
		var closePrice = this.streamerInstance._getCachedValue(identifier + ".ClosePrice");
		var lastDate = this.streamerInstance._getCachedValue(identifier + ".LastDate");
		var previousClosePrice = this.streamerInstance._getCachedValue(identifier + ".PreviousClosePrice");
		var previousCloseDate = this.streamerInstance._getCachedValue(identifier + ".PreviousCloseDate");

		if (closePrice == null) { closePrice = this.streamerInstance._getCachedValue(identifier + ".LastPrice"); }
		if ((closePrice == null) || (closePrice.value == 0) || (previousClosePrice == null) || (previousClosePrice.value == 0)) {
			if (previousAbsoluteDifference != null)
				this.streamerInstance._update(identifier + "." + this.fieldName, null, vwdStreamerDataType.UNKNOWN);
			return; 
		}
		if ((lastDate != null) && (previousCloseDate != null) && (lastDate.value == previousCloseDate.value)) { 
			if (previousAbsoluteDifference != null)
				this.streamerInstance._update(identifier + "." + this.fieldName, null, vwdStreamerDataType.UNKNOWN);
			return; 
		}
		
		var value = Number(closePrice.value) - Number(previousClosePrice.value);
		this.streamerInstance._update(identifier + "." + this.fieldName, value, vwdStreamerDataType.NUMBER);
	}
};

var RelativeDifferenceTrigger = function(streamerInstance) {
	this.streamerInstance = streamerInstance;
	this.fieldName = "RelativeDifference";
	this.fields = new Array("LastDate", "PreviousCloseDate", "LastPrice", "ClosePrice", "PreviousClosePrice");
	
	this.getFieldName = function() { return this.fieldName; }
	this.getFields = function() { return this.fields; }	
	this.fire = function(identifier) {
		var previousRelativeDifference = this.streamerInstance._getCachedValue(identifier + "." + this.fieldName);
		var closePrice = this.streamerInstance._getCachedValue(identifier + ".ClosePrice");
		var lastDate = this.streamerInstance._getCachedValue(identifier + ".LastDate");
		var previousClosePrice = this.streamerInstance._getCachedValue(identifier + ".PreviousClosePrice");
		var previousCloseDate = this.streamerInstance._getCachedValue(identifier + ".PreviousCloseDate");

		if (closePrice == null) { closePrice = this.streamerInstance._getCachedValue(identifier + ".LastPrice"); }
		if ((closePrice == null) || (closePrice.value == 0) || (previousClosePrice == null) || (previousClosePrice.value == 0)) {
			if (previousRelativeDifference != null)
				this.streamerInstance._update(identifier + "." + this.fieldName, null, vwdStreamerDataType.UNKNOWN);
			return; 
		}
		if ((lastDate != null) && (previousCloseDate != null) && (lastDate.value == previousCloseDate.value)) { 
			if (previousRelativeDifference != null)
				this.streamerInstance._update(identifier + "." + this.fieldName, null, vwdStreamerDataType.UNKNOWN);
			return; 
		}
		
		var value = (Number(closePrice.value) - Number(previousClosePrice.value)) / Number(previousClosePrice.value);
		this.streamerInstance._update(identifier + "." + this.fieldName, value, vwdStreamerDataType.NUMBER);
	}
};/*
 * 	vwd Streamer, CopyRight(c) 2009 vwd group
 * 
 *  Author: Sander Moelands, Ruben Prins
 *
 */

var vwdStreamerLogLevel = {
	TRACE: 0,
	INFO: 1,
	WARNING: 2,
	ERROR: 3
};

var vwdStreamerDataType = {
	UNKNOWN: 0,
	NUMBER: 1,
	STRING: 2,
	DATE: 3,
	TIME: 4,
	DATETIME: 5
};

vwdStreamerDataType.regTimeString = /^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/;
vwdStreamerDataType.regDateString = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[01])$/;
vwdStreamerDataType.regDateTimeString = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[01]) ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/;

vwdStreamerDataType.parseDataType = function(dataType, value) {		
	if (dataType == "NUMBER") {
		return vwdStreamerDataType.NUMBER;
	}
	if (dataType == "DATE") {
		if (vwdStreamerDataType.regDateString.test(value))
			return vwdStreamerDataType.DATE;
		else
			return vwdStreamerDataType.STRING;
	}
	if (dataType == "TIME") {
		if (vwdStreamerDataType.regTimeString.test(value))				
			return vwdStreamerDataType.TIME;
		else
			return vwdStreamerDataType.STRING;
	}
	if (dataType == "DATETIME") { 
		if (vwdStreamerDataType.regDateTimeString.test(value))				
			return vwdStreamerDataType.DATETIME;
		else
			return vwdStreamerDataType.STRING;
	}
	if (dataType == "STRING") {
		if (vwdStreamerDataType.regTimeString.test(value))				
			return vwdStreamerDataType.TIME;
		else if (vwdStreamerDataType.regDateString.test(value))
			return vwdStreamerDataType.DATE;
		else if (vwdStreamerDataType.regDateTimeString.test(value))
			return vwdStreamerDataType.DATETIME;
		else
			return vwdStreamerDataType.STRING;
	}
	
	return vwdStreamerDataType.UNKNOWN;
}

Date.parseVwdStreamerTime = function(value) {
	var result = new Date();
	result.setHours(value.substring(0,2));
	result.setMinutes(value.substring(3,5));
	result.setSeconds(value.substring(6,8));
	return result;
}

Date.parseVwdStreamerDate = function(value) {
	var result = new Date();
	result.setFullYear(value.substring(0,4));
	result.setMonth(Number(value.substring(5,7)) - 1);
	result.setDate(value.substring(8,10));
	return result;
}

Date.parseVwdStreamerDateTime = function(value) {
	var result = new Date();
	result.setFullYear(value.substring(0,4));
	result.setMonth(Number(value.substring(5,7)) - 1);
	result.setDate(value.substring(8,10));
	result.setHours(value.substring(11,13));
	result.setMinutes(value.substring(14,16));
	result.setSeconds(value.substring(17,19));	
	return result;
}

if(document.all && !document.getElementById) { 
	document.getElementById = function(id) { 
		return document.all[id]; 
	}
}