﻿//-------------------------------------------------------------------------------------------------
//  ChecksDivManager: Responsible for CourtType- and Court- checklists functionality.
//-------------------------------------------------------------------------------------------------

var checksDivManager =
{
    target: {}, // object containing div and hidden input ID of courts check-list
    selectedCourtTypes: [], // array of current selected court-types
    savedCourts: [], // saved courts array in case query was requested for load
    maagarId: 0,
    courts: '',
    courtTypes: '',

    GetCourtsHidden: function() // hidden input containing current court IDs
    {
        return document.getElementById(checksDivManager.target.hiddenId);
    },
    GetAllCourtsCheck: function()
    {
        return document.getElementById('allCourtsCheck');
    },
    GetAllCourtTypesCheck: function()
    {
        return document.getElementById('allCourtTypesCheck');
    },
    GetMaagarIdHidden: function(maagarClientId) // hidden input containing current maagar ID
    {
        return document.getElementById(maagarClientId).value;
    },

    // param "courts": csv string of court-ID values to initiate or null.
    // param "courtTypes": array of court-type-ID values to initiate or null.
    Init: function(divId, hiddenId, courts, courtTypes, maagarHiddenClientId)
    {
        this.target =
        {
            divId: divId,
            hiddenId: hiddenId
        };

        if (courtTypes !== '')
        {
            // parse the courtTypes to an object
            //courtTypes = eval(courtTypes);
            checksDivManager.maagarId = checksDivManager.GetMaagarIdHidden(maagarHiddenClientId);
            //this.SetMultipleCourtTypes(courtTypes);
        }
        else
        {
            // Set all court types
            //this.GetAllCourtTypesCheck().checked = true;
            checksDivManager.maagarId = checksDivManager.GetMaagarIdHidden(maagarHiddenClientId);
            //this.SetAllCourtTypes(true);
        }

        //        if (courts !== '')
        //            this.savedCourts = eval(courts);
        //        else
        //        // Check all courts check
        //            this.GetAllCourtsCheck().checked = true;

        checksDivManager.courts = courts;
        checksDivManager.courtTypes = courtTypes;
    },

    InitFromData: function(target, courts, courtTypes, maagarId)
    {
        this.target = target;

        if (courtTypes !== '')
        {
            // parse the courtTypes to an object
            courtTypes = eval(courtTypes);
            checksDivManager.maagarId = maagarId;
            this.SetMultipleCourtTypes(courtTypes);
        }
        else
        {
            // Set all court types
            this.GetAllCourtTypesCheck().checked = true;
            checksDivManager.maagarId = maagarId;
            this.SetAllCourtTypes(true);
        }

        if (courts !== '')
        {
            this.savedCourts = eval(courts);
        }
        else
        // Check all courts check
            this.GetAllCourtsCheck().checked = true;
    },

    // Set saved court values to all
    ClearCourtValues: function()
    {
        checksDivManager.GetCourtsHidden().value = '0';
    },

    // Add/Remove a selected court value
    SetCourtValue: function(value, checked)
    {
        var hidden = checksDivManager.GetCourtsHidden();

        if (checksDivManager.GetAllCourtsCheck().checked)
        {
            checksDivManager.GetAllCourtsCheck().checked = false;

            // allCourtsCheck was checked: Clear the hidden's value of all courts or 0
            hidden.value = '';
        }

        value = value + ',';

        if (checked)
        {
            // add value and comma
            hidden.value += value;
        }
        else
        {
            // remove value and comma
            hidden.value = hidden.value.replace(value, '');
        }
    },

    SetCourtType: function(value, checked)
    {
        checksDivManager.GetAllCourtTypesCheck().checked = false;

        if (this.selectedCourtTypes[0] === 0)
        // Remove 0 from array
            this.selectedCourtTypes.splice(0, 1);

        if (checked)
        {
            // Add value to array
            this.selectedCourtTypes.push(value);
        }
        else
        {
            // get index of item
            var i = utility.ArrayIndexOf(this.selectedCourtTypes, value);

            if (i >= 0)
            // Remove value from array
                this.selectedCourtTypes.splice(i, 1);
        }

        GetCourtsByCourtType(this.selectedCourtTypes);
    },

    // Set selected court-types array to a specified array of values: used for reloading a query
    // param "values": array of court-type IDs
    SetMultipleCourtTypes: function(values)
    {
        checksDivManager.GetAllCourtTypesCheck().checked = false;

        // Set array values
        this.selectedCourtTypes = values;

        //
        // Run through all court-types check-boxes and check al requested checkboxes.
        //
        utility.ExecFuncOnElements
        (
            'checkbox', 'courtTypeCheck',
            function(checkbox)
            {
                if (utility.ArrayIndexOf(values, checkbox.value) > -1)
                {
                    checkbox.checked = true;
                }
            }
        );

        GetCourtsByCourtType(values);
    },

    // Set selected court-types array to a specified array of values: used for reloading a query
    // param "values": array of court IDs
    SetMultipleCourts: function(values)
    {
        checksDivManager.GetAllCourtsCheck().checked = false;
        var hid = checksDivManager.GetCourtsHidden();

        // clear values
        hid.value = '';

        //
        // Run through all court check-boxes and check al requested checkboxes and add their values to the hidden input.
        //
        utility.ExecFuncOnElements
        (
            'checkbox', 'listDivCheck',
            function(checkbox)
            {
                if (utility.ArrayIndexOf(values, checkbox.value) > -1)
                {
                    checkbox.checked = true;

                    // Add court-ID to hidden input
                    hid.value += checkbox.value + ',';
                }
            }
        );
    },

    SetAllCourtTypes: function(isChecked)
    {
        if (isChecked)
        {
            // Set array to all
            this.selectedCourtTypes = [0];

            // Uncheck all
            utility.ExecFuncOnElements('checkbox', 'courtTypeCheck', function(checkbox) { checkbox.checked = false; });
        }
        else
        {
            // Clear array
            this.selectedCourtTypes = [];
        }

        GetCourtsByCourtType(this.selectedCourtTypes);
    },

    // allCourtsCheck has been checked
    SetAllCourts: function(checked)
    {
        if (checked == null)
            checked = checksDivManager.GetAllCourtsCheck().checked;

        if (!checked)
        {
            // Clear saved court values
            checksDivManager.GetCourtsHidden().value = '';
            return;
        }

        var isAll = checksDivManager.GetAllCourtTypesCheck().checked;
        var hid = checksDivManager.GetCourtsHidden();

        // Set saved court value to all courts if allCourtTypesCheck checked, else to empty
        hid.value = isAll ? '0' : '';

        //
        // Run through all court-check-boxes and uncheck and, if this is not all court-types, 
        // add court-IDs to hidden input
        //
        utility.ExecFuncOnElements
        (
            'checkbox', 'listDivCheck',
            function(checkbox)
            {
                checkbox.checked = false;

                if (!isAll)
                // Add court-ID to hidden input
                    hid.value += checkbox.value + ',';
            }
        );
    },

    Validate: function(source, args)
    {
        args.IsValid = true;

        if (divManager.IsPanelVisible('dataPanel'))
            args.IsValid = checksDivManager.GetCourtsHidden().value !== '';
    }
};


