SSRS报表管理器在非IE浏览器中使用下拉菜单时出现JavaScript错误

20
我一直在使用Firefox和Firebug调试ReportingServices.js文件。我发现SSRS(SQL Server Reporting Services)报告管理器(报告的Web前端)在Firefox(v7.0.1)中不能正常工作,原因是它使用javascript .lastChild 来查找元素。不幸的是,Firefox也将空格视为TextNode元素,从而导致元素选择无法按预期工作。
这在IE中可以正常运行,希望有人知道解决方法。我编辑了JavaScript代码以解决一个错误,但遇到了另一个更复杂的错误,因此手动修复可能会很麻烦。希望有可用的更新或补丁。
此版本运行于Windows 2008 R2数据中心服务器上的SQL Server 2008 R2标准版。
如果您认为这不是这样一个问题的论坛,请谅解。如果不合适,请建议我应该去哪里提问。这可能是一个JavaScript问题,但可能有软件更新的解决方案。
更新:
在修复了ReportingServices.js文件中浏览器兼容性问题几个小时后,我设法使其在Firefox、Chrome、Opera和Safari以及IE中都能正常工作。很抱歉我的答案分成了两部分,下面是更新的ReportingServices.js代码的全部内容。

我在我的电脑上安装了来自SQL Server 2008 R2的SSRS 2008,一切正常。但是当我安装了带有SSRS和报告管理器的SQL Server 2012 SP1后,所有浏览器都会出现“未捕获的类型错误:无法设置空值的'className'属性”错误。这很烦人,阻止了使用报告属性下拉菜单和管理报告。只有在IE处于IE7文档模式时才能正常工作。 - demp
1
说实话,我不认为这个问题是“过于局限”的。它涉及到向SSRS 2008 R2(以及显然是2012)报告管理器添加功能,以便它可以与非IE浏览器一起使用。SSRS本身是堆栈溢出上的一个标签,因此它必须是一个感兴趣的话题,而答案是解决所提出问题的出色工作代码。耸肩无论如何,这是我的两分钱... - Mike D.
我不同意这个问题被认为是“太局限”。这对许多人来说都是一个真正的编程问题。我的一个应用程序在Firefox中刚刚因为链接到Reporting Services而出现了故障。既然你已经关闭了它,我将看不到进一步答案的好处。 - Rob Kent
这个问题并不是“过于局限”——我也遇到了同样的问题,并使用了Chris的解决方法。 - ShellNinja
我认为这个问题已经帮助了4个“未来的访问者”,所以我很高兴。 - Michael Edenfield
显示剩余3条评论
2个回答

24

经过几个小时的修改原始的 ReportingServices.js 文件,我成功地修复了javascript代码,使其在Firefox、Chrome、Opera和Safari以及IE上都能正常工作。

尽管它有点大,但我已经发布了整个编辑后的 ReportingServices.js 供其他人使用。我在旧行上方标注了“chris edit”评论,以显示其编辑内容,这样您就可以(如果您关心的话)了解我所做的更改。我还在代码顶部总结了这些更改。

SSRS报告管理器Web界面非常有用,如果因为Microsoft没有测试它在IE之外的其他浏览器上而不使用它,那将是一件遗憾。这就是为什么我总是喜欢jQuery而不是普通的javascript,以避免这种跨浏览器兼容性问题。

我不得不分成两部分发布它。希望这对其他人有所帮助!

/*
    Author: Chris Snowden
    Modified Date: 21st October 2011
    ReportingServices.js file for SQL Server 2008 R2 Reporting Services

    Updated to fix a bug whereby drop down context menus didn't work
    for any other browser than IE.

    1) I added functions to find firstChild and lastChild while skipping any
       whitespace TextNode elements.
    2) I updated the Clicked function to get at the table element value in a way
       that works for firefox.
    3) I updated the SplitContextMenuConfigString function to access the table
       cells by first looping through each row and then the cells.

    Drop downs now work on Firefox, Chrome, Opera and Safari as well as IE.
*/

var checkBoxCount;
var checkBoxId;
var checkBoxHead;

// Context menu
var _divContextMenu; // The container for the context menu
var _selectedIdHiddenField; // The id of the item that opened th context menu
var _timeOutLimit = 3000; // How long the context menu stays for after the cursor in no longer over it
var _timeOutTimer; // The timout for the context menu
var _itemSelected = false;
var _mouseOverContext = false; // If the mouse is over the context menu
var _contextMenusIds; // The array of the diffrent context menus
var _fadeTimeouts; // The array of timouts used for the fade effect
var _onLink = false; // If the user is over a name link
var _selectedItemId;
var _tabFocusedItem = '';
var _mouseOverItem = '';
var _unselectedItemStyle;
var _currentContextMenuId;  // ID of currently displayed context menu
var _currentMenuItemId = null;     // ID of currently selected context menu item

// Search bar
var _searchTextBoxID;
var _defaultSearchValue; // The value that the box defaults to.

// start chris edit
// new functions to find firstChild and lastChild but skipping whitespace elements
function firstChildNoWS(element) {
    var child = element.firstChild;
    while (child != null && child.isElementContentWhitespace) {
        child = child.nextSibling;
    }
    return child;
}

function lastChildNoWS(element) {
    var child = element.lastChild;
    while (child != null && child.isElementContentWhitespace) {
        child = child.previousSibling;
    }
    return child;
}
// end chris edit

function ToggleItem(itemId) {
    var item = document.getElementById(itemId);
    if (item.style.display == 'none')
        item.style.display = 'inline';
    else
        item.style.display = 'none';
}

function ToggleButtonImage(image1ID, image2ID) {
    var image1 = document.getElementById(image1ID);
    var image2 = document.getElementById(image2ID);
    if (image1.style.display == 'none') {
        image1.style.display = 'inline-block';
        image2.style.display = 'none';
    }
    else {
        image1.style.display = 'none';
        image2.style.display = 'inline-block';
    }
}

function SetFocus(id) {
    var obj = document.getElementById(id);
    if (obj != null && !obj.disabled)
        obj.focus();
}

// Validates that an extension has been selected
function ValidateDropDownSelection(source, args) {
    var obj = document.getElementById(source.controltovalidate);

    if (obj.options[0].selected && !obj.disabled)
        args.IsValid = false;
    else
        args.IsValid = true;
}

/// selectAll
/// selects all the checkBoxes with the given id
function selectAll() {
    var i;
    var id;
    var checked = checkBoxHead.checked;
    for (i = 0; i < checkBoxCount; i++) {
        id = checkBoxId + i;
        document.getElementById(id).checked = checked;
    }
}

