﻿var GROUP_MEMBERSHIP = "Group Membership";
        //  <DefinitionRules name="Basic Application">
        //    <operator name="AND">
        //      <attribute DefinitionAttributeID="c4c7ef37-761c-4add-8019-bc244001c9d4">
        //        <criteria operator="AdHoc">
        //          <values>
        //            <value type="System.DateTime">6/23/2006 12:00:00 AM</value>
        //            <value type="System.DateTime">6/23/2006 11:59:59 PM</value>
        //          </values>
        //        </criteria>
        //      </attribute>
        //      <group GroupID="c4c7ef37-761c-4add-8019-bc244001c9d4">
        //      </group>
        //    </operator>
        //  </DefinitionRules>
        //  <DefinitionRuleActions>
        //    <DefinitionRuleAction propertyName='Enabled' propertyValue='true' />
        //  </DefinitionRuleActions>
        //</DefinitionRuleSet>


function RuleSet()
{
    this.ruleLogicalNode = null;
    this.ruleActions = new Array();
    this.activeRuleAction=null;

}
RuleSet.prototype.evaluate=function(definitionDisplayLayoutControl,processErrors)
{
    var result=this.ruleLogicalNode.evaluate(definitionDisplayLayoutControl);
    
    definitionDisplayLayoutControl.formRuleLogger.log('Rule set evaluated to ' + result);

    for(var index=0;index<this.ruleActions.length;index++)
    {
        definitionDisplayLayoutControl.formRuleLogger.log('Running rule action ' + index);
        this.ruleActions[index].executeRuleAction(definitionDisplayLayoutControl,!result,processErrors);
    }
}
RuleSet.prototype.validate = function(formView)
{
    formView.htmlPageErrors.length=0;
    if(this.ruleActions.length==0)
        formView.addHtmlError("Please apply atleast one action.")
    else
    {
        for(var index=0;index<this.ruleActions.length;index++)
            this.ruleActions[index].validate(formView);
    }
    if(this.ruleLogicalNode == null )
        formView.addHtmlError("Please specify a rule.");
    this.ruleLogicalNode.validate(formView);
}
RuleSet.prototype.buildFromXml = function(rulesetXml, evaluateRules)
{
    var xmlDoc = GetXmlParser();
    LoadXml(xmlDoc,unescape(rulesetXml));
    this.buildFromXmlNode(xmlDoc,evaluateRules);
}
RuleSet.prototype.buildFromXmlNode = function(xmlDoc)
{
    var ruleNodes = xmlDoc.getElementsByTagName("DefinitionRules");
    if(ruleNodes.length>0)
    {
       var ruleNode = ruleNodes[0];
       var logicalNode = ruleNode.childNodes[0];
       this.ruleLogicalNode = new RuleLogicalNode(GetAttributeValue(logicalNode, 'name'),this);
       this.ruleLogicalNode.buildFromXml(logicalNode);
    }
    var actionNodes = xmlDoc.getElementsByTagName("DefinitionRuleAction");
    
    for(var index=0;index<actionNodes.length;index++)
    {
        this.ruleActions.push(new RuleAction(GetAttributeValue(actionNodes[index], 'propertyName'),GetAttributeValue(actionNodes[index], 'propertyValue'),unescape(GetAttributeValue(actionNodes[index], 'propertyTarget')) ));
    }
}
RuleSet.prototype.buildRuleString = function()
{
    var ruleString = "If <br> &nbsp;&nbsp;"
    ruleString += this.ruleLogicalNode.buildRuleString();
    ruleString += "<br>"
    ruleString += "Then "
    for(var index=0;index<this.ruleActions.length;index++)
    {
        ruleString += "<li>" + this.ruleActions[index].buildRuleString() + "</li>";
    }
    + ""
    return ruleString;
}
RuleSet.prototype.wireEventHandlers = function(definitionDisplayLayoutControl)
{
    this.ruleLogicalNode.wireEventHandler(definitionDisplayLayoutControl);
}
RuleSet.prototype.buildXml = function()
{
    var markup = "<DefinitionRuleSet>";
    markup += "<DefinitionRules>"
    if(this.ruleLogicalNode)
    markup += this.ruleLogicalNode.buildXml();
    markup += "</DefinitionRules>";
    markup += "<DefinitionRuleActions>";
    for(var index=0;index<this.ruleActions.length;index++)
        markup += this.ruleActions[index].buildXml();
    markup += "</DefinitionRuleActions>";
    markup+= "</DefinitionRuleSet>";
    return markup;
}