function GetCourtsTypeByMaagarIdAndPackCode(maagarId)
{
    ajaxHelper.ShowProgress('GetCourtsTypeByMaagarIdAndPackCode', $j(".courtTypeChecksDiv:first").attr("id"));
    ajaxHelper.ShowProgress('GetCourtsByCourtType', checksDivManager.target.divId);
    //    if (checksDivManager.GetAllCourtsCheck().checked === false)
    //        checksDivManager.savedCourts = eval('[' + checksDivManager.GetCourtsHidden().value + ']');
    checksDivManager.maagarId = maagarId;
    SearchEngineService.GetCourtsTypeByMaagarIdAndPackCode(maagarId, GetCourtsTypeByMaagarIdAndPackCode_Complete);
}

function GetCourtsTypeByMaagarIdAndPackCode_Complete(result)
{
    $j(".courtTypeChecksDiv:first").html(result !== null ? result : '');
    checksDivManager.InitFromData(checksDivManager.target, checksDivManager.courts, checksDivManager.courtTypes, checksDivManager.maagarId);

}

function GetCourtsByCourtType(courtTypes)
{
    ajaxHelper.ShowProgress('GetCourtsByCourtType', checksDivManager.target.divId);

    if (checksDivManager.GetAllCourtsCheck().checked === false)
        checksDivManager.savedCourts = eval('[' + checksDivManager.GetCourtsHidden().value + ']');

    SearchEngineService.GetCourtsByCourtType(courtTypes, checksDivManager.maagarId, GetCourtsByCourtType_Complete);
}

function GetCourtsByCourtType_Complete(result)
{
    //alert(checksDivManager.savedCourts);
    document.getElementById(checksDivManager.target.divId).innerHTML = result !== null ? result : '';

    if (checksDivManager.savedCourts && checksDivManager.savedCourts.length > 0)
    {
        // Set the court values from the requested query
        checksDivManager.SetMultipleCourts(checksDivManager.savedCourts);
        checksDivManager.savedCourts = null;
    }
    else if (checksDivManager.GetAllCourtsCheck().checked)
    {
        // reset hidden value to new values    
        checksDivManager.SetAllCourts();
    }
}

//-------------------------------------------------------------------------------------------------
//  DivManager: Responsible for all panel functionality as well as general page functionality.
//-------------------------------------------------------------------------------------------------