/// onSglCheck
/// performs actions when a single checkBox is checked or unchecked
/// cb -> the checkBox generating the event
/// topId -> id of the "select all" checkBox
function onSglCheck() {
    // uncheck the top checkBox
    checkBoxHead.checked = false;
}

/// ToggleButton
/// Toggle a buttons enable state
function ToggleButton(id, disabled) {
    if (document.getElementById(id) != null)
        document.getElementById(id).disabled = disabled;
}

function ToggleValidator(id, enabled) {
    document.getElementById(id).enabled = enabled;
}

function SetCbVars(cbid, count, cbh) {
    checkBoxCount = count;
    checkBoxId = cbid;
    checkBoxHead = cbh;
}

/// Check to see if any check boxes should disable 
/// a control
/// cbid -> id prefix of the checkBoxes
/// cbCount -> total checkBoxes to check
/// hidden -> input to look for
/// display -> control to disable
function CheckCheckBoxes(cbid, hidden, display) {
    var i;
    var id;
    var disable;

    disable = false;
    for (i = 0; i < checkBoxCount; i++) {
        id = cbid + i;
        if (document.getElementById(id).checked) {
            id = hidden + id;
            if (document.getElementById(id) != null) {
                disable = true;
                break;
            }
        }
    }

    ToggleButton(display, disable);
}

function HiddenCheckClickHandler(hiddenID, promptID, promptStringID) {
    var hiddenChk = document.getElementById(hiddenID);
    var promptChk = document.getElementById(promptID);

    // prompt should be in opposite state of hidden
    promptChk.checked = !hiddenChk.checked;
}

function validateSaveRole(source, args) {
    var i;
    var id;
    var c = 0;
    for (i = 0; i < checkBoxCount; i++) {
        id = checkBoxId + i;
        if (document.getElementById(id).checked) c++;
    }
    if (0 == c)
        args.IsValid = false;
    else
        args.IsValid = true;
}

/// Pad an integer less then 10 with a leading zero
function PadIntWithZero(val) {
    var s = val.toString();

    if (val < 10 && val >= 0) {
        if (s.length == 1)
            s = "0" + s;
        else if (s.length > 2)
            s = s.substring(s.length - 2, s.length);
    }

    return s;
}

/// Pad the contents of an input with leading zeros if necesarry
function PadInputInteger(id) {
    document.getElementById(id).value = PadIntWithZero(document.getElementById(id).value);
}

/// text of confirmation popup when a single item is selected for deletion
/// e.g. "Are you sure you want to delete this item"
var confirmSingle;

/// text of confirmation popup when multiple items are selected for deletion
/// e.g. "Are you sure you want to delete these items"
var confirmMultiple;
function SetDeleteTxt(single, multiple) {
    confirmSingle = single;
    confirmMultiple = multiple;
}

/// doCmDel: DoConfirmDelete
/// Given a number of checked items, confirm their deletion
/// return true if OK was clicked; false otherwise
function doCmDel(checkedCount) {
    var confirmTxt = confirmSingle;
    if (checkedCount == 0)
        return false;

    if (checkedCount > 1)
        confirmTxt = confirmMultiple;
    return confirm(confirmTxt);
}

/// on non-Netscape browsers, confirm deletion of 0 or more items
function confirmDelete() {
    return doCmDel(getChkCount());
}

/// confirm deletion of policies
function confirmDeletePlcies(alertString) {
    var count = getChkCount();
    if (count >= checkBoxCount) {
        alert(alertString);
        return false;
    }
    return doCmDel(count);
}

/// counts whether 0, 1, or more than 1 checkboxes are checked
/// returns 0, 1, or 2
function getChkCount() {
    var checkedCount = 0;
    for (i = 0; i < checkBoxCount && checkedCount < 2; i++) {
        if (document.getElementById(checkBoxId + i).checked) {
            checkedCount++;
        }
    }
    return checkedCount;
}

function ToggleButtonBasedOnCheckBox(checkBoxId, toggleId, reverse) {
    var chkb = document.getElementById(checkBoxId);
    if (chkb != null) {
        if (chkb.checked == true)
            ToggleButton(toggleId, reverse); // enable if reverse == false
        else
            ToggleButton(toggleId, !reverse); // disable if reverse == false
    }
}

function ToggleButtonBasedOnCheckBoxWithOverride(checkBoxId, toggleId, overrideToDisabled, reverse) {
    if (overrideToDisabled == true)
        ToggleButton(toggleId, true); // disable
    else
        ToggleButtonBasedOnCheckBox(checkBoxId, toggleId, reverse);
}

function ToggleButtonBasedOnCheckBoxes(checkBoxId, checkboxId2, toggleId) {
    var chkb = document.getElementById(checkBoxId);
    if (chkb != null) {
        if (chkb.checked == true)
            ToggleButtonBasedOnCheckBox(checkboxId2, toggleId, false);
        else
            ToggleButton(toggleId, true); // disable
    }

}

function ToggleButtonBasedOnCheckBoxesWithOverride(checkBoxId, checkboxId2, toggleId, overrideToDisabled) {
    if (overrideToDisabled == true)
        ToggleButton(toggleId, true); // disable
    else
        ToggleButtonBasedOnCheckBoxes(checkBoxId, checkboxId2, toggleId);
}

function ToggleValidatorBasedOnCheckBoxWithOverride(checkBoxId, toggleId, overrideToDisabled, reverse) {
    if (overrideToDisabled == true)
        ToggleValidator(toggleId, false);
    else {
        var chkb = document.getElementById(checkBoxId);
        if (chkb != null) {
            ToggleValidator(toggleId, chkb.checked != reverse);
        }
    }
}

function ToggleValidatorBasedOnCheckBoxesWithOverride(checkBoxId, checkBoxId2, toggleId, overrideToDisabled, reverse) {
    if (overrideToDisabled == true)
        ToggleValidator(toggleId, false);
    else {
        var chkb = document.getElementById(checkBoxId);
        if (chkb != null) {
            if (chkb.checked == reverse)
                ToggleValidator(toggleId, false);
            else
                ToggleValidatorBasedOnCheckBoxWithOverride(checkBoxId2, toggleId, overrideToDisabled, reverse);
        }
    }
}

function CheckButton(buttonID, shouldCheck) {
    document.getElementById(buttonID).checked = shouldCheck;
}