function ParenthesesNode(parent)
{
    this.parent=parent;
    this.children=new Array();
    this.isParenthesesNode=true;
}
ParenthesesNode.prototype.swapConditionalNodes = function()
{
    if(this.children.length==1 && this.children[0].isConditionalNode)
    {
        var myIndex= GetIndex(this.parent.children,this);
        this.parent.children[myIndex] = this.children[0];
        var me = this;
        me = null;
    }
    else
    {
        for(var index=0;index<this.children.length;index++)
        {
            if(this.children[index].isParenthesesNode)
                this.children[index].swapConditionalNodes();
        }
    }
}
ParenthesesNode.prototype.swapLogicalNodes = function()
{
    var newParent = null;
    for(var index=0;index<this.children.length;index++)
    {
        if(this.children[index].isLogicalNode)
        {
            if(!newParent )
                newParent = this.children.splice(index,1)[0];
            else
            {
                if(this.children[index].operator != newParent.operator)
                    alert("Warning : You have more than 1 logical operators (AND/OR) at the same level. Only the first logical opeartor has been used. Please enclose your expressions in parentheses appropriately. For example, (X AND Y OR Z) cannot be deterministically evaluated, but ((X AND Y) OR Z) can.")
                this.children.splice(index,1);
            }
        }
    }
    if(!newParent)
    newParent = new RuleLogicalNode("AND",this);
    if(this.parent && this.parent.children)
    {
        var myIndex= GetIndex(this.parent.children,this);
        this.parent.children[myIndex] = newParent;
    }

    for(var index=0;index<this.children.length;index++)
    if(this.children[index].isParenthesesNode)
            newParent.logicalNodes.push(this.children[index].swapLogicalNodes());

    for(var index=0;index<this.children.length;index++)
        if(this.children[index].isConditionalNode)
            newParent.conditionalNodes.push(this.children[index]);
    return newParent;
}
function RuleLogicalNode(operator,parent)
{
    this.parent = parent;
    this.operator = operator;
    this.conditionalNodes = new Array();
    this.logicalNodes = new Array();
    this.isLogicalNode = true;
}
RuleLogicalNode.prototype.validate=function(formView)
{
    var nodes = this.logicalNodes.concat(this.conditionalNodes);
    for(var index=0;index<nodes.length;index++)
        nodes[index].validate(formView);
    if(this.conditionalNodes.concat(this.logicalNodes).length==0)
        formView.addHtmlError("Please enter a rule.");
}
RuleLogicalNode.prototype.buildFromXml=function(parentNode)
{
    for(var nodeIndex=0;nodeIndex<parentNode.childNodes.length;nodeIndex++)
    {   
        var xmlNode = parentNode.childNodes[nodeIndex];
        if(xmlNode.tagName == "attribute") //conditional node
        {
            var fieldName = GetAttributeValue(xmlNode, 'name');
            var criteriaNodes = xmlNode.getElementsByTagName("criteria")
            var ruleOperator = unescape( GetAttributeValue(criteriaNodes[0], 'operator') );
            var subField = GetAttributeValue(criteriaNodes[0], 'field');
            
            var valueNodes = xmlNode.getElementsByTagName("value");
            var values = new Array();
            for(var index=0;index<valueNodes.length;index++)
                values.push(GetInnerText(valueNodes[index]));
            
            var conditionalLogicalNode = new RuleConditionalNode(fieldName,ruleOperator,values,subField);
            this.conditionalNodes.push(conditionalLogicalNode);
        }
        else if(xmlNode.tagName == "operator")
        {
            var ruleLogicalNode = new RuleLogicalNode(GetAttributeValue(xmlNode, 'name'),this);
            this.logicalNodes.push(ruleLogicalNode);
            ruleLogicalNode.buildFromXml(xmlNode);
        }
    }
}
RuleLogicalNode.prototype.buildRuleString = function()
{
    var nodes = this.logicalNodes.concat(this.conditionalNodes);
    var ruleString = " (";
    for(var index=0;index<nodes.length;index++)
    {
        ruleString += nodes[index].buildRuleString();
        if(index!=(nodes.length-1))
            ruleString += " " + this.operator +" ";
    }
    ruleString += ") ";
    return ruleString;
}
RuleLogicalNode.prototype.wireEventHandler = function(definitionDisplayLayoutControl)
{
    for(var index=0;index<this.conditionalNodes.length;index++)
    {
        this.conditionalNodes[index].wireEventHandler(definitionDisplayLayoutControl);
    }
}

