this.backpropagate = function(oAnn, aTargetOutput, nLearningRate) {

nLearningRate = nLearningRate || 1;

var oNode,

n = 0;

for (sNodeId in oAnn.getOutputGroup().getNodes()) {

oNode = oAnn.getOutputGroup().getNodes()[sNodeId];

oNode.setError(aTargetOutput[n] - oNode.getOutputValue());

n ++;

}

for (sNodeId in oAnn.getInputGroup().getNodes()) {

this.backpropagateNode(oAnn.getInputGroup().getNodes()[sNodeId], nLearningRate);

}

}

this.backpropagateNode = function(oNode, nLearningRate) {

var nError = oNode.getError(),

oOutputNodes,

oConn,

nWeight,

nOutputError,

nDerivative = oNode.getOutputValue() * (1 - oNode.getOutputValue()), // Derivative for sigmoid activation funciton

nInputValue = oNode.getInputValue(),

n;

if (nError === null /* Dont do the same node twice */ && oNode.hasOutputs()) {

nDerivative = nDerivative || 0.000000000000001;

nInputValue = nInputValue || 0.000000000000001;

oOutputNodes = oNode.getOutputNodes();

for (n=0; n<oOutputNodes.length; n++) {

nOutputError = this.backpropagateNode(oOutputNodes[n], nLearningRate);

oConn = oAnn.getConnection(oNode, oOutputNodes[n]);

nWeight = oConn.getWeight();

oConn.setWeight(nWeight + nLearningRate * nOutputError * nDerivative * nInputValue);

nError += nOutputError * nWeight;

}

oNode.setError(nError);

}

return oNode.getError();

}