function EnableMultiButtons(prefix) {
    // If there are no multibuttons, there is no reason to iterate the
    // list of checkboxes.
    if (checkBoxCount == 0 || multiButtonList.length == 0)
        return;

    var enableMultiButtons = false;
    var multipleCheckboxesSelected = false;

    // If the top level check box is checked, we know the state of all
    // of the checkboxes
    var headerCheckBox = document.getElementById(prefix + "ch");
    if (headerCheckBox != null && headerCheckBox.checked) {
        enableMultiButtons = true;
        multipleCheckboxesSelected = checkBoxCount > 1;
    }
    else {
        // Look at each checkbox.  If any one of them is checked,
        // enable the multi buttons.
        var foundOneChecked = false;
        var i;
        for (i = 0; i < checkBoxCount; i++) {
            var checkBox = document.getElementById(prefix + 'cb' + i);
            if (checkBox.checked) {               
                if (foundOneChecked) {
                    multipleCheckboxesSelected = true;
                    break;
                }
                else {
                    enableMultiButtons = true;
                    foundOneChecked = true;
                }
            }
        }
    }

    // Enable/disable each of the multi buttons 
    var j;
    for (j = 0; j < multiButtonList.length; j++) {
        var button = document.getElementById(multiButtonList[j]);
        if (button.allowMultiSelect)
            button.disabled = !enableMultiButtons;
        else
            button.disabled = !enableMultiButtons || multipleCheckboxesSelected;
    }
}

//function ShadowCopyPassword(suffix)
function MarkPasswordFieldChanged(suffix) {
    if (event.propertyName == "value") {
        var pwdField = document.getElementById("ui_txtStoredPwd" + suffix);
        //var shadowField = document.getElementById("ui_shadowPassword" + suffix);
        var shadowChanged = document.getElementById("ui_shadowPasswordChanged" + suffix);

        // Don't shadow copy during initialization
        if (pwdField.IsInit) {
            //shadowField.value = pwdField.value;
            //pwdField.UserEnteredPassword = "true";
            shadowChanged.value = "true";

            // Update validator state (there is no validator on the data driven subscription page)
            var validator = document.getElementById("ui_validatorPassword" + suffix)
            if (validator != null)
                ValidatorValidate(validator);
        }
    }
}

function InitDataSourcePassword(suffix) {
    var pwdField = document.getElementById("ui_txtStoredPwd" + suffix);
    var shadowChanged = document.getElementById("ui_shadowPasswordChanged" + suffix);
    //  var shadowField = document.getElementById("ui_shadowPassword" + suffix);
    var storedRadioButton = document.getElementById("ui_rdoStored" + suffix);
    var pwdValidator = document.getElementById("ui_validatorPassword" + suffix);

    pwdField.IsInit = false;

    // Initialize the field to the shadow value (for when the user clicks back/forward)
    // Or to a junk initial value.
    if (pwdValidator != null && storedRadioButton.checked) {
        /*      if (shadowField.value.length > 0)
        pwdField.value = shadowField.value;
        else*/
        pwdField.value = "********";
    }
    else
        shadowChanged.value = "true"; // shadowChanged will be ignored if the page is submitted without storedRadioButton.checked

    // Now that the initial value is set, track changes to the password field
    pwdField.IsInit = true;

    // There is no validator on the data driven subscription page (no stored radio button either)
    if (pwdValidator != null)
        ValidatorValidate(pwdValidator);
}

function SetNeedPassword(suffix) {
    // Set a flag indicating that we need the password
    var pwdField = document.getElementById("ui_txtStoredPwd" + suffix);
    pwdField.NeedPassword = "true";

    // Make the validator visible
    ValidatorValidate(document.getElementById("ui_validatorPassword" + suffix));
}

function UpdateValidator(src, validatorID) {
    if (src.checked) {
        var validator = document.getElementById(validatorID);
        ValidatorValidate(validator);
    }
}

function ReEnterPasswordValidation(source, arguments) // source = validator
{
    var validatorIdPrefix = "ui_validatorPassword"
    var suffix = source.id.substr(validatorIdPrefix.length, source.id.length - validatorIdPrefix.length);

    var storedRadioButton = document.getElementById("ui_rdoStored" + suffix);
    var pwdField = document.getElementById("ui_txtStoredPwd" + suffix);
    var shadowChanged = document.getElementById("ui_shadowPasswordChanged" + suffix);

    var customDataSourceRadioButton = document.getElementById("ui_rdoCustomDataSource" + suffix);
    var isCustomSelected = true;
    if (customDataSourceRadioButton != null)
        isCustomSelected = customDataSourceRadioButton.checked;

    if (!isCustomSelected ||                    // If the custom (vs shared) data source radio button exists and is not selected, we don't need the pwd.
        storedRadioButton.checked == false ||   // If the data source is not using stored credentials, we don't need the password
        pwdField.UserEnteredPassword == "true" || // If the password has changed, we don't need to get it from the user
        pwdField.NeedPassword != "true" ||      // If no credentials have changed, we don't need the password
        shadowChanged.value == "true")              // If the user has typed a password
        arguments.IsValid = true;
    else
        arguments.IsValid = false;
}

function ValidateDataSourceSelected(source, arguments) {
    var validatorIdPrefix = "ui_sharedDSSelectedValidator"
    var suffix = source.id.substr(validatorIdPrefix.length, source.id.length - validatorIdPrefix.length);

    var sharedRadioButton = document.getElementById("ui_rdoSharedDataSource" + suffix);
    var hiddenField = document.getElementById("ui_hiddenSharedDS" + suffix);

    arguments.IsValid = (sharedRadioButton != null && !sharedRadioButton.checked) || hiddenField.value != "NotSelected";
}



/**************************************************************************/
// MultiValueParamClass
function MultiValueParamClass(thisID, visibleTextBoxID, floatingEditorID, floatingIFrameID, paramObject,
                              hasValidValues, allowBlank, doPostbackOnHide, postbackScript) {
    this.m_thisID = thisID;
    this.m_visibleTextBoxID = visibleTextBoxID;
    this.m_floatingEditorID = floatingEditorID;
    this.m_floatingIFrameID = floatingIFrameID;
    this.m_paramObject = paramObject;
    this.m_hasValidValues = hasValidValues;
    this.m_allowBlank = allowBlank;
    this.m_doPostbackOnHide = doPostbackOnHide;
    this.m_postbackScript = postbackScript;

    this.UpdateSummaryString();
}