RuleLogicalNode.prototype.buildXml = function()
{
    var markup="<operator name='"+this.operator+"'>";
    var nodes = this.logicalNodes.concat(this.conditionalNodes);
    for(var index=0;index<nodes.length;index++)
        markup+=nodes[index].buildXml();
    markup += "</operator>";
    return markup;
}
RuleLogicalNode.prototype.evaluate = function(definitionDisplayLayoutControl)
{
    var nodes = this.logicalNodes.concat(this.conditionalNodes);
    if(this.operator == 'AND')
    {
        for(var index=0;index<nodes.length;index++)
            if(!nodes[index].evaluate(definitionDisplayLayoutControl))
                return false;
        return true;
    }
    else if (this.operator == "OR")
    {
        for(var index=0;index<nodes.length;index++)
            if(nodes[index].evaluate(definitionDisplayLayoutControl))
                return true;
        return false;
    }
}
function RuleConditionalNode(fieldName, operator, values, subField)
{
    this.isConditionalNode = true;
    this.fieldName = fieldName;
    this.subField = (subField?subField:"");
    this.ruleOperator =  new RuleOperator(operator, values);
}
RuleConditionalNode.prototype.buildRuleString = function()
{
  var htmlDocument;
    if(window.opener.currentHtmlDocument)
        htmlDocument = window.opener.currentHtmlDocument;
    else
        htmlDocument = currentHtmlDocument;
   
    var ruleString="";
    ruleString += "[" + this.fieldName + ((this.subField!="") ? "."+ this.subField : "") + "]";
    ruleString += " " + this.ruleOperator.operator + " \"";
    var isList = false;
    
    var listID=currentDefinitions[0].getFieldbyName(this.fieldName).listID;
    if(listID && listID.length>0  && htmlDocument.loadedLists[listID])
    {
        isList = true;
    }
    
    for(var index=0;index<this.ruleOperator.values.length;index++)
    {
        if (isList) 
        {
            var listMember = htmlDocument.loadedLists[listID].findListMember(this.ruleOperator.values[index]);
            if (listMember) 
            {
                ruleString += htmlDocument.loadedLists[listID].findListMember(this.ruleOperator.values[index]).text;
            }
            else 
            {
                ruleString += this.ruleOperator.values[index].replace(",","\\,");
            }
        }
        else
        {
            ruleString += this.ruleOperator.values[index].replace(",","\\,");
        }
        if(index!=(this.ruleOperator.values.length-1))
            ruleString += ", ";
    }
    ruleString += "\""
    return ruleString;
}
RuleConditionalNode.prototype.buildXml = function()
{
    var  markup = "<attribute name='"+this.fieldName+"'>"
    markup += "<criteria operator='"+escape(this.ruleOperator.operator)+"' "+ ((this.subField!="") ? "field='"+ this.subField  + "'" : "") + ">"
    markup += "<values>"
    for(var index=0;index<this.ruleOperator.values.length;index++)
        markup += "<value>"+this.ruleOperator.values[index]+"</value>"
    markup += "</values>"
    markup += "</criteria>"
    markup += "</attribute>"
    return markup;
}
RuleConditionalNode.prototype.validate=function(formView)
{
    if(this.fieldName!=GROUP_MEMBERSHIP && formView.getControlCountByName(this.fieldName,true)!=1)
        formView.addHtmlError("Form rule references field '" + this.fieldName + "' that does not exist on the form.");
}

RuleConditionalNode.prototype.wireEventHandler = function(definitionDisplayLayoutControl)
{
    if(this.fieldName !=  GROUP_MEMBERSHIP )
    {
        var field = definitionDisplayLayoutControl.getField(this.fieldName);        
        if(field != null)
            definitionDisplayLayoutControl.getField(this.fieldName).wireEventHandler(this.subField);
    }
}
RuleConditionalNode.prototype.evaluate = function(definitionDisplayLayoutControl)
{
    var fieldValue = "";
    var result = null;
    
    if(this.fieldName == GROUP_MEMBERSHIP) 
        fieldValue = definitionDisplayLayoutControl.groups;
    else
        fieldValue = definitionDisplayLayoutControl.getField(this.fieldName).getFieldValue(this.subField);
        
    result = this.ruleOperator.evaluate(fieldValue);
    
    definitionDisplayLayoutControl.formRuleLogger.log('Result ' + result + ' for field ' + this.fieldName + ' value ' + fieldValue + ' ' + this.ruleOperator.operator + ' ' +  this.ruleOperator.values );

    return result;
}

function RuleOperator(operator, values)
{
    this.operator = operator.toUpperCase();
    this.values = new Array();
    for(var index=0;index<values.length;index++)
        this.values.push(values[index])
    
}