var divManager =
{
    panels: ['titlePanel', 'subjectsPanel', 'maagarimPanel', 'dataPanel', 'wordsPanel', 'datePanel', 'refsPanel', 'billsPanel', 'locationPanel', 'pagesNumberPanel'],

    // ID of hidden input the saves the current poolId
    poolHiddenId: '',
    maagarimDropId: '',
    byNameSmartDropKey: '',
    docTypesChecksId: '',
    allDocTypesCheckId: '',

    poolHidden:
    {
        Get: function()
        {
            return document.getElementById(divManager.poolHiddenId).value;
        },
        Set: function(value)
        {
            document.getElementById(divManager.poolHiddenId).value = value;
        }
    },

    Init: function(maagarimDropId, poolHiddenId, byNameSmartDropKey, docTypesChecksId, allDocTypesCheckId)
    {
        var maagarimDrop = document.getElementById(maagarimDropId);
        var poolId = maagarimDrop.options[maagarimDrop.selectedIndex].value; //Get current poolId

        this.allDocTypesCheckId = allDocTypesCheckId;
        this.docTypesChecksId = docTypesChecksId;
        this.byNameSmartDropKey = byNameSmartDropKey;
        this.maagarimDropId = maagarimDropId;
        this.poolHiddenId = poolHiddenId;
        this.poolHidden.Set(poolId);

        // hide all panels
        $j(".panel").css("display", "inline");

        // show appropriate panels
        this.ShowPoolPanels(poolId, false);

        // initialize popups
        $j('.popup').cluetip({
            width: 415,
            closeText: '<img src="../Images/close_rss.gif" alt="סגור" />',
            cluetipClass: 'koldin',
            mouseOutClose: true,
            closePosition: 'title',
            showTitle: false,
            positionBy: 'bottomTop',
            titleAttribute: 'ctTitle',
            ajaxProcess: function(data)
            {
                // Parse message out of xml and wrap in a div
                return '<div style="text-align:right">' + $j(data).find('string').text() + '</div>';
            },
            ajaxSettings: {
                dataType: 'xml'
            }
        });

        // Fix IE6 combo-box z-index problem
        $j('#cluetip').bgiframe();

        var docTypesIdBase = this.docTypesChecksId.replace(/\$/g, '_');

        // Add an onclick event handler to all the docType checkboxes
        $j('input[id^="' + docTypesIdBase + '"]').click(function()
        {
            if (!this.checked) document.getElementById(divManager.allDocTypesCheckId).checked = false;
        });

        //        divManager.LoadDefaultMorphBox();

        // Initialize (un)select all -- ES
        $j("#selectorCheckBox").click
        (
            function()
            {
                $j("#maagarimPanel :checkbox").attr("checked", $j(this).is(":checked"));
            }
        );
    },

    // param "doClearDrops": True to clear all smartDrop selections, else false.
    ShowPoolPanels: function(currentPoolId, doClearDrops)
    {
        divManager.poolHidden.Set(currentPoolId);

        // change the name of the title smart-drop
        document.getElementById('byNameSpan').innerHTML = poolTypes[currentPoolId].byname;

        // hide/show the all-pool-docs button
        document.getElementById('allDocsBtnDiv').style.display = poolTypes[currentPoolId].showAllDocsBtn ? '' : 'none';

        // Get poolType
        var poolType = poolTypes[currentPoolId].type;

        //set visibility of part of laws controls
        if (poolType == "Laws")
        {
            $j(".sourceAuthorityRow").removeClass("None");
            $j(".definitionsRow").removeClass("None");
            $j(".clauseTitleRow").removeClass("None");
        }
        else
        {
            $j(".sourceAuthorityRow").addClass("None");
            $j(".definitionsRow").addClass("None");
            $j(".clauseTitleRow").addClass("None");
        }

        if (doClearDrops)
        // Clear all smartDrop selections
            smartDropManager.ClearAllDrops();

        // Change the byNameSmartDrop's poolType
        smartDropManager.ChangeDropPool(divManager.byNameSmartDropKey, poolType);

        var displayedPanels = poolPanels[poolType];

        // Display and hide appropriate panels by poolType
        for (var i = 0; i < divManager.panels.length; i++)
        {
            var panel = divManager.panels[i];
            var displayed = false;

            for (var j = 0; j < displayedPanels.length; j++)
            {
                if (panel == displayedPanels[j])
                {
                    displayed = true;
                    break;
                }
            }

            document.getElementById(panel).style.display = displayed ? 'inline' : 'none';
        }

        if (divManager.IsPanelVisible('subjectsPanel'))
        {
            // Set categories control poolId
            selectsManager.SetPool(currentPoolId);
        }

        document.getElementById('byNameDiv').style.display = 'inline';

        //
        // Hide and show all the extrasElements
        //
        for (var poolId in poolTypes)
        {
            var extraId = poolTypes[poolId].extrasElemId;

            if (extraId !== '')
            {
                var extraStyle = document.getElementById(extraId).style;

                // Hide or show the extra-element depending on the currentPoolId
                extraStyle.display = poolId == currentPoolId ? '' : 'none';
            }
        }

        $j(".pageNumberDropList").val(0); // Doesn't find 2 as a value.

        
        var maagarIdsString = $j(".MaagarIdsString").val();
        var doGetCourtsType = false;
        var mySplitResult = maagarIdsString.split(",");
        for (i = 0; i < mySplitResult.length; i++)
        {
            if (mySplitResult[i] == currentPoolId)
            {
                doGetCourtsType = true;
                break;
            }
        }
        if (doGetCourtsType)
        {
            GetCourtsTypeByMaagarIdAndPackCode(currentPoolId);
        }

    },

    IsPanelVisible: function(panelName)
    {
        var poolId = divManager.poolHidden.Get();
        var poolType = poolTypes[poolId].type;
        var panels = poolPanels[poolType];
        var s = panels.toString();
        return s.indexOf(panelName) > -1;
    },

    // param "maagarimDropId": send maagarimDropId, else null
    ResetForm: function()
    {
        // Save current maagarId (for IE)
        var maagarId = divManager.poolHidden.Get();

        // Call ResetForm in CommonScripts.js
        ResetForm();

        // return maagarimDrop to saved index
        document.getElementById(divManager.maagarimDropId).value = maagarId;

        // Change the byNameSmartDrop's poolType
        smartDropManager.ChangeDropPool(divManager.byNameSmartDropKey, poolTypes[maagarId].type);

        // Reset maagarId (for IE)
        divManager.poolHidden.Set(maagarId);

        // Reset courts and courtType check lists
        checksDivManager.SetAllCourtTypes(true);

        // Reset selected categoryId to 0.
        selectsManager.Reset();

        //reset morphology
        morphManager.ClearAll();
        divManager.ResetMorphCombo();

    },

    ResetMorphCombo: function()
    {
        var combo = document.getElementById(wordsManager.morphPerFieldComboId);
        if (combo)
        {
            //checkBox.checked = false;
            combo.options[0].selected = "selected";
        }

    },

    BeforeSubmit: function(senderId)
    {
        // Parse the contents of the words fields and combine them into hidden fields
        wordsManager.ParseAllWordsToHiddens();
        tagFieldsManager.ParseWordsToHiddens();
        return smartDropManager.ValidateBeforeSubmit(senderId);
    }

    //    LoadDefaultMorphBox: function()
    //    {
    //        AjaxService.GetDefaultMorphSelectMorphWUC(morphManager.defaultMorph,divManager.LoadDefaultMorphBox_Complete );
    //    },
    //    
    //    LoadDefaultMorphBox_Complete: function(args)
    //    {
    //        if (args)
    //        {
    //            ChangeElementText("defaultMorphDiv",args);
    //        }
    //    }

};
// Separate procedure numbers into appropriate textboxes
/*function FixProcNums(numTxt, monthId, yearId)
{
var parts = numTxt.value.split('-');
    
//    if(parts.length == 1)
//        parts = parts.split('/');
        
if(parts.length == 3)
{
numTxt.value = parts[0];
document.getElementById(monthId).value = parts[1];
document.getElementById(yearId).value = parts[2];
        
//        if(parts.length == 2)
//        {
//            yearTxt.value = parts[1];
//        }
//        else if(parts.length == 3)
//        {
//            document.getElementById(monthId).value = parts[1];
//            yearTxt.value = parts[2];
//        }
}
}*/