function ToggleVisibility() {
    var floatingEditor = GetControl(this.m_floatingEditorID);
    if (floatingEditor.style.display != "inline")
        this.Show();
    else
        this.Hide();
}
MultiValueParamClass.prototype.ToggleVisibility = ToggleVisibility;

function Show() {
    var floatingEditor = GetControl(this.m_floatingEditorID);
    if (floatingEditor.style.display == "inline")
        return;

    // Set the correct size of the floating editor - no more than
    // 150 pixels high and no less than the width of the text box
    var visibleTextBox = GetControl(this.m_visibleTextBoxID);
    if (this.m_hasValidValues) {
        if (floatingEditor.offsetHeight > 150)
            floatingEditor.style.height = 150;
        floatingEditor.style.width = visibleTextBox.offsetWidth;
    }

    var newEditorPosition = this.GetNewFloatingEditorPosition();
    floatingEditor.style.left = newEditorPosition.Left;
    floatingEditor.style.top = newEditorPosition.Top;
    floatingEditor.style.display = "inline";

    var floatingIFrame = GetControl(this.m_floatingIFrameID);
    floatingIFrame.style.left = floatingEditor.style.left;
    floatingIFrame.style.top = floatingEditor.style.top;
    floatingIFrame.style.width = floatingEditor.offsetWidth;
    floatingIFrame.style.height = floatingEditor.offsetHeight;
    floatingIFrame.style.display = "inline";

    // If another multi value is open, close it first
    if (this.m_paramObject.ActiveMultValue != this && this.m_paramObject.ActiveMultiValue != null)
        ControlClicked(this.m_paramObject.id);
    this.m_paramObject.ActiveMultiValue = this;

    if (floatingEditor.childNodes[0].focus) floatingEditor.childNodes[0].focus();
    this.StartPolling();
}
MultiValueParamClass.prototype.Show = Show;

function Hide() {
    var floatingEditor = GetControl(this.m_floatingEditorID);
    var floatingIFrame = GetControl(this.m_floatingIFrameID);

    // Hide the editor
    floatingEditor.style.display = "none";
    floatingIFrame.style.display = "none";
    this.UpdateSummaryString();

    if (this.m_doPostbackOnHide)
        eval(this.m_postbackScript);

    // Check that the reference is still us in case event ordering
    // caused another multivalue to click open
    if (this.m_paramObject.ActiveMultiValue == this)
        this.m_paramObject.ActiveMultiValue = null;
}
MultiValueParamClass.prototype.Hide = Hide;

function GetNewFloatingEditorPosition() {
    // Make the editor visible
    var visibleTextBox = GetControl(this.m_visibleTextBoxID);
    var textBoxPosition = GetObjectPosition(visibleTextBox);

    return { Left: textBoxPosition.Left, Top: textBoxPosition.Top + visibleTextBox.offsetHeight };
}
MultiValueParamClass.prototype.GetNewFloatingEditorPosition = GetNewFloatingEditorPosition;

function UpdateSummaryString() {
    var summaryString;

    if (this.m_hasValidValues)
        summaryString = GetValueStringFromValidValueList(this.m_floatingEditorID);
    else
        summaryString = GetValueStringFromTextEditor(this.m_floatingEditorID, false, this.m_allowBlank);

    var visibleTextBox = GetControl(this.m_visibleTextBoxID);
    visibleTextBox.value = summaryString;
}
MultiValueParamClass.prototype.UpdateSummaryString = UpdateSummaryString;

function StartPolling() {
    setTimeout(this.m_thisID + ".PollingCallback();", 100);
}
MultiValueParamClass.prototype.StartPolling = StartPolling;

function PollingCallback() {
    // If the editor isn't visible, no more events.
    var floatingEditor = GetControl(this.m_floatingEditorID);
    if (floatingEditor.style.display != "inline")
        return;

    // If the text box moved, something on the page resized, so close the editor
    var expectedEditorPos = this.GetNewFloatingEditorPosition();
    if (floatingEditor.style.left != expectedEditorPos.Left + "px" ||
        floatingEditor.style.top != expectedEditorPos.Top + "px") {
        this.Hide();
    }
    else {
        this.StartPolling();
    }
}
MultiValueParamClass.prototype.PollingCallback = PollingCallback;
/*****************************************************************************/

function GetObjectPosition(obj) {
    var totalTop = 0;
    var totalLeft = 0;
    while (obj != document.body) {
        // Add up the position
        totalTop += obj.offsetTop;
        totalLeft += obj.offsetLeft;

        // Prepare for next iteration
        obj = obj.offsetParent;
    }

    totalTop += obj.offsetTop;
    totalLeft += obj.offsetLeft;

    return { Left: totalLeft, Top: totalTop };
}

function GetValueStringFromTextEditor(floatingEditorID, asRaw, allowBlank) {
    var span = GetControl(floatingEditorID);
    var editor = span.childNodes[0];

    var valueString = editor.value;

    // Remove the blanks
    if (!allowBlank) {
        // Break down the text box string to the individual lines
        var valueArray = valueString.split("\r\n");

        var delimiter;
        if (asRaw)
            delimiter = "\r\n";
        else
            delimiter = ", ";

        var finalValue = "";
        for (var i = 0; i < valueArray.length; i++) {
            // If the string is non-blank, add it
            if (valueArray[i].length > 0) {
                if (finalValue.length > 0)
                    finalValue += delimiter;
                finalValue += valueArray[i];
            }
        }

        return finalValue;
    }
    else {
        if (asRaw)
            return valueString;
        else
            return valueString.replace(/\r\n/g, ", ");
    }
}

function GetValueStringFromValidValueList(editorID) {
    var valueString = "";

    // Get the table
    var div = GetControl(editorID);
    var table = div.childNodes[0];
    if (table.nodeName != "TABLE")  // Skip whitespace if needed
        table = div.childNodes[1];

    // If there is only one element, it is a real value, not the select all option
    var startIndex = 0;
    if (table.rows.length > 1)
        startIndex = 1;

    for (var i = startIndex; i < table.rows.length; i++)
    {
        // Get the first cell of the row
        var firstCell = table.rows[i].cells[0];
        var span = firstCell.childNodes[0];

        var checkBox = span.childNodes[0];
        var label = span.childNodes[1];

        if (checkBox.checked) {
            if (valueString.length > 0)
                valueString += ", ";
            // chris edit - valueString += label.firstChild.nodeValue;
            valueString += firstChildNoWS(label).nodeValue;
        }
    }

    return valueString;
}