RuleOperator.prototype.evaluate = function(obj)
{
    //checkbox with multiples -  just do in for equals
    var operator = this.operator;
    if(operator =="MEMBER OF" )operator="IN";
    if(operator =="NOT MEMBER OF" )operator="NOT IN";
    if(obj)
    {
        if(obj.push && operator == "=")operator="IN";
        if(!obj.push && (operator == "NOT IN" || operator=="IN"))
        {
            var newObj = new Array();
            newObj.push(obj);
            obj = newObj;
        }
    }
    
    if(operator == "=")
        return (obj == this.values[0])
    else if(operator == "<>" || operator == "!=")
        return (obj != this.values[0])
    else if(operator == "<" || operator == "LESS THAN")
        return (obj < this.values[0])
    else if(operator == ">" || operator == "GREATER THAN")
        return (obj > this.values[0])
    else if(operator == "<=" || operator == "LESS THAN EQUALS")
        return (obj <= this.values[0])
    else if(operator == ">=" || operator == "GREATER THAN EQUALS")
        return (obj >= this.values[0])
    else if(operator == "LIKE")
        return (obj.toLowerCase().substring(this.values[0].toLowerCase())==0)
    else if(operator == "IN")
    {
        if(obj && obj.push)
        {
            for(var index=0;index<this.values.length;index++)
                for(var index1=0;index1<obj.length;index1++)
                    if(Trim(this.values[index])==Trim(obj[index1].toString()))
                        return true;
        }
        return false;
    }
    else if(operator == "NOT IN")
    {
        if(obj && obj.push)
        {
            for(var index=0;index<this.values.length;index++)
                for(var index1=0;index1<obj.length;index1++)
                    if(Trim(this.values[index])==Trim(obj[index1].toString()))
                        return false;
        }
        return true;
    }
    else if(operator == "BETWEEN")
        return (this.values[0] <= obj) && (obj <= this.values[1]);
    else
    {
        return new DateSpan(operator).evaluate(obj,this.values)
    }
}
/*

*/
function RuleAction(propertyName, propertyValue, propertyTarget)
{
    this.propertyName = propertyName;
    this.propertyValue = propertyValue;
    this.propertyTarget = propertyTarget;
}
RuleAction.prototype.buildRuleString = function()
{
    return "Set '" + this.propertyName + "' of " + this.propertyTarget + " to '" + this.propertyValue + "'";
}
RuleAction.prototype.validate = function(formView)
{
    if(!formView.getControlCountByName(this.propertyTarget,true))
        formView.addHtmlError("Invalid target name '"+this.propertyTarget+"' referenced in action : '" + this.buildRuleString() +"'");
}

RuleAction.prototype.buildXml = function()
{
    return "<DefinitionRuleAction propertyName='"+this.propertyName+"' propertyTarget='"+escape(this.propertyTarget)+"' propertyValue='"+this.propertyValue+"' />";
}


RuleAction.prototype.executeRuleAction = function(definitionDisplayLayoutControl, reverseAction, processError)
{            
    var targetDiv = document.getElementById(definitionDisplayLayoutControl.getField(this.propertyTarget).divId);
    
    definitionDisplayLayoutControl.formRuleLogger.log('Rule action for ' + this.propertyTarget + ' with DOM id ' + targetDiv.id + ' property ' + this.propertyName + ' value ' + !reverseAction);

    if (this.propertyName == "Visible")
    {

        var visibility =reverseAction ? this.propertyValue!="true" : this.propertyValue=="true";            
        targetDiv.style.display = visibility ? "block" : "none";
        definitionDisplayLayoutControl.formRuleLogger.log(this.propertyName + ' Display=' + targetDiv.style.display);
    }
    
    else if (this.propertyName == "Required")
    {
        targetDiv.required = reverseAction ? !(this.propertyValue=="true") : (this.propertyValue=="true");
        definitionDisplayLayoutControl.formRuleLogger.log(this.propertyName + ' Required=' + targetDiv.required);

    }    
    else if (this.propertyName == "Enabled")
    {
        EnableDisableRecursive(targetDiv, reverseAction ? (this.propertyValue=="true") : !(this.propertyValue=="true")) ;
        definitionDisplayLayoutControl.formRuleLogger.log(this.propertyName + ' Enabled=' + (reverseAction ? (this.propertyValue=="true") : !(this.propertyValue=="true")));

    }    
    else if (this.propertyName  == "Error" && processError && !reverseAction)
    {
        this.errorDiv.innerHTML += this.propertyValue;
    }    
    else if (this.propertyName  == "CssClass")
    {
        if(!targetDiv.origClassName)targetDiv.origClassName = targetDiv.className;
        targetDiv.className = (reverseAction ? targetDiv.origClassName : this.propertyValue);
        definitionDisplayLayoutControl.formRuleLogger.log(this.propertyName + ' Class Name=' + targetDiv.className);

    }
    

}
function PropertyNameChanged(actionDropdown)
{
    var propertyValueCell = document.getElementById("propertyValueCell");
    var propertyName = actionDropdown.options[actionDropdown.selectedIndex].value;
    if(propertyName == "Error" || propertyName == "CssClass" )
       propertyValueCell.innerHTML = "<input type='text'>";
    else
       propertyValueCell.innerHTML = "<input type='checkbox' checked>";
}