//-------------------------------------------------------------------------------------------------
//  Validator: Responsible for validation as well as the date functionality
//-------------------------------------------------------------------------------------------------

var validator =
{
    fromDateId: '',
    toDateId: '',
    selectId: '',
    lawRefInputId: '',
    clauseRefInputId: '',
    publishSourceDropId: '',

    Init: function(fromDateId, toDateId, selectId, lawRefInputId, clauseRefInputId, publishSourceDropId)
    {
        this.fromDateId = fromDateId;
        this.toDateId = toDateId;
        this.selectId = selectId;
        this.lawRefInputId = lawRefInputId;
        this.clauseRefInputId = clauseRefInputId;
        this.publishSourceDropId = publishSourceDropId;
    },

    ValidateMaagarim: function(source, args)
    {
        if (divManager.IsPanelVisible('maagarimPanel') === false)
        {
            args.IsValid = true;
            return;
        }

        args.IsValid = false;

        // Run through all maagarimCheckList checkboxes and see if at least one is checked
        utility.ExecFuncOnElements('checkbox', 'maagarimCheckList', function(checkbox)
        {
            if (checkbox.checked)
            // at least one maagar is checked: Valid
                args.IsValid = true;
        });
    },

    ValidateFromDate: function(source, args)
    {
        args.IsValid = validator.ValidateDate(source, document.getElementById(validator.fromDateId));
    },

    ValidateToDate: function(source, args)
    {
        args.IsValid = validator.ValidateDate(source, document.getElementById(validator.toDateId));
    },

    ValidateDate: function(source, args)
    {
        if (document.getElementById(validator.selectId).value !== 'Custom')
            return true;

        if (divManager.IsPanelVisible('datePanel'))
        {
            args.IsValid = false;

            var parts = args.value.split('/');

            if (parts.length == 3)
            {
                args.IsValid = parts[0] <= 31 && parts[1] <= 12 && parts[2] <= (new Date()).getFullYear() && parts[2] >= 1900;
            }
        }
        else
        {
            args.IsValid = true;
        }

        return args.IsValid;
    },

    SetDateRange: function()
    {
        var select = document.getElementById(validator.selectId);

        if (document.getElementById(validator.toDateId).value === '' && document.getElementById(validator.fromDateId).value === '')
            select.value = 'All';
        else
            select.value = 'Custom';
    },

    ValidateLawAndClauseRefs: function(source, args)
    {
        var lawRefText = document.getElementById(validator.lawRefInputId).value;
        var clauseText = document.getElementById(validator.clauseRefInputId).value;

        // Validation fails if there is clause-text but no law-text
        args.IsValid = !(utility.TrimString(clauseText).length > 0 && utility.TrimString(lawRefText).length === 0);
    },

    ValidateNumericRange: function(source, args)
    {
        // min and max attributes on the custom-validator are required for this function
        //        if ((source.hasAttribute('min') && source.hasAttribute('max')) || // standard check
        //            (source.min && source.max)) // IE check
        //            return;

        if (!source.getAttribute('min') || source.getAttribute('min') == '' ||
            !source.getAttribute('max') || source.getAttribute('max') == '')
            return

        if (source.id.indexOf('publishVolumeTxtValidator') > 0 && document.getElementById(validator.publishSourceDropId).selectedIndex == 0)
            return;

        args.IsValid = false;

        // Check that the value contains digits only
        if (/^\d+$/.test(args.Value))
        {
            var value = parseInt(args.Value);
            //            var min = source.getAttribute ? parseInt(source.getAttribute('min')) : parseInt(source.min); // standard vs IE attribute-reading
            //            var max = source.getAttribute ? parseInt(source.getAttribute('max')) : parseInt(source.max); // standard vs IE attribute-reading

            // Check that the value is within the range
            args.IsValid = value >= parseInt(source.getAttribute('min')) && value <= parseInt(source.getAttribute('max'));
        }
    }
};