function MultiValidValuesSelectAll(src, editorID)
{
    // Get the table
    var div = GetControl(editorID);
    var table = div.childNodes[0];
    if (table.nodeName != "TABLE")
        table = div.childNodes[1];

    for (var i = 1; i < table.rows.length; i++)
    {
        // Get the first cell of the row
        var firstCell = table.rows[i].cells[0];
        var span = firstCell.childNodes[0];

        var checkBox = span.childNodes[0];
        checkBox.checked = src.checked;
    }
}

function ValidateMultiValidValue(editorID, errMsg)
{
    var summaryString = GetValueStringFromValidValueList(editorID);
    var isValid = summaryString.length > 0;
    if (!isValid)
        alert(errMsg)

    return isValid;
}

function ValidateMultiEditValue(editorID, errMsg) {
    // Need to check for a value specified.  This code only runs if not allow blank.
    // GetValueStringFromTextEditor filters out blank strings.  So if it was all blank,
    // the final string will be length 0
    var summaryString = GetValueStringFromTextEditor(editorID, true, false)

    var isValid = false;
    if (summaryString.length > 0)
        isValid = true;

    if (!isValid)
        alert(errMsg);

    return isValid;
}

function GetControl(controlID) {
    var control = document.getElementById(controlID);
    if (control == null)
        alert("Unable to locate control: " + controlID);

    return control;
}

function ControlClicked(formID) {
    var form = GetControl(formID);

    if (form.ActiveMultiValue != null)
        form.ActiveMultiValue.Hide();
}

很抱歉,如果回答你的问题是“请阅读我发布的这本书”,那么这就不是 SO 上一个好的问题/回答。 - Lasse V. Karlsen
3
我已经自己解决了。只是想与其他人分享解决方法。您有任何建议应该把解决方案放在哪里吗? - Chris Snowden
2
很可能会这样做。我最初发布了一个问题,希望得到一个修复链接,但最终我自己解决了它,并想分享解决方案,不幸的是需要对原始文件进行大量编辑。 - Chris Snowden
1
看起来这个固定的.js文件也适用于SSRS 2012中的ReportManager。Firebug在“function Clicked(event, contextMenuId)”中的最后一个语句报告了一个问题(“TypeError: firstChildNoWS(...).focus不是一个函数”)。当您单击鼠标悬停在报告/文件夹上方时出现。但它似乎不会对功能产生不利影响,所以我还没有担心它。考虑到股票SSRS 2012在FireFox中比SSRS 2008更少功能,我不明白为什么有人可以在没有Chris的修复程序的情况下使用该软件。 - Granger

12

以下是我答案中更新后的ReportingServices.js的第二部分:

// --- Context Menu ---

// This function is called in the onload event of the body.
// It hooks the context menus up to the Javascript code.
//      divContextMenuId, is the id of the div that contains the context menus
//      selectedIdHiddenFieldId, is the id of the field used to post back the name of the item clicked
//      contextMenusIds, is an array of the ids of the context menus
//      searchTextBox ID, is the id of the search box
//      defaultSearchValue. the value the search box has by default
function InitContextMenu(divContextMenuId, selectedIdHiddenFieldId, contextMenusIds, searchTextBoxID, defaultSearchValue ) {

    ResetSearchBar( searchTextBoxID, defaultSearchValue );

    _divContextMenu = document.getElementById(divContextMenuId);
    _selectedIdHiddenField = document.getElementById(selectedIdHiddenFieldId);
    _contextMenusIds = contextMenusIds;
    _divContextMenu.onmouseover = function() { _mouseOverContext = true; };
    _divContextMenu.onmouseout = function() {
        if (_mouseOverContext == true) {
            _mouseOverContext = false;

            if (_timeOutTimer == null) {
                _timeOutTimer = setTimeout(TimeOutAction, _timeOutLimit);
            }
        }

    };

    document.body.onmousedown = ContextMouseDown;
    AddKeyDownListener();
}

// This handler stops bubling when arrow keys Up or Down pressed to prevent scrolling window
function KeyDownHandler(e)
{   
    // Cancel window scrolling only when menu is opened
    if(_currentContextMenuId == null)
    {
        return true;
    }

    if(!e) 
    { 
        e = window.event; 
    }

    var key = e.keyCode;

    if(key == 38 || key == 40) 
    { 
        return false; 
    }
    else 
    { 
        return true; 
    }
}

function AddKeyDownListener()
{
    if(document.addEventListener)
    {
        document.addEventListener('keydown', KeyDownHandler, false);
    }
    else
    {
        document.onkeydown = KeyDownHandler;
    }
}

// This function starts the context menu timeout process
function TimeOutAction() {
    if (_mouseOverContext == false) {

        UnSelectedMenuItem()
    }
    _timeOutTimer = null;
}

// This function is called when a name tag is clicked, it displays the contextmenu for a given item.
function Clicked(event, contextMenuId) {

    if (!_onLink) {

        ClearTimeouts();
        SelectContextMenuFromColletion(contextMenuId);

        _itemSelected = true;

        // **Cross browser compatibility code**
        // Some browsers will not pass the event so we need to get it from the window instead.
        if (event == null)
            event = window.event;

        var selectedElement = event.target != null ? event.target : event.srcElement;
        var outerTableElement = GetOuterElementOfType(selectedElement, 'table');
        var elementPosition = GetElementPosition(outerTableElement);

        _selectedItemId = outerTableElement.id;
        // chris edit - _selectedIdHiddenField.value = outerTableElement.value;
        _selectedIdHiddenField.value = outerTableElement.attributes["value"].value;
        outerTableElement.className = "msrs-SelectedItem";

        ResetContextMenu();

        var contextMenuHeight = _divContextMenu.offsetHeight;
        var contextMenuWidth = _divContextMenu.offsetWidth;

        var boxHeight = outerTableElement.offsetHeight;
        var boxWidth = outerTableElement.offsetWidth;
        var boxXcoordinate = elementPosition.left;
        var boxYcooridnate = elementPosition.top;

        var pageWidth = 0, pageHeight = 0;
        // **Cross browser compatibility code**
        if (typeof (window.innerWidth) == 'number') {
            //Non-IE
            pageWidth = window.innerWidth;
            pageHeight = window.innerHeight;
        } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
            //IE 6+ in 'standards compliant mode'
            pageWidth = document.documentElement.clientWidth;
            pageHeight = document.documentElement.clientHeight;
        } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
            //IE 4 compatible
            pageWidth = document.body.clientWidth;
            pageHeight = document.body.clientHeight;
        }

        // **Cross browser compatibility code**
        var iebody = (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body
        var pageXOffSet = document.all ? iebody.scrollLeft : pageXOffset
        var pageYOffSet = document.all ? iebody.scrollTop : pageYOffset

        _divContextMenu.style.left = SetContextMenuHorizonatalPosition(pageWidth, pageXOffSet, boxXcoordinate, contextMenuWidth, boxWidth) + 'px';
        _divContextMenu.style.top = SetContextMenuVerticalPosition(pageHeight, pageYOffSet, boxYcooridnate, contextMenuHeight, boxHeight) + 'px';

        ChangeOpacityForElement(100, _divContextMenu.id);

        // chris edit - document.getElementById(_currentContextMenuId).firstChild.focus();
        firstChildNoWS(document.getElementById(_currentContextMenuId)).focus();
    }
}