function DateSpan(dateSpan)
{
    this.dateSpan = dateSpan.toUpperCase();
}
DateSpan.prototype.evaluate = function(objDate,values)
{
    var dateToEvaluate = new Date(objDate);
    var now = new Date();
    if(this.dateSpan == "TODAY")
        return this.isSameDay(dateToEvaluate,now)
    else if(this.dateSpan == "SINGLEDAY")
        return this.isSameDay(dateToEvaluate,new Date(values[0]))
    else if(this.dateSpan == "ADHOC")
        return (new Date(values[0]) <= dateToEvaluate <= new Date(values[1]))
    else if(this.dateSpan == "YESTERDAY")
        return this.isSameDay(dateToEvaluate,new Date(now.getYear(),now.getMonth(),now.getDay()-1));
    else if(this.dateSpan == "LASTMONTH")
        return this.isSameMonth(dateToEvaluate, new Date(now.getYear(),now.getMonth()-1,1));
//    else if(this.dateSpan == "LASTSEVENDAYS")
//        return  new Date(now.getYear(),now.getMonth(),now.getDay()-7,0,0,0) <= dateToEvaluate <= now;
//    else if(this.dateSpan == "TOMORROW")
//        return this.isSameDay(dateToEvaluate,new Date(now.getYear(),now.getMonth(),now.getDay()+1));
//    else if(this.dateSpan == "NEXTMONTH")
//        return this.isSameMonth(dateToEvaluate, new Date(now.getYear(),now.getMonth()+1,1));
//    else if(this.dateSpan == "NEXTSEVENDAYS")
//        return  new Date(now.getYear(),now.getMonth(),now.getDay()+7,0,0,0) <= dateToEvaluate <= now;
//    else if(this.dateSpan == "NEXT30DAYS")
//        return  new Date(now.getYear(),now.getMonth(),now.getDay()+30,0,0,0) <= dateToEvaluate <= now;
//    else if(this.dateSpan == "NEXT60DAYS")
//        return  new Date(now.getYear(),now.getMonth(),now.getDay()+60,0,0,0) <= dateToEvaluate <= now;
//    else if(this.dateSpan == "NEXT90DAYS")
//        return  new Date(now.getYear(),now.getMonth(),now.getDay()+90,0,0,0) <= dateToEvaluate <= now;
    else if(this.dateSpan == "FUTURE")
        return dateToEvaluate > now;
    else if(this.dateSpan == "THISMONTH")
        return this.isSameMonth(dateToEvaluate, now);
    else if(this.dateSpan == "THISYEAR")
        return dateToEvaluate.getFullYear() == now.getFullYear();
    else if(this.dateSpan == "LASTYEAR")
        return dateToEvaluate.getFullYear() == (now.getFullYear()-1);
    else 
        alert("Not implemented:" + this.dateSpan);
    return false;
}
DateSpan.prototype.isSameMonth = function(date1,date2)
{
        return (date1.getDay() == date2.getDay() 
                && date1.getMonth() == date2.getMonth() 
                && date1.getFullYear() == date2.getFullYear())

}
DateSpan.prototype.isSameDay = function(date1,date2)
{
        return (date1.getMonth() == date2.getMonth() 
                && date1.getFullYear() == date2.getFullYear())

}
DateSpan.prototype.unitTest = function()
{
}


function FormRuleLogger(definitionDisplayLayoutControl, active)
{
    this.text = '';
    this.id = definitionDisplayLayoutControl.id + 'formRuleLogger';
    this.definitionDisplayLayoutControl = definitionDisplayLayoutControl;
    this.active = active;

}

FormRuleLogger.prototype.show = function()
{
    if(this.active)
    {
        var element = document.getElementById(this.id);
        
        if(element != null)
        {
            element.parentNode.removeChild(element);
        }
        
        element = document.createElement('DIV');
        element.id = this.id;
        document.body.appendChild(element);
        
        element.innerHTML = this.text;
    }
    

}

FormRuleLogger.prototype.log = function(text)
{
    if(this.active)
        this.text = this.text + '<br>' + this.definitionDisplayLayoutControl.id + ' - ' + text;

}
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();