//-------------------------------------------------------------------------------------------------
//  WordsManager: Responsible for words functionality
//-------------------------------------------------------------------------------------------------

var wordsManager =
{
    maxRows: 5,
    inputsPerRow: 3,

    // 1-based id-counter of last-added anyWords-row
    anyWordsCounter: 1,

    // anyWords-row html template
    anyWordsRowHtml: '',

    // Regex for identifying and changing all ids/names of important elements
    anyWordsCounterRegex: new RegExp('(anyWords[a-zA-Z(]+)1', 'gi'),

    // Regex for finding input-values
    htmlCleanerRegex: new RegExp('(<input[^><]+value=)("[^"]*"|[^ ]+)', 'gi'),

    // Regex for finding jQuery attributes (which might be cloned in IE)
    jqueryRefCleanerRegex: /jQuery\d+="\d+"/gi,

    wordsGroups:
    {
        AllWords: { baseName: 'allWords', hiddenId: '', innerBinder: ' וגם ', hasOuterBinder: false, divCounter: 1 },
        AnyWords: { baseName: 'anyWords', hiddenId: '', innerBinder: ' או ', hasOuterBinder: true, divCounter: 1 },
        ExactWords: { baseName: 'exactWords', hiddenId: '', innerBinder: ' וגם ', hasOuterBinder: false, divCounter: 1 },
        ExcludedWords: { baseName: 'excludedWords', hiddenId: '', innerBinder: ' או ', hasOuterBinder: false, divCounter: 1 }
    },

    morphPerFieldComboId: '',

    // Returns the hidden input associated with the specified group
    // param "groupName": The name of the group whose hidden should be returned
    GetHiddenInput: function(groupName)
    {
        return document.getElementById(wordsManager.wordsGroups[groupName].hiddenId);
    },

    // Returns all inputs (as a jQuery object-array) belonging to the specified group.
    GetGroupInputs: function(group, counter)
    {
        if (!counter || counter == undefined)
            counter = '';

        //return $j('input[@name='+ group.baseName + 'Txt' + counter +']');
        return $j('input[@id^="' + group.baseName + 'Txt' + counter + '"]');
    },

    // Returns all  morph inputs belonging to the specified group.
    GetGroupMorphInputs: function(group, counter)
    {
        var morphInputs = morphManager.inputGroups[utility.CapitalizeFirstLetter(group.baseName)].inputs;

        if (counter && counter != undefined)
        {
            var allMorphInputs = morphInputs;
            morphInputs = [];

            for (var i = 0; i < allMorphInputs.length; i++)
            {
                if (allMorphInputs[i].input.id.indexOf(group.baseName + 'Txt' + counter) === 0)
                {
                    morphInputs.push(allMorphInputs[i]);
                }
            }
        }
        return morphInputs;
    },

    // Parses the wordsExpression client-string back to the inputs of the specified group, adding rows if necessary
    // param "group": The group into whose associated inputs to set the parsed words-strings
    // param "groupString": The wordsExpression client-string to parse
    // param "counter" (optional): (For anyWords only) The one-based counter of the anyWords row into whose inputs to set the parsed strings
    ParseGroupStringToInputs: function(group, groupString, counter, prevRows)
    {
        // Get all text-nodes from the string in the hidden
        //        var matches = utility.MatchAll(/\{([^{}]+)\}/g, groupString);
        //        var matches = utility.MatchAll(/\{([^{}]+)\}\[m([\d+])\]/g, groupString);
        var matches = utility.MatchAll(/\{([^{}]+)\}\[m(\d+)\](\[d(\d+)\])?/g, groupString);


        // Get all group-inputs
        var $inputs = wordsManager.GetGroupInputs(group, counter);

        // Check that there are enough inputs
        if ($inputs.length < matches.length)
        {
            var diff = matches.length - $inputs.length;
            var rowsCount = Math.ceil(diff / 3);

            // Add as many rows as are necessary to fill with the matched text-nodes
            for (var rowIndex = 1; rowIndex <= rowsCount; rowIndex++)
            {
                var morphType = '';
                if (group.baseName == "exactWords")
                {
                    morphType = "MorphixExact";
                }

                wordsManager.AddWordsDiv(group.baseName, utility.CapitalizeFirstLetter(group.baseName), morphType, counter);
            }

            // Get all inputs (including those newly created)
            $inputs = wordsManager.GetGroupInputs(group, counter);
        }

        for (var i = 0; i < matches.length; i++)
        {
            var fieldValue = matches[i][1];
            var isNear = false;
            //            if (fieldValue.indexOf('[ק]') >= 0)
            //            {
            //                //fieldValue = utility.ReplaceAllOccurances(fieldValue,"[1]","[ק]");
            //                isNear = true;
            //            }

            var nearCount = parseInt(matches[i][4]);
            isNear = (!isNaN(nearCount));

            // Insert text-nodes into inputs
            $inputs[i].value = fieldValue;

            var morphIndex = i;
            if (counter)
            {
                morphIndex += (counter - 1) * 3;
            }

            var inputsIndex;
            if (prevRows)
            {
                inputsIndex = i + prevRows * 3;
            }
            else
            {
                inputsIndex = i;
            }

            // morphManager.SetActiveMorphInput(morphManager.inputGroups[utility.CapitalizeFirstLetter(group.baseName)].inputs[morphIndex]);
            morphManager.SetActiveMorphInput(morphManager.inputGroups[utility.CapitalizeFirstLetter(group.baseName)].inputs[inputsIndex]);
            morphManager.ChangeMorphByCode(matches[i][2]);
            morphManager.ChangeNearType(isNear, nearCount);
        }

        return $inputs.length / 3;
    },

    Init: function(allWordsHiddenId, anyWordsHiddenId, exactWordsHiddenId, excludedWordsHiddenId, morphPerFieldComboId, selectedMorphType)
    {

        this.morphPerFieldComboId = morphPerFieldComboId;
        this.wordsGroups.AllWords.hiddenId = allWordsHiddenId;
        this.wordsGroups.AnyWords.hiddenId = anyWordsHiddenId;
        this.wordsGroups.ExactWords.hiddenId = exactWordsHiddenId;
        this.wordsGroups.ExcludedWords.hiddenId = excludedWordsHiddenId;

        wordsManager.RegisterAllGroups();

        // Get the anyWordsRow html for adding rows later 
        wordsManager.anyWordsRowHtml =
            $j('tr[@id="anyWordsRow1"]:last').html().replace(wordsManager.htmlCleanerRegex, '$1""').replace('עם', '<i>וגם</i> עם').replace(wordsManager.jqueryRefCleanerRegex, '');

        //if checkbox is checked, show morph buttons
        var combo = document.getElementById(morphPerFieldComboId);
        if (combo)
        {
            if (combo.options[combo.selectedIndex].value == "true")
            {
                morphManager.ToggleMorphs();
            }
        }

        morphManager.SetAllNearTypes(Boolean.parse($j('[id$=' + morphManager.mainNearSelect + ']').val()), $j('[id$=' + morphManager.mainNearValueText + ']').val());

        //load selected morph as default for morphManager
        morphManager.SetAllMorphs(selectedMorphType);

        //
        // Reload from hidden fields
        //
        for (var groupName in wordsManager.wordsGroups)
        {
            var group = wordsManager.wordsGroups[groupName];
            var hidden = wordsManager.GetHiddenInput(groupName);

            if (hidden.value.length > 0)
            {
                var groupMatches = hidden.value.match(/\{{2}/g);

                if (group.hasOuterBinder)// || (groupMatches && groupMatches.length > 1))
                {
                    //
                    // Hidden contains multiple word-groups
                    //
                    var groupsRegex = / (וגם|או) (?=\{{2})/g;

                    // Remove binding-expressions between groups
                    var cleanedValue = hidden.value.replace(groupsRegex, '');

                    // Split up the groups
                    var wordsStrings = cleanedValue.split(/(?=\{{2})/g);

                    var typeRows = 0;

                    for (var i = 0; i < wordsStrings.length; i++)
                    {
                        if (i > 0)
                        {
                            // Add another anyWords row for this wordsString
                            wordsManager.AddAnyWordsRow(i + 1);
                        }

                        typeRows += wordsManager.ParseGroupStringToInputs(group, wordsStrings[i], i + 1, typeRows);
                    }
                }
                else
                {
                    // Hidden contains a single word-group
                    wordsManager.ParseGroupStringToInputs(group, hidden.value);
                }
            }
        }
    },

    RegisterGroupMorphInputs: function(groupBaseName, morphTypeName, counter)
    {
        if (!counter)
            counter = '';

        var morphInputs = $j('div[@id="' + groupBaseName + 'Div' + counter + '" ] table[@class="morphTable"] input');

        for (var fieldIndex = 0; fieldIndex < morphInputs.length; fieldIndex++)
        {
            morphManager.Register(morphInputs[fieldIndex].id, morphInputs[fieldIndex].id, morphTypeName, utility.CapitalizeFirstLetter(groupBaseName));
        }
    },

    RegisterAllGroups: function()
    {
        wordsManager.RegisterGroupMorphInputs('allWords', 'Morphix1');
        wordsManager.RegisterGroupMorphInputs('exactWords', 'Morphix1'); //'MorphixExact');
        wordsManager.RegisterGroupMorphInputs('excludedWords', 'Morphix1');

        for (var i = 1; i <= wordsManager.anyWordsCounter; i++)
        {
            wordsManager.RegisterGroupMorphInputs('anyWords', 'Morphix1', i);
        }
    },

    // Concatenates together the values of each input field as a word-string, together with the wordGroup's innerBinder, and
    // wrap the result in curlies.
    // param "morphInputs": The inputs whose values are to be converted to a string
    // param "group": The wordGroup to which the input belongs
    WordInputsToString: function(morphInputs, group)
    {
        var regex = new RegExp(group.innerBinder + '$');
        var string = '';

        for (var i = 0; i < morphInputs.length; i++)
        {
            var currClientString = morphInputs[i].GetClientString();

            if (currClientString && currClientString.replace(' ', '').length > 0)
                string += currClientString + group.innerBinder;
        }

        if (string.length > 0)
        // Remove last comma and binder, trim, and surround with curlies
            string = '{' + utility.TrimString(string.replace(regex, '')) + '}';

        return string;
    },

    // Converts the inputs in the specified div object, together with the select associated with the specified wordGroup, to a string.
    // Returns an empty string if the inputs are all empty.
    //    WordGroupToString: function($div, group)
    //    {
    //        // Get the binder-select associated with the current group
    //        var select = wordsManager.GetBinderSelect($div, group);
    //        
    //        // Get the selected bindingExpression
    //        var bindingExpression = '';
    //        
    //        if(select)
    //            bindingExpression = select.options[select.selectedIndex].value;
    //        
    //        // Get the inputs in the div
    //        var $inputs = $div.children();
    //        
    //        var string = wordsManager.WordInputsToString($inputs, group);
    //        
    //        return string.length > 0 ? bindingExpression + string : '';
    //    },

    // Appends to the specified element a clone of that element, with any text in input values removed
    // CANCELLED--->(and with the counter-value of the name attribute upped by one)
    AppendEmptyClone: function(element)//, counter)
    {
        // Get inner html without any of the input-text
        var html = element.html().replace(wordsManager.htmlCleanerRegex, '$1""').replace(wordsManager.jqueryRefCleanerRegex, '');

        // Add a new element after the found one with the new html
        //element.after(element.clone().attr('name',(++counter).toString()).html(html));
        element.after(element.clone(false).html(html));
    },

    // Add a words-row (div) identical to the last div with the specified divId
    // If the maximal number of rows have been added, the link with the specified linkId is deactivated
    // param "counter" (optional): The counter to add to the element names in order to find them in the DOM
    AddWordsDiv: function(baseName, groupName, defaultMorph, counter)
    {
        if (!counter)
            counter = '';

        if (!defaultMorph)
            defaultMorph = morphManager.defaultMorph;

        var divId = baseName + 'Div' + counter;
        var linkId = baseName + 'Link' + counter;

        // Get last div with specified Id    
        var div = $j('div[@id=' + divId + ']:last');

        wordsManager.AppendEmptyClone(div);

        //
        // Get the new counter and up the new DIV's ID by one
        //
        var newCounter = wordsManager.wordsGroups[groupName].divCounter + 1;

        // Get new div with specified Id    
        var newDiv = $j('div[@id=' + divId + ']:last');

        //change ids for morphInputs and register them
        var inputs = newDiv.find('input');
        var images = newDiv.find('img');
        for (var i = 0; i < inputs.length; i++)
        {
            var inputId = baseName + 'Txt' + counter + "_" + newCounter + "_" + (i + 1).toString();

            $j(inputs[i]).attr('id', inputId);

            var imageId = inputId + '_img';
            $j(images[i]).attr('id', imageId);

            morphManager.Register(inputId, inputId, defaultMorph, groupName);
        }

        //incrader counter
        wordsManager.wordsGroups[groupName].divCounter++;

        // allow up to 5 rows
        if ($j('div[@id=' + divId + ']').length >= wordsManager.maxRows)
        {
            // de-activate link
            $j('#' + linkId).attr('class', 'inactiveLink').attr('href', 'javascript:void(0)');
        }
    },

    AddAnyWordsDiv: function(counter)
    {
        wordsManager.AddWordsDiv('anyWords', 'AnyWords', morphManager.defaultMorph, counter);
    },

    AddAnyWordsRow: function()
    {
        // Hide the link
        $j('a[@id="anyWordsRowLink"]:last').css('display', 'none');

        // Get last anyWords TR
        var tr = $j('tr[@id^="anyWordsRow"]:last');

        // Add a new row after the last one with the html-template
        tr.after(tr.clone().html(wordsManager.anyWordsRowHtml));

        // Hide the help image
        //$j('img[@id^="anyWordsHelpBtn"]:last').css('display','none');

        // Get newly created anyWords TR 
        tr = $j('tr[@id^="anyWordsRow"]:last');

        //
        // Get the new counter and up the new TR's ID by one
        //
        var newCounter = wordsManager.anyWordsCounter + 1;
        trId = tr.attr('id').replace(wordsManager.anyWordsCounter.toString(), newCounter.toString());
        tr.attr('id', trId);

        // Add "וגם", and up all id-counters
        tr.html(tr.html().replace(wordsManager.anyWordsCounterRegex, '$1' + newCounter.toString()));

        //change ids for morphInputs and register them
        var inputs = tr.find('input');
        var images = tr.find('img[@id^="anyWordsTxt"]');
        for (var i = 0; i < inputs.length; i++)
        {
            var inputId = 'anyWordsTxt' + newCounter + "_1_" + (i + 1).toString();
            $j(inputs[i]).attr('id', inputId);

            var imageId = inputId + '_img';
            $j(images[i]).attr('id', imageId);

            morphManager.Register(inputId, inputId, morphManager.defaultMorph, 'AnyWords');
        }

        wordsManager.anyWordsCounter++;

        if (wordsManager.anyWordsCounter >= wordsManager.maxRows)
        // No more rows allowed: Hide the add-new-row link
            var link = $j('a[@id="anyWordsRowLink"]:last').css('display', 'none');

        // initialize popup for this help button
        var image = $j('img[@id^="anyWordsHelpBtn"]:last');
        image.attr('rel', image.attr('rel').replace('AnyWords', 'AnyWordsGroupWords'));

        image.cluetip({
            width: 415,
            closeText: '<img src="../Images/close_rss.gif" alt="סגור" />',
            cluetipClass: 'koldin',
            mouseOutClose: true,
            closePosition: 'title',
            showTitle: false,
            positionBy: 'bottomTop',
            titleAttribute: 'ctTitle',
            ajaxProcess: function(data)
            {
                // Parse message out of xml and wrap in a div
                return '<div style="text-align:right">' + $j(data).find('string').text() + '</div>';
            },
            ajaxSettings: {
                dataType: 'xml'
            }
        });

    },

    // Parses the contents of all words fields into their hidden-input counterparts
    // This method should be called just before the form is submitted
    ParseAllWordsToHiddens: function()
    {

        for (var groupName in wordsManager.wordsGroups)
        {
            var group = wordsManager.wordsGroups[groupName];
            var hidden = document.getElementById(group.hiddenId);

            // clear the hidden
            hidden.value = '';

            if (group.hasOuterBinder)
            {
                //
                // Group has an outer bindingExpression: Convert the inputs in each div and their associated select into
                // separate group-strings, and set them (one after the other) to the hidden field
                //
                for (var i = 1; i <= wordsManager.anyWordsCounter; i++)
                {
                    //var $inputs = wordsManager.GetGroupInputs(group, i.toString());
                    //var valuesString = wordsManager.WordInputsToString($inputs, group);
                    var morphInputs = wordsManager.GetGroupMorphInputs(group, i.toString());

                    var valuesString = wordsManager.WordInputsToString(morphInputs, group);

                    if (valuesString.length > 0)
                    {
                        hidden.value += hidden.value.length > 0 ? ' וגם ' : '';
                        hidden.value += valuesString;
                    }
                }
            }
            else
            {
                //
                // Group has no outer bindingExpression: Just combine the values of all the inputs into one string and
                // set them to the hidden field
                //
                //var $inputs = wordsManager.GetGroupInputs(group);
                var morphInputs = wordsManager.GetGroupMorphInputs(group);

                hidden.value = wordsManager.WordInputsToString(morphInputs, group);
            }
        }
    }
};


var tagFieldsManager =
{
    regex: new RegExp(" וגם " + '$'),

    Init: function()
    {
        tagFieldsManager.SetSeperateString();
    },

    ParseWordsToHiddens: function()
    {
        //var regex = new RegExp(" וגם " + '$');
        //        var lawDocNameString = '', clauseNumberString = '';
        //        var lawDocName = $j(".lawDocName").val();
        //        var clauseNumber = $j(".clauseNumber").val();

        //        var currClientString = tagFieldsManager.PrepareMorphString(lawDocName);
        //        if (currClientString && currClientString.replace(' ', '').length > 0)
        //        {
        //            lawDocNameString += currClientString + " וגם ";
        //        }
        //        if (lawDocNameString.length > 0)
        //        {
        //            // Remove last comma and binder, trim, and surround with curlies
        //            lawDocNameString = '{' + utility.TrimString(lawDocNameString.replace(regex, '')) + '}';
        //        }

        //        currClientString = tagFieldsManager.PrepareMorphString(clauseNumber);
        //        if (currClientString && currClientString.replace(' ', '').length > 0)
        //        {
        //            clauseNumberString += currClientString + " וגם ";
        //        }
        //        if (clauseNumberString.length > 0)
        //        {
        //            // Remove last comma and binder, trim, and surround with curlies
        //            clauseNumberString = '{' + utility.TrimString(clauseNumberString.replace(regex, '')) + '}';
        //        }

        var lawDocNameString = tagFieldsManager.ParseWordMorphology($j(".lawDocName").val());
        $j(".lawDocNameHidden").val(lawDocNameString);

        var clauseNumberString = tagFieldsManager.ParseWordMorphology($j(".clauseNumber").val());
        $j(".clauseNumberHidden").val(clauseNumberString);

        var definitionsString = tagFieldsManager.ParseWordMorphology($j(".definition").val());
        $j(".definitionHidden").val(definitionsString);

        var clauseTitleString = tagFieldsManager.ParseWordMorphology($j(".clauseTitle").val());
        $j(".clauseTitleHidden").val(clauseTitleString);

    },

    ParseWordMorphology: function(inputValue)
    {

        var parsedString = '';

        if (inputValue != undefined)
        {
            var morphString = tagFieldsManager.PrepareMorphString(inputValue);
            if (morphString && morphString.replace(' ', '').length > 0)
            {
                parsedString += morphString + " וגם ";
            }
            if (parsedString.length > 0)
            {
                // Remove last comma and binder, trim, and surround with curlies
                parsedString = '{' + utility.TrimString(parsedString.replace(tagFieldsManager.regex, '')) + '}';
            }
        }
        return parsedString;
    },

    PrepareMorphString: function(inputValue)
    {
        inputValue = inputValue.replace(/[\{\}]/g, '');

        if (/\S+/.test(inputValue)) //check that the string contains non-space chars
        {
            return "{" +
                utility.TrimString(inputValue) +
                "}[m6]";
        }
    },

    SetSeperateString: function()
    {
        var lawDocNameString = $j(".lawDocNameHidden").val();
        var clauseNumberString = $j(".clauseNumberHidden").val();

        var matches = utility.MatchAll(/\{([^{}]+)\}\[m(\d+)\](\[d(\d+)\])?/g, lawDocNameString);
        if (matches.length > 0)
        {
            $j(".lawDocName").val(matches[0][1]);
        }

        matches = utility.MatchAll(/\{([^{}]+)\}\[m(\d+)\](\[d(\d+)\])?/g, clauseNumberString);
        if (matches.length > 0)
        {
            $j(".clauseNumber").val(matches[0][1]);
        }
    }

}