// ***********************************
// Context menu keyboard navigation
// ***********************************

// Opens context menu via keyboard.  Context menu
// is opened by selecting an item and pressing
// Alt + Down.
function OpenMenuKeyPress(e, contextMenuId)
{
    // Alt key was pressed
    if (e.altKey)
    {
        var keyCode;

        if (window.event)
            keyCode = e.keyCode;
        else
            keyCode = e.which;

        // Down key was pressed
        if (keyCode == 40)
        {
            // Open context menu.
            Clicked(event, contextMenuId);

            // Highlight the first selectable item 
            // in the context menu.
            HighlightContextMenuItem(true);
        }
    }
}

// Performs keyboard navigation within
// opened context menu.
function NavigateMenuKeyPress(e)
{
    var keyCode;

    if (window.event)
        keyCode = e.keyCode;
    else
        keyCode = e.which;

    // Down key moves down to the next context menu item
    if (keyCode == 40)
    {            
        HighlightContextMenuItem(true);
    }

    // Up key moves up to the previous context menu item
    else if (keyCode == 38)
    {
        HighlightContextMenuItem(false);
    } 

    // Escape key closes context menu
    else if (keyCode == 27)
    {
        // Close context menu
        UnSelectedMenuItem();

        // Make sure focus is given to the catalog item
        // in the folder view.
        document.getElementById(_selectedItemId).focus();
    }        
}

// Highlights context menu item.
// Parameter:  highlightNext
// - If true, highlights menu item below current menu item.
//   If current menu item is the last item, wraps around and
//   highlights first menu item.
// - If false, highlights menu item above current menu item.
//   If current menu item is the first item, wraps around and
//   highlights last menu item.
function HighlightContextMenuItem(highlightNext)
{
    var contextMenu = document.getElementById(_currentContextMenuId);
    // chris edit - var table = contextMenu.lastChild;
    var table = lastChildNoWS(contextMenu);

    var currentMenuItemIndex = -1;
    if (_currentMenuItemId != null)
        currentMenuItemIndex = document.getElementById(_currentMenuItemId).parentNode.rowIndex;

    var index = currentMenuItemIndex;
    while (true)
    {
        if (highlightNext)
        {
            index++;

            // If the index is out of range,
            // reset it to the beginning
            if (index < 0 || index >= table.cells.length)
                index = 0;
        }
        else
        {
            index--;

            // If the index is out of range,
            // reset it to the end
            if (index < 0 || index >= table.cells.length)
                index = table.cells.length - 1;
        }

        // Each context menu item has an associated
        // group ID.  Make sure the table cell has a valid
        // group ID, otherwise it is not a menu item (e.g.
        // an underline separator).
        if (table.cells[index].group >= 0)
        {
            FocusContextMenuItem(table.cells[index].id, 'msrs-MenuUIItemTableHover', 'msrs-MenuUIItemTableCell');
            break;
        }

        // If we reach the orignal index, that means we looped
        // through all table cells and did not find a valid context
        // menu item.  In that case, stop searching.
        if (index == currentMenuItemIndex)
            break;
    }
}

// *** End keyboard navigation ***

// This function resets the context menus shape and size.
function ResetContextMenu() {
    _divContextMenu.style.height = 'auto';
    _divContextMenu.style.width = 'auto';
    _divContextMenu.style.overflowY = 'visible';
    _divContextMenu.style.overflowX = 'visible';
    _divContextMenu.style.overflow = 'visible';
    _divContextMenu.style.display = 'block';
}

// This function sets the horizontal position of the context menu.
// It also sets is the context menu has vertical scroll bars.
function SetContextMenuHorizonatalPosition(pageWidth, pageXOffSet, boxXcoordinate, contextMenuWidth, boxWidth) {

    var menuXCoordinate = boxXcoordinate + boxWidth - contextMenuWidth;
    var spaceRightBox = (pageWidth + pageXOffSet) - menuXCoordinate;
    var spaceLeftBox = menuXCoordinate - pageXOffSet;
    var returnValue;

    if ((contextMenuWidth < spaceRightBox) && (pageXOffSet < menuXCoordinate)) {
        returnValue = menuXCoordinate;
    }
    else if ((contextMenuWidth < spaceRightBox)) {
        returnValue = pageXOffSet;
    }
    else if (contextMenuWidth < spaceLeftBox) {
        returnValue = menuXCoordinate - (contextMenuWidth - (pageWidth + pageXOffSet - menuXCoordinate));
    }
    else {
        _divContextMenu.style.overflowX = "scroll";
        if (spaceLeftBox < spaceRightBox) {
            _divContextMenu.style.width = spaceRightBox;
            returnValue = pageXOffSet;
        }
        else {
            _divContextMenu.style.width = spaceLeftBox;
            returnValue = menuXCoordinate - (spaceLeftBox - (pageWidth + pageXOffSet - menuXCoordinate));
        }
    }

    return returnValue;
}

// This function sets the vertical position of the context menu.
// It also sets is the context menu has horizontal scroll bars.
function SetContextMenuVerticalPosition(pageHeight, pageYOffSet, boxYcooridnate, contextMenuHeight, boxHeight) {

    var spaceBelowBox = (pageHeight + pageYOffSet) - (boxYcooridnate + boxHeight);
    var spaceAboveBox = boxYcooridnate - pageYOffSet;
    var returnValue;

    if (contextMenuHeight < spaceBelowBox) {
        returnValue = (boxYcooridnate + boxHeight);
    }
    else if (contextMenuHeight < spaceAboveBox) {
        returnValue = (boxYcooridnate - contextMenuHeight);
    }
    else if (spaceBelowBox > spaceAboveBox) {
        _divContextMenu.style.height = spaceBelowBox;
        _divContextMenu.style.overflowY = "scroll";
        returnValue = (boxYcooridnate + boxHeight);
    }
    else {
        _divContextMenu.style.height = spaceAboveBox;
        _divContextMenu.style.overflowY = "scroll";
        returnValue = (boxYcooridnate - spaceAboveBox);
    }

    return returnValue;
}

// This function displays a context menu given its id and then hides the others
function SelectContextMenuFromColletion(contextMenuConfigString) {

    var contextMenuId = SplitContextMenuConfigString(contextMenuConfigString);

    for (i = 0; i < _contextMenusIds.length; i++) {
        var cm = document.getElementById(_contextMenusIds[i]);
        if (cm.id == contextMenuId) {
            cm.style.visibility = 'visible';
            cm.style.display = 'block';
            _currentContextMenuId = contextMenuId;
        }
        else {
            cm.style.visibility = 'hidden';
            cm.style.display = 'none';
        }
    }
}

function SplitContextMenuConfigString(contextMenuConfigString) {

    var contextMenuEnd = contextMenuConfigString.indexOf(":");
    var contextMenuId = contextMenuConfigString;
    var contextMenuHiddenItems;

    if (contextMenuEnd != -1)
    {
        contextMenuId = contextMenuConfigString.substr(0, contextMenuEnd);
    }

    var cm = document.getElementById(contextMenuId);
    // chris edit - var table = cm.firstChild;
    var table = firstChildNoWS(cm);
    var groupItemCount = []; // The items in each group
    var groupUnderlineId = []; // The Id's of the underlines.

    // Enable all menu items counting the number of groups, 
    // number of items in the groups and underlines for the groups as we go.
    // start chris edit
    /* for (i = 0; i < table.cells.length; i++) 
    {
        table.cells[i].style.visibility = 'visible';
        table.cells[i].style.display = 'block'

        if ((groupItemCount.length - 1) < table.cells[i].group) {
            groupItemCount.push(1);
            groupUnderlineId.push(table.cells[i].underline);
        }
        else {
            groupItemCount[table.cells[i].group]++;
        }

        AlterVisibilityOfAssociatedUnderline(table.cells[i], true)
    }*/
    if (table != null && table.rows != null)
    {
        for (r = 0; r < table.rows.length; r++) {
            for (i = 0; i < table.rows[r].cells.length; i++) 
            {
                table.rows[r].cells[i].style.visibility = 'visible';
                table.rows[r].cells[i].style.display = 'block'

                if ((groupItemCount.length - 1) < table.rows[r].cells[i].group) {
                    groupItemCount.push(1);
                    groupUnderlineId.push(table.rows[r].cells[i].underline);
                }
                else {
                    groupItemCount[table.rows[r].cells[i].group]++;
                }

                AlterVisibilityOfAssociatedUnderline(table.rows[r].cells[i], true)
            }
        }
    }
    // end chris edit

    // If hidden items are listed, remove them from the context menu
    if (contextMenuEnd != -1) 
    {            
        contextMenuHiddenItems = contextMenuConfigString.substr((contextMenuEnd + 1), (contextMenuConfigString.length - 1)).split("-");
        var groupsToHide = groupItemCount;

        // Hide the hidden items          
        for (i = 0; i < contextMenuHiddenItems.length; i++) 
        {           
            var item = document.getElementById(contextMenuHiddenItems[i]);

            item.style.visibility = 'hidden';
            item.style.display = 'none'

            groupsToHide[item.group]--;
        }

        var allHidden = true;

        // Work back through the groups hiding the underlines as required.
        for (i = (groupsToHide.length - 1); i > -1; i--) {
            if (groupsToHide[i] == 0) {
                AlterVisibilityOfAssociatedUnderline(groupUnderlineId[i], false);
            }
            else if (allHidden && i == (groupsToHide.length - 1)) {
                allHidden = false;
            }
            // If all the items have been hidden so far hide the last underline too.
            else if (allHidden) {
                allHidden = false;
                AlterVisibilityOfAssociatedUnderline(groupUnderlineId[i], false);
            }
        }
    }

    return contextMenuId;
}

function AlterVisibilityOfAssociatedUnderline(underLineId, visibility) {

    if (underLineId != null && underLineId != "") {

        var underlineElement = document.getElementById(underLineId);

        if (underlineElement != null) {

            if (visibility) {
                underlineElement.style.visibility = 'visible';
                underlineElement.style.display = 'block'
            }
            else {
                underlineElement.style.visibility = 'hidden';
                underlineElement.style.display = 'none'
            }
        }
    }
}

function ClearTimeouts() {
    if (_fadeTimeouts != null) {
        for (i = 0; i < _fadeTimeouts.length; i++) {
            clearTimeout(_fadeTimeouts[i]);
        }
    }
    _fadeTimeouts = [];
}

// This function chnages an elements opacity given its id.
function FadeOutElement(id, opacStart, opacEnd, millisec) {

    ClearTimeouts();
    //speed for each frame 
    var speed = Math.round(millisec / 100);
    var timer = 0;

    for (i = opacStart; i >= opacEnd; i--) {
        _fadeTimeouts.push(setTimeout("ChangeOpacityForElement(" + i + ",'" + id + "')", (timer * speed)));
        timer++;
    }
}

// This function changes the opacity of an elemnent given it's id.
// Works across browsers for different browsers
function ChangeOpacityForElement(opacity, id) {
    var object = document.getElementById(id).style;
    if (opacity != 0) {
        // **Cross browser compatibility code**
        object.opacity = (opacity / 100);
        object.MozOpacity = (opacity / 100);
        object.KhtmlOpacity = (opacity / 100);
        object.filter = "alpha(opacity=" + opacity + ")";
    }
    else {
        object.display = 'none';
    }
}

// This function is the click for the body of the document 
function ContextMouseDown() {

    if (_mouseOverContext) {
        return;
    }
    else {
        HideMenu()
    }
}

// This function fades out the context menu and then unselects the associated name control
function UnSelectedMenuItem() {
    if (_itemSelected) {

        FadeOutElement(_divContextMenu.id, 100, 0, 300);
        UnselectCurrentMenuItem();
    }
}

// Hides context menu without fading effect
function HideMenu()
{
    if (_itemSelected)
    {
        ChangeOpacityForElement(0, _divContextMenu.id);
        UnselectCurrentMenuItem();
    }
}

function UnselectCurrentMenuItem()
{
        _itemSelected = false;
        _currentContextMenuId = null;
        SwapStyle(_currentMenuItemId, 'msrs-MenuUIItemTableCell');
        _currentMenuItemId = null;
        ChangeReportItemStyle(_selectedItemId, "msrs-UnSelectedItem");
}

// This function walks back up the DOM tree until it finds the first occurrence
// of a given element. It then returns this element
function GetOuterElementOfType(element, type) {
    while (element.tagName.toLowerCase() != type) {

        element = element.parentNode;
    }
    return element;
}

// This function gets the corrdinates of the top left corner of a given element
function GetElementPosition(element) {
    element = GetOuterElementOfType(element, 'table');

    var left, top;
    left = top = 0;
    if (element.offsetParent) {
        do {
            left += element.offsetLeft;
            top += element.offsetTop;
        } while (element = element.offsetParent);
    }
    return { left: left, top: top };
}

function FocusContextMenuItem(menuItemId, focusStyle, blurStyle)
{
    SwapStyle(_currentMenuItemId, blurStyle);
    SwapStyle(menuItemId, focusStyle);
    // chrid edit - document.getElementById(menuItemId).firstChild.focus();
    firstChildNoWS(document.getElementById(menuItemId)).focus();
    _currentMenuItemId = menuItemId;
}

// This function swaps the style using the id of a given element 
function SwapStyle(id, style) {
    if (document.getElementById) {
        var selectedElement = document.getElementById(id);
        if (selectedElement != null)
        {
            selectedElement.className = style;
        }
    }
}

// This function changes the style using the id of a given element
// and should only be called for catalog items in the tile or details view
function ChangeReportItemStyle(id, style) 
{
    if (!_itemSelected) 
    {
        if (document.getElementById) 
        {
            var selectedElement = document.getElementById(id);
            selectedElement.className = style;
            // Change the style on the end cell by drilling into the table.
            if (selectedElement.tagName.toLowerCase() == "table") 
            {
                // chris edit - var tbody = selectedElement.lastChild;
                var tbody = lastChildNoWS(selectedElement);
                if (tbody != null) 
                {
                    // chris edit - var tr = tbody.lastChild;
                    var tr = lastChildNoWS(tbody);
                    if (tr != null)
                    {
                        // chris edit - tr.lastChild.className = style + 'End';
                        trLastChild = lastChildNoWS(tr);
                        if (trLastChild != null)
                        {
                            trLastChild.className = style + 'End';
                        }
                    }
                }
            }

        }
    }
}

function ChangeReportItemStyleOnFocus(id, currentStyle, unselectedStyle)
{
    _unselectedItemStyle = unselectedStyle;
    _tabFocusedItem = id;

    // We should unselect selected by mouse over item if there is one
    if(_mouseOverItem != '')
    {
        ChangeReportItemStyle(_mouseOverItem, _unselectedItemStyle);
        _mouseOverItem = '';
    }

    ChangeReportItemStyle(id, currentStyle);
}

function ChangeReportItemStyleOnBlur(id, style)
{    
    ChangeReportItemStyle(id, style);
    _tabFocusedItem = '';
}

function ChangeReportItemStyleOnMouseOver(id, currentStyle, unselectedStyle)
{
    _unselectedItemStyle = unselectedStyle;
    _mouseOverItem = id;

    // We should unselect tabbed item if there is one
    if(_tabFocusedItem != '')
    {
        ChangeReportItemStyle(_tabFocusedItem, _unselectedItemStyle);
        _tabFocusedItem = '';
    }

    ChangeReportItemStyle(id, currentStyle);
}

function ChangeReportItemStyleOnMouseOut(id, style)
{    
    ChangeReportItemStyle(id, style);
    _mouseOverItem = '';
}

// This function is used to set the style of the search bar on the onclick event.
function SearchBarClicked(id, defaultText, style) {
    var selectedElement = document.getElementById(id);
    if (selectedElement.value == defaultText) {
        selectedElement.value = "";
        selectedElement.className = style;
    }
}

// This function is used to set the style of the search bar on the onblur event.
function SearchBarBlured(id, defaultText, style) {
    var selectedElement = document.getElementById(id);
    if (selectedElement.value == "") {
        selectedElement.value = defaultText;
        selectedElement.className = style;
    }
}

function ResetSearchBar(searchTextBoxID,defaultSearchValue) {

    var selectedElement = document.getElementById(searchTextBoxID);
    if (selectedElement != null) {
        if (selectedElement.value == defaultSearchValue) {
            selectedElement.className = 'msrs-searchDefaultFont';
        }
        else {
            selectedElement.className = 'msrs-searchBarNoBorder';
        }
    }
}

function OnLink() 
{
    _onLink = true;     
}

function OffLink() 
{
    _onLink = false;
}

function ShouldDelete(confirmMessage) {
    if (_selectedIdHiddenField.value != null || _selectedIdHiddenField.value != "") {
        var message = confirmMessage.replace("{0}", _selectedIdHiddenField.value);
        var result = confirm(message);
        if (result == true) {
            return true;
        }
        else {
            return false;
        }
    }
    else {
        return false;
    }
}

function UpdateValidationButtonState(promptCredsRdoBtnId, typesDropDownId, forbiddenTypesConfigString, validateButtonId)
{
    var dropdown = document.getElementById(typesDropDownId);

    if(dropdown == null)
    {
        return;
    }

    var selectedValue = dropdown.options[dropdown.selectedIndex].value;    
    var forbiddenTypes = forbiddenTypesConfigString.split(":");

    var chosenForbiddenType = false;

    for (i = 0; i < forbiddenTypes.length; i++)
    {
        if(forbiddenTypes[i] == selectedValue) 
        {
            chosenForbiddenType = true;
        }   
    }

    var isDisabled = chosenForbiddenType || IsRadioButtonChecked(promptCredsRdoBtnId);

    ChangeDisabledButtonState(validateButtonId, isDisabled);
}

function ChangeDisabledButtonState(buttonId, isDisabled)
{
    var button = document.getElementById(buttonId);

    if(button != null)
    {
        button.disabled = isDisabled;
    }
}

function IsRadioButtonChecked(radioButtonId)
{
    var rbtn = document.getElementById(radioButtonId);

    if(rbtn != null && rbtn.checked)
    {
        return true;
    }

    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接