/* IBM Confidential OCO Source Materials IBM Cognos Products: authoring (C) Copyright IBM Corp. 2015, 2022 The source code for this program is not published or otherwise divested of its trade secrets, irrespective of what has been deposited with the U.S. Copyright Office. */ define( [ 'bi/glass/app/ContentView', 'jquery', 'q', 'bi/classicviewer/nls/StringResource', 'bi/commons/utils/Utils', 'bi/authoring/utils/pat/rsLaunchParameters', 'bi/authoring/utils/pat/rsPromptParameters', 'bi/authoring/utils/rsPerformance', 'bi/authoring/utils/rsCommon', 'bi/authoring/utils/rsOpenHelper', 'bi/authoring/utils/rsShareHelper', 'bi/authoring/utils/rsPromptHandler', 'bi/admin/common/utils/parameters/ParameterValues'], function(ContentView, $, Q, StringResource, Utils, rsLaunchParameters, rsPromptParameters, rsPerformance, rsCommon, rsOpenHelper, rsShareHelper, rsPromptHandler, ParameterValues) { 'use strict'; var classicViewerGatewaySuffix = "v1/disp"; var promptPageEndpoint = "/rds/promptPage/report/"; var cvFormFields = [ "cv.navlinks", "cv.header", "cv.toolbar", "cv.selection", "cv.drill", "cv.contextInfo", "cv.contextmenu", "cv.id", "cv.responseFormat", "cv.rsProfile", "cv.showFaultPage", "cv.useAjax", "cv.reuseConversation", "cv.promptForDownload", "cv.gateway", "cv.webcontent", "cv.keepWindowOpen", "ui.action", "ui.cafcontextid", "ui.conversation", "ui.spec", "ui.errURL", "ui.routingServerGroup", "ui.name", "ui.object", "run.outputFormat", "run.outputLocale", "run.outputPageDefinition", "run.outputPageOrientation", "run.verticalElements", "run.horizontalElements", "run.prompt", "run.xslURL", "run.data", "specification.editSpecification", "modelPath", "m_tracking", "parameterValues", "reuseResults", "keepIterators", "rs_aliases", "authenticitytoken", "specificationType", "system.http://developer.cognos.com/ceba/constants/systemOptionEnum#accessibilityFeatures", "biDirectional.http://developer.cognos.com/ceba/constants/biDirectionalOptionEnum#biDirectionalFeaturesEnabled", "generic.anyURI.runOptionEnum#globalParameters", "isTitan", "IncludePerformance" ]; // Pattern to extract search path from defaultOutput method var g_reDefaultOutput = /^defaultOutput\((.*),\s*'[^']*'\s*,\s*'[^']*'\s*\)/; var cvContentView = ContentView.extend({ init: function(options, appView) { rsCommon.decodeAndMoveCMProperties(options); rsCommon.convertStringQSToValues(options, options.glassContext); rsCommon.decodeAndMoveRSOptions(options); // After the following call, every member of options is now a member of this cvContentView.inherited('init', this, arguments); this.m_sTitle = StringResource.get('classicviewer_welcome'); this.m_oAppView = appView; try { // to open a report passed on the URL and pass parameters, we need access to the // launch parameters stored in the calling window's Application object var v_oLaunchParameters = rsLaunchParameters.Retrieve(this.launchParametersKey); if (!v_oLaunchParameters && window.opener && !window.opener.closed && window.opener.RSParameters) { var v_sLaunchParametersKey = window.opener.RSParameters.launchParametersKey; var v_oLaunchParameters = rsLaunchParameters.Retrieve(v_sLaunchParametersKey); } else if (window.parent && options.launchParametersRef && window.parent[options.launchParametersRef]) { this.m_oPromptContext = window.parent[options.launchParametersRef].promptContext; } if (v_oLaunchParameters) { // merge options with the values pointed to by the key // the options from the object pointed to by the key take precedence $.extend(true, this, v_oLaunchParameters); } } catch(e) { console.log("cvContentView.init() - Call to window.opener failed, assume it doesn't exist"); } // console.log("end: cvContentView.init" ); this.m_bFullyInitialized = false; // Name of the classic viewer creation callback method this.m_sCreateCallback = "cvCreateCallback" + Date.now(); }, _postInit: function() { if (this.m_bFullyInitialized) { return; } this.m_bFullyInitialized = true; var options = this; if (options.type == 'output' && options.cmProperties && options.cmProperties.parent && options.cmProperties.parent[0] && options.cmProperties.parent[0].parent) { // If processing an output object and we have ancestor information, generate the saved output name. var v_sReportName = options.cmProperties.parent[0].parent[0].defaultName; this.m_sTitle = v_sReportName + " - " + options.cmProperties.modificationTime.substring(0, 10)+ " - " + options.cmProperties.format; } else if (options.cmProperties && options.cmProperties.defaultName) { this.m_sTitle = options.cmProperties.defaultName; } else if (options.defaultName) { this.m_sTitle = options.defaultName; } if (options.rsFinalRunOptions) { if (typeof options.rsFinalRunOptions.format != 'undefined') { options.format = options.rsFinalRunOptions.format; } if (typeof options.rsFinalRunOptions.locale != 'undefined') { // test how used options.locale = options.rsFinalRunOptions.locale; } if ( typeof options.rsFinalRunOptions.prompt !== 'undefined') { options.prompt = options.rsFinalRunOptions.prompt; } if (typeof options.rsFinalRunOptions.a11y != 'undefined') { options.a11y = options.rsFinalRunOptions.a11y; } if (typeof options.rsFinalRunOptions.bidi != 'undefined') { options.bidi = options.rsFinalRunOptions.bidi; } if (typeof options.rsFinalRunOptions.editSpecification != 'undefined') { options.editSpecification = options.rsFinalRunOptions.editSpecification; } delete options.rsFinalRunOptions; } // let create a form to submit to the classicviewer // TODO....change m_oRVFormParamters to something more meaningful like m_oClassicViewerForm? this.m_oRVFormParameters = {}; // Transfer all options.xxx fields where xxx is a recognised form field name to m_oRVFormParameters cvFormFields.forEach(function(v_sFormField){ if (typeof options[v_sFormField] != 'undefined') { this.m_oRVFormParameters[v_sFormField] = options[v_sFormField]; } }, this); if (options.format && !this.m_oRVFormParameters['run.outputFormat']) { this.m_oRVFormParameters['run.outputFormat'] = options.format; } if (options.contentLocale) { this.m_oRVFormParameters['run.outputLocale'] = options.contentLocale; } if (typeof options.prompt != 'undefined') { this.m_oRVFormParameters['run.prompt'] = (typeof options.prompt === "string") ? (options.prompt == "true") : !!options.prompt; } else { // for classical report running from RS, we treated undefined as true this.m_oRVFormParameters['run.prompt'] = true; } if (typeof options.a11y != 'undefined') { this.m_oRVFormParameters["system.http://developer.cognos.com/ceba/constants/systemOptionEnum#accessibilityFeatures"] = !!(options.a11y); } if (typeof options.bidi != 'undefined') { this.m_oRVFormParameters["biDirectional.http://developer.cognos.com/ceba/constants/biDirectionalOptionEnum#biDirectionalFeaturesEnabled"] = !!(options.bidi); } if (typeof options.editSpecification != 'undefined') { // This option is an XML string so only set it if we actually have something this.m_oRVFormParameters["specification.editSpecification"] = options.editSpecification; } // Ensure viewer renders the client side viewer in the response. // By default, PDF with accessibility enabled produces a page with PDF only and no viewer this.m_oRVFormParameters["ui.reuseWindow"] = true; this.m_oRVFormParameters["cv.createCallback"] = this.m_sCreateCallback; if (options.parameterValuesJSON) { this.m_oRVFormParameters["parameterValues"] = rsPromptParameters.rsBuildPromptParameters(null, options.parameterValuesJSON); } else if (options.parameterValuesXML) { this.m_oRVFormParameters["parameterValues"] = options.parameterValuesXML; } //Prompt parameters simple format this.m_aPromptParameters = []; for(var v_sKey in options) { if (options.hasOwnProperty(v_sKey) && v_sKey.indexOf('p_') == 0 && options[v_sKey]) { this.m_aPromptParameters.push({name: v_sKey, value: options[v_sKey]}); } } //Prompt parameters defined in json this.m_aPromptParametersComplex = []; if (options["promptParameters"]) { this.m_aPromptParametersComplex = Array.isArray(options["promptParameters"]) ? options["promptParameters"] : [options["promptParameters"]]; } var v_sSearchPath = this.getSearchPath(); var v_sStoreId = this.cmProperties && this.cmProperties.id; // Prefer search path over store ID since external lineage expects a standard search path var v_sReportSearchPath = v_sSearchPath || (v_sStoreId ? 'storeID("' + v_sStoreId + '")': ""); this.m_oRVFormParameters['ui.object'] = v_sReportSearchPath; if (!this.m_oRVFormParameters['ui.object']) { this.m_oRVFormParameters['cv.id'] = 'RS'; // this.m_oRVFormParameters['ui.errURL'] = 'javascript:window.close()'; } if (this.type == 'output') { this.m_oRVFormParameters['ui.action'] = 'view'; // Remove "run." parameters since we are viewing // Some of these override the ui.action=view and cause a run instead which is not what we want. for (var v_sProp in this.m_oRVFormParameters) { if (v_sProp.substring(0, 4) == "run.") { delete this.m_oRVFormParameters[v_sProp]; } } } else { // deletions are to ensure empty form fields are // not passed to viewer to avoid CAF rejecting them. if (this.m_oRVFormParameters['ui.object']) { this.m_oRVFormParameters['ui.action'] = 'run'; delete this.m_oRVFormParameters['ui.spec']; } else if (this.m_oRVFormParameters['ui.spec']) { this.m_oRVFormParameters['ui.action'] = 'runSpecification'; delete this.m_oRVFormParameters['ui.object']; } else { delete this.m_oRVFormParameters['ui.action']; delete this.m_oRVFormParameters['ui.spec']; delete this.m_oRVFormParameters['ui.object']; this.glassContext.showToast(StringResource.get('invalid_refresh'), {type:'error'}); } } // We want to explicitly turn off the header (which says Cognos Viewer) as well as the toolbars this.m_oRVFormParameters["cv.header"] = false; this.m_oRVFormParameters["cv.toolbar"] = false; this.m_oRVFormParameters["cv.keepWindowOpen"]=true; this.m_oRVFormParameters["isTitan"] = true; if (this.glassContext && this.glassContext.services && this.glassContext.services.userProfile && this.glassContext.services.userProfile.userProfileSettings && this.glassContext.services.userProfile.userProfileSettings.parameter_values) { this.m_oRVFormParameters["generic.anyURI.runOptionEnum#globalParameters"] = ParameterValues.toXML(this.glassContext.services.userProfile.userProfileSettings.parameter_values); } }, showWaitIndicator: function() { this.m_elDivViewerContainer.style.visibility = "hidden"; this.m_elDivStartUp.style.visibility = "visible"; }, hideWaitIndicator: function() { this.m_elDivStartUp.style.visibility = "hidden"; this.m_elDivViewerContainer.style.visibility = "visible"; }, _getNameFromViewer: function () { var v_oViewer = this.getCognosViewer(); return v_oViewer && v_oViewer.envParams && v_oViewer.envParams['ui.name']; }, getTitle: function() { // console.log("In cvContentView.getTitle\n"); return this.m_sTitle; }, getIcon: function() { //console.log("In cvContentView.getIcon\n"); return 'common-report'; }, getIconTooltip: function() { //console.log("In cvContentView.getIconTooltip\n"); return StringResource.get('classicviewer_welcome'); }, /** * Get the store ID of the underlying report object */ getReportStoreId: function() { if (rsCommon.isObjectOfType(this.cmProperties, 'output')) { return this.cmProperties.parent[0].parent[0].id; } return this.cmProperties && this.cmProperties.id; }, /** * Get the search path of the underlying report object */ getSearchPath: function() { return this.cmProperties && this.cmProperties.searchPath; }, getType: function() { var v_sType = this.cmProperties && this.cmProperties.type; if (!v_sType) { //When a classic report is launched from shared URL, we will not have cmPromperties, nor the type, we need to get the type from the viewer itself. var v_oCV = this.getCognosViewer(); v_sType = v_oCV && v_oCV.envParams["ui.objectClass"]; } return v_sType; }, getTimeStamp: function() { //console.log("In cvContentView.getTimeStamp\n"); return new Date().toUTCString(); }, updateButtons: function() { // Determine if readable. Assume readable if permissions not provided. var v_bRead = true; if (this.cmProperties && this.cmProperties.permissions && this.cmProperties.permissions.indexOf("read") == -1) { v_bRead = false; } var v_bShowEdit = false; //StoreId will be null if Authoring has launched classic viewer (because we are doing a runspec) if (this.glassContext.hasCapability("canUseReportStudio") && this.getReportStoreId() && v_bRead) { v_bShowEdit = true; } if (this.cmProperties && this.cmProperties.type == 'reportView') { // Don't allow edit on report views v_bShowEdit = false; } else if (this.cmProperties && this.cmProperties.type == 'report' && this.cmProperties.metadataModelPackage && this.cmProperties.metadataModelPackage[0] && this.cmProperties.metadataModelPackage[0].effectiveUserCapabilities) { if (this.cmProperties.metadataModelPackage[0].effectiveUserCapabilities.indexOf('canUseReportStudio') === -1 ) { v_bShowEdit = false; } } if (this.type == "output" || (this.cmProperties && this.cmProperties.type == "output")) { var v_bRun = this.cmProperties.permissions.indexOf("execute") != -1; this._setDisplayForPlugins(v_bRun, ["com.ibm.bi.classicviewer.outputBtn"]); this._setDisplayForPlugins(false, ["com.ibm.bi.classicviewer.saveBtn", "com.ibm.bi.classicviewer.editBtn", "com.ibm.bi.classicviewer.runMenu", "com.ibm.bi.classicviewer.previousReportBtn"]); } else { var v_bHasStoreId = !!this.getReportStoreId(); var v_sPluginId = "com.ibm.bi.classicviewer.editBtn"; this._setDisplayForPlugins(v_bShowEdit, [v_sPluginId]); this._setDisplayForPlugins(v_bHasStoreId, ["com.ibm.bi.classicviewer.saveBtn"]); this._setDisplayForPlugins(v_bHasStoreId, ["com.ibm.bi.classicviewer.runMenu"]); this._setDisplayForPlugins(false, ["com.ibm.bi.classicviewer.previousReportBtn","com.ibm.bi.classicviewer.outputBtn"]); } }, /** * Make the viewer iframe visible and hide the 'wait' indicator. */ _showViewer: function() { this.hideWaitIndicator(); this.updateButtons(); delete window.OnErrorPage; //console.log("end: cvContentView._showViewer\n"); }, show: function() { this.m_bDeactivated = undefined; cvContentView.inherited('show', this, arguments); if (this.m_aFnCallbacks) { // show saved plugings status var fnCallback; while (this.m_aFnCallbacks.length > 0) { fnCallback = this.m_aFnCallbacks.shift(); fnCallback(); } } }, _setEnabledForPlugins: function(v_bEnabled, v_aPluginIds) { v_aPluginIds.forEach(function(v_sPluginId) { var v_oPlugin = this.findPlugin(v_sPluginId); if (v_oPlugin) { if (v_bEnabled) { // console.log('%s show', v_sPluginId); v_oPlugin.enable(); } else { // console.log('%s hide', v_sPluginId); v_oPlugin.disable(); } } else { console.log('could not find plugin: %s', v_sPluginId); } }, this.glassContext); }, _setDisplayForPlugins: function(v_bShow, v_aPluginIds) { //console.group("cvContentView._setDisplayForPlugins"); //console.log("v_bShow:%s", v_bShow); v_aPluginIds.forEach( function(v_sPluginId) { var v_oPlugin = this.findPlugin(v_sPluginId); if ( v_oPlugin ) { if (v_bShow) { // console.log('%s show', v_sPluginId); v_oPlugin.show(); } else { // console.log('%s hide', v_sPluginId); v_oPlugin.hide(); } } else { console.log('could not find plugin: %s', v_sPluginId); } }, this.glassContext ); //console.groupEnd(); }, _isInternetExplorer: function() { var v_sAgent = navigator.userAgent.toLowerCase(); var v_fIEVersion = ( v_sAgent.search(/trident\/([0-9]+\.[0-9]+)/) != -1 ) ? parseFloat( RegExp.$1 ) : 0; return (v_fIEVersion >= 7.0); }, removePdfClass: function() { if (this._isInternetExplorer()) { this.m_elDivViewerContainer.classList.remove('PdfViewer'); } }, addPdfClass: function() { if (this._isInternetExplorer()) { //console.log("Setting PdfViewer class for IE"); this.m_elDivViewerContainer.classList.add('PdfViewer'); } }, onReportStatusComplete: function() { rsPerformance.mark('cvContentView.onReportStatusComplete'); console.timeEnd('rsperf: render->reportStatusComplete'); if (!!this.getReportStoreId()) { this._setEnabledForPluginsLazily(true, ["com.ibm.bi.classicviewer.saveBtn", "com.ibm.bi.classicviewer.runMenu"]); } // did we do something that changed the title? //if (this.m_sTitle) //{ // var v_sViewerName = this._getNameFromViewer(); // if (v_sViewerName && this.m_sTitle != v_sViewerName) // { // this.m_sTitle = v_sViewerName; // this.trigger('change:title', {'value': this.m_sTitle}); // } //} var v_oCognosViewer = this.getCognosViewer(); if (v_oCognosViewer && v_oCognosViewer.outputFormat == "PDF") { this.addPdfClass(); // Get PDF iframe this.m_iFramePDF = this.m_iframeClassicViewer.contentDocument.querySelector("iframe"); } else { this.removePdfClass(); delete this.m_iFramePDF; } }, onReportStatusPrompting: function() { if (!!this.getReportStoreId()) { this._setEnabledForPluginsLazily(false, ["com.ibm.bi.classicviewer.saveBtn", "com.ibm.bi.classicviewer.runMenu"]); // NOTE : Setting focus on the window did not work, but setting focus to the control did. // Not sure why. If you can think of a better way...great. // Change at your own risk. // get a node list of all the prompting control widgets var v_sCSSSelectors = ".clsTextWidget, .clsSelectDateEditBox, .clsSelectDateYearEditBox, .clsSelectDateEditBox, .clsSelectDateYearEditBox"; var v_nlPromptingControl = this.m_iframeClassicViewer.contentDocument.querySelectorAll(v_sCSSSelectors); // classnames of all the prompting control widgets including the prompt buttons var v_sCSSAllSelectors = v_sCSSSelectors + ", .bp"; if (v_nlPromptingControl.length > 0) { var v_aSelectors = v_sCSSAllSelectors.split(", "); // Check the active element matches one of the desired classname var bFound = v_aSelectors.includes("." + this.m_iframeClassicViewer.contentDocument.activeElement.className); // if none of them has a focus, set focus to the first non-button one. if (bFound == false) { v_nlPromptingControl[0].focus(); } } } }, _setEnabledForPluginsLazily: function(v_bEnabled, v_aPluginIds) { if (this.m_bDeactivated) { /** * this is for a long run report. when the running report's appView is hided behind and the report finish running, * this.findPlugin() function could find any plugin correctly, because glass always try to find plugin from current appView * which is not the long run report belong's to. so here, keep those status and do them when user switch the long run report's appView back to active */ if (!this.m_aFnCallbacks) { this.m_aFnCallbacks = []; } this.m_aFnCallbacks.push(function() { this._setEnabledForPlugins(v_bEnabled, v_aPluginIds); }.bind(this)); } else { this._setEnabledForPlugins(v_bEnabled, v_aPluginIds); } }, getViewerConfiguration: function() { console.timeEnd('rsperf: render->getViewerConfiguration'); rsPerformance.mark('cvContentView.getViewerConfiguration'); return { httpRequestCallbacks: { reportStatus: { complete: this.onReportStatusComplete.bind(this), prompting: this.onReportStatusPrompting.bind(this) } } }; }, render: function() { if (this.rsResolved) { // urlMap already fully resolved so proceed with rendering return this._render(); } // Perspective was launched directly without going through rsOpenHelper // Complete open resolution then proceed with render. if (!this.cmProperties && this.objRef) { // Create cmProperties from available information so resolution has something to work with this.cmProperties = { id: this.objRef, type: this.type // may or not be available but that is OK }; } var v_oOpenSpec = { cmProperties: this.cmProperties, glassContext: this.glassContext, urlMap: this }; return rsOpenHelper.resolveUrlMap(v_oOpenSpec).then(function(v_oResolvedOpenSpec) { // Merge resolved urlMap back into this instance and proceed with rendering $.extend(this, v_oResolvedOpenSpec.urlMap); return this._render(); }.bind(this)); }, _cleanPromptContext: function() { delete window.OnReportOutput; delete window.OnErrorPage; delete window.FinishCollectPrompts; this.m_oPromptContext.promptOpener.close(); }, _render: function() { // Perform any final initialization now that urlMap is fully resolved this._postInit(); // Update the buttons before we display the view in order to prevent flicker (REPORT-11732) this.updateButtons(); // This method returns a promise whose resolution signals the glass that our content view is done drawing. // Our view adds an iframe to the document and we use the onload event to trigger further processing to avoid race conditions. // The problem is the onload is called when the iframe is added to the document but also when the glass processes the promise resolution. // We need to defer our onload processing to the second call because any work done on the first load will get erased by the second load // (iframe src is about:blank). // One wrinkle in this is that under Edge, the iframe is not loaded when the glass promise is resolved so it must process things slightly differently. var deferred = $.Deferred(); console.timeEnd('rsperf: onSelectItem->render'); console.time('rsperf: render->loadIframe'); rsPerformance.mark('cvContentView.render'); this.$el.empty(); // Create wait indicator var v_aWaiting = document.getElementsByClassName("loadingIndicatorContainer"); var v_oWaiting = (v_aWaiting && v_aWaiting.length > 0) ? v_aWaiting.item(0) : null; this.m_elDivStartUp = document.createElement('div'); this.m_elDivStartUp.innerHTML = v_oWaiting ? v_oWaiting.outerHTML : ""; this.$el.append(this.m_elDivStartUp); // Create viewer div this.m_elDivViewerContainer = document.createElement('div'); this.m_elDivViewerContainer.style.width = "100%"; this.m_elDivViewerContainer.style.height = "100%"; this.m_elDivViewerContainer.style.position = "absolute"; this.m_elDivViewerContainer.style.visibility = "hidden"; this.$el.append(this.m_elDivViewerContainer); // Create viewer iframe this.m_iframeClassicViewer = document.createElement('iframe'); this.m_iframeClassicViewer.name = this.m_iframeClassicViewer.title = this.id || '_classicViewer'; this.m_iframeClassicViewer.src = "about:blank"; this.m_iframeClassicViewer.style.border = "none"; this.m_iframeClassicViewer.style.width = "100%"; this.m_iframeClassicViewer.style.height = "100%"; this.m_iframeClassicViewer.style.position = "absolute"; // HACK: the Glass doesn't have an API to squish the sidebar this.m_oAppView.$('.navbar').addClass('narrow'); console.time('rsperf: render->getViewerConfiguration'); window.getViewerConfiguration = this.getViewerConfiguration.bind(this); console.time('rsperf: render->reportStatusComplete'); // prompting for collectParameterValues in classical viewer if (this.m_oPromptContext && this.m_oPromptContext.promptOpener && this.getReportStoreId()) { // The following callback is invoked from prompt pages. // If a report has no parameters then no prompt page is rendered so we never // show the window - this is what we want. If we do show the window when there a no prompt pages, then // when we close the window in FinishCollectPrompts (see above), this terminates the java script which prevents // the caller from displaying the correct toasts. window.OnReportOutput = function() { // show prompting window if we have not already done so if (!this.m_oPromptContext.promptOpenerShown) { this._showViewer(); this.m_oPromptContext.promptOpener.show(); this.m_oPromptContext.promptOpenerShown = true; } }.bind(this); // Callback from classic error page window.OnErrorPage = function( code, message, details ) { if (this.m_oPromptContext.promptFnErrorCallback) { var error = { "code": code, "message": message, "details": details }; this.m_oPromptContext.promptFnErrorCallback( error ); } else if (this.m_oPromptContext.promptFnCancelCallback) { this.m_oPromptContext.promptFnCancelCallback(); } this._cleanPromptContext(); }.bind(this); // classical API function, this is called when finishing prompting window.FinishCollectPrompts = function(success) { if (success) { if (this.m_oPromptContext.promptFnOkCallback1) { console.log("promptContext.promptFnOkCallback1"); // This version of the OK callback will invoke the prompting service to retrieve the results. // This resolves a strange behavior in IE (see APAR 14584 which applies to reports as well as QS) this.m_oPromptContext.promptFnOkCallback1(); this._cleanPromptContext(); } else { console.log("promptContext.promptFnOkCallback"); this.glassContext.getSvc('.Prompting').then(function(promptingSvc) { promptingSvc.getPromptAnswers(this.m_oPromptContext.promptRDSId).then( function(values) { this.m_oPromptContext.promptFnOkCallback(values); this._cleanPromptContext(); }.bind(this)); }.bind(this)); } } else { if (this.m_oPromptContext.promptFnCancelCallback) { this.m_oPromptContext.promptFnCancelCallback(); } this._cleanPromptContext(); } }.bind(this); // RDS request for prompt ID this.glassContext.services.ajax.ajax({ url: classicViewerGatewaySuffix + promptPageEndpoint + this.getReportStoreId(), type: 'GET', dataType: 'xml' }).done(function(xmlDoc, status, jqXHR) { this.m_oPromptContext.promptRDSId = xmlDoc.getElementsByTagNameNS('http://developer.cognos.com/schemas/rds/types/2', 'promptID').item(0).textContent; this.m_oPromptContext.promptOpener.id = this.m_oPromptContext.promptRDSId; // Store any parameter values provided by the caller in the runTimeState object which is created by the // call to the promptPageEndpoint above. // The promting XTS pages look for initial parameter values in this runTimeState object. var v_sParameterValues = rsPromptParameters.rsBuildPromptParameters( null, this.m_oPromptContext.parameters ); this.glassContext.services.ajax.ajax({ type: 'PUT', url: 'v1/objects/' + this.m_oPromptContext.promptRDSId, data: JSON.stringify( { type: 'runTimeState', state: v_sParameterValues }), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }).done(function(){ // submit collectParameterValues request based on report id and prompt id this._createAndSubmitCollectParameterValuesForm(this.getReportStoreId(), xmlDoc.getElementsByTagNameNS('http://developer.cognos.com/schemas/rds/types/2', 'url').item(0).textContent); deferred.resolve(this.$el); }.bind(this)); }.bind(this)); } else { // onload must be specified before iframe is attached to the document because Chrome will trigger the load immediately this.m_iframeClassicViewer.onload = this._onLoad.bind(this); this.loadClassicViewer(); // This is the "early" resolve - show the StartUp div right away deferred.resolve(this.$el); } // On Chrome, adding the iframe to the document triggers the onload event immediately whereas other browsers load the iframe // when the current js call stack finishes. We must delay adding the iframe until all work is done in this method to ensure // all callbacks are properly configured before the ifrmae loads so this is the last thing we do. this.m_elDivViewerContainer.appendChild(this.m_iframeClassicViewer); return deferred.promise(); }, /** * This method is called by the classic viewer right after the classic viewer object is created but before the page finishes loading. * The timing is important because it lets us know about the classic viewer before any iframes are processed which may trigger a download. * The div containing the classic viewer is hidden until we are notified the classic viewer exists so this must happen before download is initiated. * @param v_sCV Name of the classic viewer object */ _onClassicViewerCreated: function( v_sCV ) { //console.log("CV create callback"); // Get the classic viewer object if (this.m_iframeClassicViewer && this.m_iframeClassicViewer.contentWindow) { // Ensure getClassicViewer returns the the classic viewer without having to look for it this.m_oCognosViewer = this.m_iframeClassicViewer.contentWindow[v_sCV]; } this._onClassicViewerLoaded(); }, /** * Hook up the classic viewer to the cvContentView * and display the viewer iframe */ _onClassicViewerLoaded: function() { console.timeEnd('rsperf: render->loadIframe'); console.timeEnd('rsperf: total_RunReport'); rsPerformance.mark('cvContentView._onClassicViewerLoaded'); rsPerformance.mark('authoring-selectItemAndDraw-stop'); //console.log("cvContentView._onClassicViewerLoaded(). "); if (this.isViewerLoaded()) { // Either classic viewer or RDS prompt page is loaded var v_oCognosViewer = this.getCognosViewer(); if (v_oCognosViewer) { this.m_iframeClassicViewer.contentWindow.onunload = this._release.bind(this); // get a reference to the original executeBackURL method...just in case we need to call it this.original_executeBackURL = this.m_iframeClassicViewer.contentWindow.executeBackURL; // point the existing method to our method this.m_iframeClassicViewer.contentWindow.executeBackURL = this.executeBackURL.bind(this); // get a reference to the original doSingleDrill method...we need to call it later this.original_doSingleDrill = this.m_iframeClassicViewer.contentWindow.doSingleDrill; // point the existing method to our method this.m_iframeClassicViewer.contentWindow.doSingleDrill = this.doSingleDrill.bind(this); // Define goto page handler this.m_iframeClassicViewer.contentWindow.rsGoToHandler = this.gotoHandler.bind(this); // Prevent old PDF drill logic from trying to restore PDF using browser history (see viewer drill-from-pdf.xts) this.m_iframeClassicViewer.contentWindow.f_restorePDF = this.f_restorePDF.bind(this); // get a reference to the original addDrillEnvironmentFormFields method...we need to call it later this.original_addDrillEnvironmentFormFields = this.m_iframeClassicViewer.contentWindow.addDrillEnvironmentFormFields; // point the existing method to our method this.m_iframeClassicViewer.contentWindow.addDrillEnvironmentFormFields = this.addDrillEnvironmentFormFields.bind(this); if (this.glassContext && this.glassContext.isDevInstall && this.glassContext.isDevInstall()) { rsPerformance.summarizeClassicViewerRun( this ); } if (v_oCognosViewer.updateNewBrowserWindow && this.m_iframeClassicViewer.contentWindow.getFormWarpRequest) { var form = this.m_iframeClassicViewer.contentWindow.getFormWarpRequest(); var v_sBackURL = v_oCognosViewer.m_bIgnoreCloseWindow ? "" : (form ? form["ui.backURL"].value : ""); if (v_sBackURL == "javascript:window.close();") { v_oCognosViewer.updateNewBrowserWindow = function() { this.glassContext.closeAppView("classicviewer", this.id); }.bind(this); } } /** * inject a function to classical viewer, when keyboard conbination ctrl-shift-1 is hit, * this function will be called to fucus to Appbar */ v_oCognosViewer.focusBackToContent = function(evt) { this.glassContext.accessibilityController.setFocusToAppbar(); }.bind(this); /** * inject a function to classical viewer, when keyboard conbination ctrl-shift-2 is hit, * this function will be called to fucus to MainContent */ v_oCognosViewer.focusBackToMainContent = function(evt) { this.setFocus(); }.bind(this); } this._showViewer(); } // Stop processing load event this.m_iframeClassicViewer.onload = null; }, createLaunchSpec : function(v_oCmObject, format, method, locale, sPrompt) { // Convert HTML-like formats to HTML to ensure perspective resolution works // Viewer will handle specifying the actual format that it really wants. format = format.indexOf('HTML') != -1 ? 'HTML' : format; var v_oLaunchSpec = { // string of output format // one of HTML, PDF, xlsxData, CSV, spreadsheetML, layoutDataXML, rawXML, singleXLS, HTMLFragment format: format, objRef: v_oCmObject.id, type: v_oCmObject.type, contentLocale: locale, }; switch (method) { case "execute": case "run": v_oLaunchSpec.action = "run"; break; case "view": case "viewOutput": v_oLaunchSpec.action = "viewOutput"; break; case "edit": v_oLaunchSpec.action = "edit"; break; } switch (sPrompt) { case "yes": case "true": v_oLaunchSpec.prompt = true; break; case "no": case "false": v_oLaunchSpec.prompt = false; break; } return v_oLaunchSpec; }, // we are hijacking the viewer's copy of this method so that we can // add a form field to the form before being submitted addDrillEnvironmentFormFields : function(drillForm, oCV) { // call the original viewer method this.original_addDrillEnvironmentFormFields (drillForm, oCV); // append the cv.keepWindowOpen field to the form // we probably do not need to check to see if the "createFormField" method exists, but // I would rather make sure before I called it. if (this.m_iframeClassicViewer && this.m_iframeClassicViewer.contentWindow && this.m_iframeClassicViewer.contentWindow.createFormField) { drillForm.appendChild(this.m_iframeClassicViewer.contentWindow.createFormField("cv.keepWindowOpen", "true")); } }, /** * Handler called from goto page for authored drill. * @param drillType Indicates what kind of drill is being performed * @param drill drill information specific to drill type */ gotoHandler : function(drillType, drill) { if (drillType == 'authoredDrillthru') { this._authoredDrillHandler(drill); } else { this._packageDrillHandler(drill); } }, /** * Submit form described by drill as AJAX request after adding option * to make request return results of calling drill service on package drill object. */ _packageDrillHandler : function(drillForm) { this._sendDrillThroughRequest( {packageDrill: true}, drillForm ); }, /** * Perform authored drill. Converts the drill information into the parameters expected by the doSingleDrill method * and then calls said method. As a result, authored drill from goto page behaves the same * as single authored drill. */ _authoredDrillHandler : function(drill) { var target, args, method, format, locale, bookmark, sourceContext, objectPaths, cvId, sPrompt, dynamicDrill; var v_aObj = []; var v_aParams = []; var v_oDrillDoc = this.parseXML(drill); var v_nDrillNode = v_oDrillDoc && v_oDrillDoc.documentElement.firstChild; while (v_nDrillNode) { switch (v_nDrillNode.nodeName) { case 'param': switch (v_nDrillNode.getAttribute('name')) { case 'action': method = this.getNodeText(v_nDrillNode); break; case 'format': format = this.getNodeText(v_nDrillNode); break; case 'locale': locale = this.getNodeText(v_nDrillNode); break; case 'target': var v_sTarget = this.getNodeText(v_nDrillNode); // When action on drill is view, the target search path provided from the goto page // is based on the defaultOutput( path, format, locale ) CM search path function. // Use a pattern to extract the path parameter. var v_aMatch = v_sTarget.match(g_reDefaultOutput); if (v_aMatch && v_aMatch.length > 1) { v_sTarget = v_aMatch[1]; } v_aObj.push('obj'); v_aObj.push(v_sTarget); break; case 'prompt': sPrompt = this.getNodeText(v_nDrillNode); break; case 'dynamicDrill': dynamicDrill = this.getNodeText(v_nDrillNode); break; case 'showInNewWindow': target = (this.getNodeText(v_nDrillNode) == 'false') ? '' : '_blank'; break; case 'executionParameters': break; case 'metadataModel': break; case 'sourceContext': sourceContext = this.getNodeText(v_nDrillNode); break; case 'bookmark': bookmark = this.getNodeText(v_nDrillNode); break; case 'objectPaths': objectPaths = this.getNodeText(v_nDrillNode); break; } break; case 'drillParameters': var v_nParamNode = v_nDrillNode.firstChild; while (v_nParamNode) { if (v_nParamNode.nodeName == 'param') { var v_sName = v_nParamNode.getAttribute('name'); var v_sValue = this.getNodeText(v_nParamNode); v_aParams.push([v_sName, v_sValue]); } v_nParamNode = v_nParamNode.nextSibling; } break; } v_nDrillNode = v_nDrillNode.nextSibling; } args = [v_aObj]; cvId = this.getCognosViewer().getId(); this.doSingleDrill(target, args.concat(v_aParams), method, format, locale, bookmark, sourceContext, objectPaths, cvId, sPrompt, dynamicDrill ); }, doSingleDrill : function(target,args,method,format,locale,bookmark,sourceContext,objectPaths,cvId,sPrompt,dynamicDrill) { // This method is called by the classic viewer when doing a single drill. The viewer first determines whether or // not a drill to self should be done. If not, then it calls this method. We need to take over processing // so that we can determine which perspective should be used on the target. // But first, we use the classic viewer to call the drill service. var v_oArguments = { target: target, args: args, method: method, format: format, locale: locale, bookmark: bookmark, sourceContext: sourceContext, objectPaths: objectPaths, cvId: cvId, sPrompt: sPrompt, dynamicDrill: dynamicDrill }; this.showWaitIndicator(); // Override how classic viewer issues it's drill request and call original drill method this.getCognosViewer().sendDrillThroughRequest = this._sendDrillThroughRequest.bind(this, v_oArguments); this.original_doSingleDrill(target,args,method,format,locale,bookmark,sourceContext,objectPaths,cvId,sPrompt,dynamicDrill); }, _htmlDecode: function (v_sText) { var v_oDoc = new DOMParser().parseFromString('
' + v_sText + '
', "text/html"); return v_oDoc.documentElement.textContent; }, _sendDrillThroughRequest: function(v_oDrillArguments, v_elViewerDrillForm) { // Convert classic viewer drill form into an AJAX request var v_sAction = v_elViewerDrillForm.action; var v_iIdx = v_sAction.indexOf("?"); var v_sURL = v_iIdx > 0 ? v_sAction.substring(0, v_iIdx) : v_sAction; var v_aData = []; var v_nlInputs = v_elViewerDrillForm.elements; for (var idx = 0; idx < v_nlInputs.length; ++idx) { var v_nInput = v_nlInputs[idx]; if (v_nInput.nodeName === 'INPUT') { v_aData.push(v_nInput.name + '=' + encodeURIComponent(v_nInput.value) ); } } // Force viewer to only perform drill service call v_aData.push('cv.drillServiceOnly=true'); var v_oRequest = { type : 'POST', url : v_sAction, dataType: 'xml', data : v_aData.join('&') }; this.glassContext.services.ajax.ajax(v_oRequest) .fail(function(x,http,z) { var v_sMsg = null; if (http && typeof http.getResponseHeader == 'function' && http.getResponseHeader('Content-Type').indexOf('text/html') == 0) { // We appear to have an error page since we were expecting a SOAP response but we got html var v_aMatch = http.responseText.match(/(.*)<\/ERROR_CODE>(.*)<\/ERROR_MSG>/); if (v_aMatch && v_aMatch.length >= 3) { v_sMsg = this._htmlDecode(v_aMatch[2]); console.error("cvContentView._sendDrillThroughRequest " + v_aMatch[1] + ":" + v_sMsg); } } this.hideWaitIndicator(); this.glassContext.showToast(v_sMsg || "Unexpected error on drill request", {type:'error'}); }.bind(this)) .then(this._drillServiceResponse.bind(this, v_oDrillArguments, v_sURL)); }, getOutputPages: function(v_nDrillServiceResponse) { var v_nDetails = v_nDrillServiceResponse.querySelector('Envelope>Body result>details'); var v_aElements = this._getChildElementsByXsiType( v_nDetails, 'bus:asynchDetailReportOutput' ); var v_nOutputPages = v_aElements.length > 0 ? v_aElements[0] : null; // should only be 0 or 1 var v_nPage = v_nOutputPages.querySelector('item'); return v_nPage ? v_nPage.textContent : ''; }, F_SetPromptCallbackResponse: function(v_sResponse, v_oAttachments) { if (v_sResponse) { var v_nResponse = this.parseXML(v_sResponse); if (this._promptDeferred) { this._promptDeferred.resolve(v_nResponse); } } else { this._promptDeferred.reject({"state": "Cancel"}); } }, _drillServiceResponse: function(v_oDrillArguments, v_sUrl, v_oResponseData, v_sStatus, v_oXHR) { // Handle drill service response which might require // - issuing a wait request if status=working // - display prompt page is status=prompting var v_nDrillServiceResponse = v_oResponseData; var v_nStatus = v_nDrillServiceResponse.querySelector('Envelope>Body result>status'); var v_sStatus = v_nStatus && v_nStatus.textContent; if (v_sStatus == 'working' || v_sStatus == 'stillWorking') { this._wait( v_nDrillServiceResponse, v_sUrl ) .fail(function() { console.error("cvContentView._drillServiceResponse wait failed"); this.hideWaitIndicator(); this.glassContext.showToast("Unexpected error on drill request", {type:'error'}); }.bind(this)) .then(this._drillServiceResponse.bind(this, v_oDrillArguments, v_sUrl)); return; } var v_nDetails = v_nDrillServiceResponse.querySelector('Envelope>Body result>details'); var v_aElements = this._getChildElementsByXsiType( v_nDetails, 'bus:asynchDetailReportStatus' ); var v_nReportStatus = v_aElements.length > 0 ? v_aElements[0] : null; // should only be 0 or 1 v_nStatus = v_nReportStatus ? v_nReportStatus.querySelector('status') : null; v_sStatus = v_nStatus && v_nStatus.textContent; if (v_sStatus == 'prompting') { this._prompt( v_nDrillServiceResponse ) .fail(function(err) { this.hideWaitIndicator(); if (!err || err.state != "Cancel") { console.error("cvContentView._drillServiceResponse prompt failed"); this.glassContext.showToast(err.message, {type:'error'}); } }.bind(this)) .then( // Process the drill response now that prompts have been satisfied this._drillServiceResponse.bind(this, v_oDrillArguments, v_sUrl) ); return; } // Invoke target this._doDrill(v_oDrillArguments, v_nDrillServiceResponse); }, _wait : function( v_nDrillServiceResponse, v_sUrl ) { var v_nBody = v_nDrillServiceResponse.querySelector('Envelope>Body'); var v_nResponse = v_nBody.querySelector('runSpecificationResponse,runResponse,waitResponse'); if (v_nResponse) { v_nBody.removeChild(v_nResponse); } var v_nWait = v_nDrillServiceResponse.createElementNS("http://developer.cognos.com/schemas/reportService/1", "rs:wait"); v_nBody.appendChild(v_nWait); this._addCafContextID(v_nDrillServiceResponse); var v_nDocElem = v_nDrillServiceResponse.documentElement; // IE doesn't have outerHTML var v_sWait = v_nDocElem.outerHTML || ( new XMLSerializer() ).serializeToString(v_nDocElem); var v_oRequest = { type : 'POST', url : v_sUrl, dataType: 'xml', contentType: 'text/xml; charset=UTF-8', headers: {SOAPAction: 'http://www.ibm.com/xmlns/prod/cognos/reportService/201712/.absolute' }, data : v_sWait }; return this.glassContext.services.ajax.ajax(v_oRequest); }, _addCafContextID : function( v_nSoapRequest ) { var v_oCognosViewer = this.getCognosViewer(); if (v_oCognosViewer) { var v_sCAF = v_oCognosViewer.getCAFContext(); if (v_sCAF) { var v_nBiBusHeader = v_nSoapRequest.querySelector('Envelope>Header>biBusHeader'); if (v_nBiBusHeader) { var v_nCAF = v_nBiBusHeader.querySelector('CAF'); if (!v_nCAF) { // no CAF node, create it v_nCAF = v_nSoapRequest.createElement('bus:CAF'); v_nCAF.setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:type', 'bus:CAF'); v_nBiBusHeader.appendChild(v_nCAF); } var v_nContextID = v_nCAF.querySelector('contextID'); if (!v_nContextID) { // no contextID node, create it v_nContextID = v_nSoapRequest.createElement('bus:contextID'); v_nContextID.setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:type', 'xs:string'); v_nCAF.appendChild(v_nContextID); } // set contextID v_nContextID.textContent = v_sCAF; } } } }, _prompt : function( v_nDrillServiceResponse ) { // This method should display the prompt page and when the prompting sequence is complete // return the resulting SOAP response as the promise resolution so that the drill through can proceed. // Until this logic is implemented, we force a failure instead. this._promptDeferred = $.Deferred(); rsPromptHandler.F_DoPrompting(this, v_nDrillServiceResponse, this.glassContext); return this._promptDeferred.promise(); }, _extractOption : function( v_nDrillThroughDetails, v_sType, v_sName ) { var v_oValue; var v_nOptions = v_nDrillThroughDetails && v_nDrillThroughDetails.querySelector('options'); var v_aXMLOptions = this._getChildElementsByXsiType( v_nOptions, v_sType ); for (var idx = 0; idx < v_aXMLOptions.length; ++idx) { var v_nOption = v_aXMLOptions[idx]; var v_nName = v_nOption.querySelector('name'); if (v_nName && v_nName.textContent == v_sName) { var v_nValue = v_nOption.querySelector('value'); if (v_nValue) { if (v_nValue.getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'type') == 'SOAP-ENC:Array') { v_oValue = []; var v_nChildren = v_nValue.childNodes; for (var idx = 0; idx < v_nChildren.length; ++idx) { var v_nChild = v_nChildren.item(idx); if (v_nChild.nodeType == Node.ELEMENT_NODE) { v_oValue.push( v_nChild.textContent ); } } } else { v_oValue = v_nValue.textContent; } } break; } } return v_oValue; }, /** * Extract information from SOAP asynchDetailDrillThroughRequest response of a run package drill object request. * Convert information into a form as an authored drill was being processed. * @param v_oDrillArguments Extracted drill information * @param v_nDrillThroughDetails The asynchDetailDrillThroughRequest from the SOAP response */ _extractPackageDrillArguments : function(v_oDrillArguments, v_nDrillThroughDetails) { /* bookmark not provided in drill response */ v_oDrillArguments.args = []; // Extract target report search path var v_nNode = v_nDrillThroughDetails.querySelector('target'); var v_sNode = v_nNode ? v_nNode.textContent : ""; v_oDrillArguments.args.push(['obj', v_sNode]); // Extract drill in new window v_oDrillArguments.target = ''; // no way to specify open in new window on package drill // Extract drill format var v_aFormat = this._extractOption( v_nDrillThroughDetails, 'bus:runOptionStringArray', 'outputFormat' ); v_oDrillArguments.format = v_aFormat && v_aFormat.length > 0 ? v_aFormat[0] : ''; // Extract drill action v_nNode = v_nDrillThroughDetails.querySelector('action'); var v_sNode = v_nNode ? v_nNode.textContent : ""; v_oDrillArguments.method = v_sNode; // Package drill does not provide content locale so use this instance's value // If viewing saved output, use output locale else use current locale v_oDrillArguments.locale = this.cmProperties && this.cmProperties.type == 'output' ? this.cmProperties.locale : (this.contentLocale || ''); // Extract drill prompt v_oDrillArguments.sPrompt = this._extractOption( v_nDrillThroughDetails, 'bus:runOptionBoolean', 'prompt' ); }, _doDrill : function(v_oDrillArguments, v_nDrillServiceResponse) { // Get the asynchDetailDrillThroughRequest element from the drill service SOAP response var v_nDetails = v_nDrillServiceResponse.querySelector('Envelope>Body result>details'); // IE does not appear to support query selectors on attribute values with namespace qualified attributes i.e. xsi:type // so we do it the 'hard' way var v_aElements = this._getChildElementsByXsiType( v_nDetails, 'bus:asynchDetailDrillThroughRequest' ); var v_nDrillThroughDetails = v_aElements.length > 0 ? v_aElements[0] : null; // should only be 0 or 1 if (v_oDrillArguments.packageDrill && v_nDrillThroughDetails) { this._extractPackageDrillArguments(v_oDrillArguments, v_nDrillThroughDetails); } /*bookmark,*/ var v_bDrillThoughNewWindow = v_oDrillArguments.target == "_blank" ? true : false; var v_sSearchPath = v_oDrillArguments.args[0][1]; var v_oCurrentThisContext = this; this._getCMInfo(v_sSearchPath).then(function(v_oCmObject) { var v_sDrillTargetId = v_oCmObject.id; var v_oLaunchSpec = this.createLaunchSpec(v_oCmObject, v_oDrillArguments.format, v_oDrillArguments.method, v_oDrillArguments.locale, v_oDrillArguments.sPrompt); var v_oDrillContext = {}; // Extract edit specification option value and add to 'large' options var v_sEditSpecification = this._extractOption( v_nDrillThroughDetails, 'bus:specificationOptionXMLEncodedXML', 'editSpecification' ); if (v_sEditSpecification && v_sEditSpecification.length > 0) { v_oDrillContext.editSpecification = v_sEditSpecification; } // Extract parameter values and add to 'large' options var v_nParameters = v_nDrillThroughDetails && v_nDrillThroughDetails.querySelector('parameters'); if (v_nParameters) { var v_sParameterXML = ( new XMLSerializer() ).serializeToString( v_nParameters ); if (v_sParameterXML && v_sParameterXML.length > 0) { v_oDrillContext.parameterValuesXML = v_sParameterXML; } } var v_oGlass = this.glassContext; var v_oGlassSettings = rsCommon.extractGlassSettings( this ); if (v_bDrillThoughNewWindow) { v_oLaunchSpec.launchParametersKey = rsLaunchParameters.Store(v_oDrillContext); // Encode options as a JSON string to avoid unexpected changes to parameters var v_oUrlMap = { objRef: v_sDrillTargetId, rsEncodedOptions: JSON.stringify(v_oLaunchSpec) }; // Transfer any glass settings that we are aware of v_oUrlMap = $.extend( v_oUrlMap, v_oGlassSettings ); v_oUrlMap.closeWindowOnLastView = true; v_oUrlMap.prefetchsvc = "disabled"; // this an optimization for the new window - it tells the glass to load less stuff var v_sUrl = this.glassContext.getUrl( { urlMap: v_oUrlMap } ); var newWindow = window.open(v_sUrl, "_blank"); if (newWindow) { newWindow.focus(); } this.f_restorePDF(); this.hideWaitIndicator(); } else { v_oLaunchSpec = $.extend(v_oLaunchSpec, v_oDrillContext); // Transfer any glass settings that we are aware of v_oLaunchSpec = $.extend( v_oLaunchSpec, v_oGlassSettings ); // close the authoring perspective with the drill target id (if exists) v_oGlass.closeAppView("authoring", v_sDrillTargetId) // close the classicviewer perspective with the drill target id (if exists) .then(v_oGlass.closeAppView.bind(v_oGlass, "classicviewer", v_sDrillTargetId)) // open a classicviewer perspective with the drill target id .then(v_oGlass.openAppView.bind(v_oGlass, undefined, {content: v_oLaunchSpec})) .then(function(targetAppView) { // wait for the appView to be created return targetAppView.onViewRendered(); }) .then(function() { this.f_restorePDF(); this.hideWaitIndicator(); }.bind(this)); } }.bind(this)) .catch(function(err){ var v_sSearchPath = err.message; var v_sMessage = "Content Manager did not return information for '" + v_sSearchPath + "'"; v_oCurrentThisContext.glassContext.showToast(v_sMessage, {type:'error'}); console.warn('cvContentView.sendDrillThroughRequest could NOT load: %s', err); }).bind(this); }, _getChildElementsByXsiType : function ( v_nNode, v_sXsiType ) { var v_aResult = []; if (v_nNode) { var v_nChildren = v_nNode.childNodes; for (var idx = 0; idx < v_nChildren.length; ++idx) { var v_nChild = v_nChildren.item(idx); if (v_nChild.nodeType == Node.ELEMENT_NODE && v_nChild.getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'type') == v_sXsiType) { v_aResult.push( v_nChild ); } } } return v_aResult; }, // This function's existence is checked in Viewer's drill-from-pdf.xts f_restorePDF : function () { if (this.m_iFramePDF) { // refresh the PDF iframe var src = this.m_iFramePDF.src; this.m_iFramePDF.src = src; } }, f_convertParametersfromXmlToJsonStr : function (v_nParameters) { // the node comes in as // // <selectChoices><selectOption useValue="Outdoor Protection" displayValue="Outdoor Protection"/></selectChoices> // var v_aParameters = []; // walk through all the child nodes var v_nChildNode = v_nParameters.firstChild; while (v_nChildNode) { var v_oParameter = {}; if (v_nChildNode.nodeName === "param") { // populate the name field with the v_nChildNode's name attribute and // strip off the p_ v_oParameter.name = v_nChildNode.getAttribute("name"); v_oParameter.name = v_oParameter.name.slice(2); v_oParameter.value = []; // // the textContent of the param node needs to be converted from string // <selectChoices><selectOption useValue="Outdoor Protection" displayValue="Outdoor Protection"/></selectChoices> // to an XML doc // // // // so that is can be parsed var v_nSelectDoc = this.parseXML(v_nChildNode.textContent); // create a v_oParameter.value entry for each selectOption node var v_aSelectOptionNodes = v_nSelectDoc.getElementsByTagName("selectOption"); for (var i = 0; i < v_aSelectOptionNodes.length; i++) { var v_oValue = {}; if (v_aSelectOptionNodes[i].hasAttribute("displayValue")) { v_oValue.display = v_aSelectOptionNodes[i].getAttribute("displayValue"); } v_oValue.use = v_aSelectOptionNodes[i].getAttribute("useValue"); v_oParameter.value.push(v_oValue); } v_nChildNode = v_nChildNode.nextSibling; v_aParameters.push(v_oParameter); } } return JSON.stringify(v_aParameters); }, _getCMInfo: function (v_sSearchPath) { var v_sSearchPathUrl = "v1/search_path?searchPath=" + encodeURIComponent(v_sSearchPath) + "&fields=id,type"; return this.glassContext.services.fetch.get(v_sSearchPathUrl) .then( function(v_oCmInfoResponse) { return v_oCmInfoResponse.data.data[0]; }) .catch(function(v_oErr){ console.log('cvContentView.getCMInfo FAILED for "%s"', v_sSearchPathUrl); throw new Error(v_sSearchPath); }); }, _extractCmPathTermFromCmPathUrl: function(v_sCmPathUrl) { // Extract the "path" query-value from the path url // /bi/v1/path?path=.public_folders%2Fbugs%2Fcv-apar120836_pi88512-rtc200676-drillparams var v_sQuery = v_sCmPathUrl.split("?", 2)[1]; var v_aFields = v_sQuery.split("&"); for(var i=0; i < v_aFields.length; ++i) { var v_aNameValue = v_aFields[i].split("=", 2); var v_sName = v_aNameValue[0], v_sValue = v_aNameValue[1]; if (v_sName == "path") { return decodeURIComponent(v_sValue); } } }, executeBackURL: function (s_CVId) { // get the cognos viewer var v_oCognosViewer = this.getCognosViewer(); var form = this.m_iframeClassicViewer.contentDocument.getElementById("formWarpRequest" + (s_CVId ? s_CVId : "")); var v_sBackURL = v_oCognosViewer.m_bIgnoreCloseWindow ? "" : form["ui.backURL"].value; // see if there is a "CMODAL_FRAME" in the authoring document var v_nClassicViewerDialogFrame = this.m_iframeClassicViewer.contentDocument.getElementById("CMODAL_FRAME"); // if there is a modal shown and the CModal class is available, hide the dialog box if (v_oCognosViewer.modalShown && v_nClassicViewerDialogFrame && v_nClassicViewerDialogFrame.CModal) { // hide the dialog v_nClassicViewerDialogFrame.CModal.hide(); // reset the flag v_oCognosViewer.modalShown = false; } // the perspective is in an iframe, so we can not use window.close. // instead, we will call the class to close the current app view (which is us) if (v_sBackURL == "javascript:window.close();") { this.glassContext.closeAppView("classicviewer", this.id); } // otherwise call the original execBackURL method else { this.original_executeBackURL(s_CVId); } v_oCognosViewer.m_bIgnoreCloseWindow = undefined; }, _createAndSubmitCollectParameterValuesForm: function(v_sStoreId, v_sUrl) { v_sUrl = decodeURIComponent(v_sUrl); var v_sParameters = v_sUrl.slice(v_sUrl.indexOf("?")); var v_elForm = document.createElement("form"); v_elForm.target = this.m_iframeClassicViewer.name; v_elForm.method = "post"; v_elForm.action = classicViewerGatewaySuffix + v_sParameters; v_elForm.style.display = "none"; document.body.appendChild(v_elForm); v_elForm.submit(); document.body.removeChild(v_elForm); }, /** * onload handler for classic viewer iframe */ _onLoad: function() { //console.log( "_onLoad"); if (this.isViewerLoaded()) { //console.log( "_onLoad viewer loaded"); // Classic viewer exists so the viewer form post result was loaded this._onClassicViewerLoaded(); } else if (this.m_fOnLoadHandler) { //console.log( "_onLoad launch viewer"); // Call the classic viewer now that the iframe has been loaded by the glass this.m_fOnLoadHandler(); // Ensure form is posted only once in case things go bad this.m_fOnLoadHandler = null; } else { //console.log( "_onLoad show viewer"); // If we get here, something unexpected occurred. Most likely, the viewer returned something // (error page) that does not signal cvContentView that it is there via window.OnErrorPage or // the classic viewer creation callback. // Show the viewer div in case there is something there. this._showViewer(); } }, _createAndSubmitNewForm: function() { //console.log( "_createAndSubmitNewForm"); // APAR 120836 REPORT FAILS TO RUN IN IE11 IF THE DRILL-THROUGH PROMPTPARAMETER VALUES ARE LARGER THAN 2,083 CHAR LIMIT // Use a POST with form instead of GET request var v_elForm = document.createElement("form"); v_elForm.target = this.m_iframeClassicViewer.name; v_elForm.method = "post"; v_elForm.action = classicViewerGatewaySuffix; v_elForm.style.display = "none"; var v_elInput = v_elForm.appendChild( document.createElement("input") ); v_elInput.type = "hidden"; v_elInput.name = "b_action"; v_elInput.value = "cognosViewer"; Object.keys(this.m_oRVFormParameters).forEach( function(key){ var v_elInput = v_elForm.appendChild( document.createElement("input") ); v_elInput.type = "hidden"; v_elInput.name = key; v_elInput.value = this.m_oRVFormParameters[key]; }, this); rsPromptParameters.cvAddPromptParameters(v_elForm, this.m_aPromptParameters, this.m_aPromptParametersComplex); document.body.appendChild(v_elForm); // if include performance details is selected, create an xml http request // and manually pass the X-CA-IPA header. if (this.m_oRVFormParameters.IncludePerformance == "true") { // concatenate the name and values into an array of name=values var v_saFormData = []; for (var i = 0; i < v_elForm.elements.length; i++) { var v_sElementName = v_elForm.elements[i].name; var v_sElementValue = v_elForm.elements[i].value; v_saFormData.push(encodeURIComponent(v_sElementName) + '=' + encodeURIComponent(v_sElementValue)); } // encode formData elements into one string var v_sPostData = v_saFormData.join('&').replace(/%20/g, '+'); var v_http = new XMLHttpRequest(); var v_sBaseURI = v_elForm.action; v_http.onreadystatechange = function() { // insert server response into the classic viewer iFrame if (this.readyState == 4 && this.status == 200) { var v_nClassicViewerIframe = document.getElementsByName("_classicViewer")[0]; var v_nIframeDocument = v_nClassicViewerIframe.contentDocument; var v_sHeadWithBase = ''; var v_sHtmlText = v_http.responseText.replace(//, v_sHeadWithBase); v_nIframeDocument.open(); v_nIframeDocument.write(v_sHtmlText); v_nIframeDocument.close(); } }; v_http.open(v_elForm.method, v_elForm.action); v_http.setRequestHeader('Cache-Control', 'max-age=0'); v_http.setRequestHeader('Upgrade-Insecure-Requests', '1'); v_http.setRequestHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'); v_http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); v_http.setRequestHeader('X-CA-IPA', '1'); v_http.send(v_sPostData); } else { v_elForm.submit(); } document.body.removeChild(v_elForm); }, _setLocalSessionData : function (key, data) { if (typeof(Storage) !== "undefined") { localStorage.setItem(key, data); } else { console.log("Sorry! No Web Storage support..."); } }, loadClassicViewer: function () { //console.log("begin: cvContentView.loadClassicViewer" ); this.m_fOnLoadHandler = this._createAndSubmitNewForm.bind(this); // If the viewer responds with an error page then the _onLoad will not find the viewer object // so the div visibility won't be adjusted. // Use the error page OnErrorPage callback to detect this and ensure the error page is displayed. window.OnErrorPage = function() { this._showViewer(); }.bind(this); window[this.m_sCreateCallback] = this._onClassicViewerCreated.bind(this); var v_oMRU; if (this.getType() == "output") { if (this.reportProperties) { v_oMRU = $.extend(true, {}, this.reportProperties); } } else { if (this.cmProperties) { v_oMRU = $.extend(true, {}, this.cmProperties); } } if (v_oMRU) { console.log('add to MRU'); this.glassContext.getSvc('.Content').then(function(contentSvc) { contentSvc.addToMRU(v_oMRU); }); } // Instrumentation wants specific terms // Some glass actions won't be instrumented var v_oGlassActionToInstrumentationEventType = { 'run' : 'Ran Process', 'viewOutput': 'Read Object', }; var v_sInstrumentationType = v_oGlassActionToInstrumentationEventType[ this.action ]; if (v_sInstrumentationType && this.glassContext) { var v_oInstrumentationService = this.glassContext.getCoreSvc('.Instrumentation'); if (v_oInstrumentationService && v_oInstrumentationService.enabled) { var v_sType; var v_sId; if (this.type == 'output' && this.cmProperties) { // our opener code should fill in the parent chain // cmProperties.parent[0] is the reportVersion object // cmProperties.parent[0].parent[0] is the object that has output versions var v_oVersionedAncestor = this.cmProperties.parent[0].parent[0]; v_oVersionedAncestor = v_oVersionedAncestor.base || v_oVersionedAncestor; v_sType = v_oVersionedAncestor.type; v_sId = v_oVersionedAncestor.id; } else { v_sType = this.type; v_sId = this.id; } var v_oEvent = { type: v_sInstrumentationType, objectType: v_sType, object: v_sId, 'custom.viewer' : 'classic', 'custom.outputFormat' : this.format, milestoneName: v_sInstrumentationType + '_' + v_sType }; if (v_sInstrumentationType === v_oGlassActionToInstrumentationEventType.run) { v_oEvent['processType'] = 'Run a Report'; } v_oInstrumentationService.track( v_oEvent ); } } //console.log("end: cvContentView.loadClassicViewer" ); }, getCognosViewer: function() { if (this.m_oCognosViewer) { return this.m_oCognosViewer; } var v_oViewer = null; if (this.m_iframeClassicViewer && this.m_iframeClassicViewer.contentWindow) { v_oViewer = this.m_iframeClassicViewer.contentWindow.oCV_NS_ || this.m_iframeClassicViewer.contentWindow.oCVRS || this.m_iframeClassicViewer.contentWindow.oCV_THIS_; if (v_oViewer) { this.m_oCognosViewer = v_oViewer; } } return v_oViewer; }, /** * Return whether or not a classic viewer or an RDS prompt page is loaded. * This method is needed because the RDS prompt page used by collect parameter values does not have a real classic viewer so * getCognosViewer returns null in this scenario but we still need to know if a prompt page was loaded. */ isViewerLoaded: function() { return this.getCognosViewer() || (this.m_iframeClassicViewer && this.m_iframeClassicViewer.contentWindow && this.m_iframeClassicViewer.contentWindow.oCV); }, /* Get content is called by the SHARE team. See RTC #82310 they expect: .application { .storeID, // Store ID in the CM, null if report not saved .cmSearthPath, // Search Path in the CM of the report, null if report not saved .reportName, // report name .isModified // whether the report has beeen modified. Set to false. } .promptParameters{ .name, .value { inclusive, // true | false, use, // true | false, display, // "Run report with data", type // "simpleParmValueItem" } } As per RTC #82310, the json format depends on the type of parameter: // range (bound and unbound) parameters: [ { name: "string", value: [ { inclusive: boolean, type, start: { inclusive: boolean, use: "string", display: "string", }, end: { inclusive: boolean, use: "string", display: "string", } } ] } ] */ /* Story 86638 Note: You will know glass is making this type of getContent(options) request when the passed in options contains {mode:'bookmark'}. If the options argument is missing or does not contain "mode":"bookmark", you can interpret it as a "Full state" request. It's ok for both calls to return the same state. It's completely up to you and your implementation. */ getContent: function(options) { //console.log("cvContentView.getContent" ); return rsCommon.getContent( options, this, rsShareHelper ); }, getApplicationContent: function( v_oContent, v_oCmProperties ) { var application = {}; application.storeID = this.cmProperties && this.cmProperties.id; application.cmSearchPath = !rsCommon.isObjectOfType(this.cmProperties, 'output') && this.getSearchPath(); application.reportName = this.getTitle(); application.isModified = false; application.type = (this.cmProperties && this.cmProperties.type) || undefined; return application; }, parseXML: function(v_sXML) { if (window.DOMParser) { return (new window.DOMParser()).parseFromString(v_sXML, "text/xml"); } else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) { var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(v_sXML); return xmlDoc; } return null; }, getParametersFromViewer: function() { var v_aGlassParameters = []; try { if (this.getCognosViewer()) { var v_sXMLParameters = this.getCognosViewer().getExecutionParameters(); //console.log("Parameters = %s", v_sXMLParameters); var v_oDoc = this.parseXML(v_sXMLParameters); // get all the children nodes var v_nChildNode = v_oDoc.documentElement.firstChild; while (v_nChildNode) { // we are interested in the item nodes if (v_nChildNode.nodeName == "item") { var v_oGlassParamStruct = { name: "", value: [] }; // walk through the child nodes..looking for bus:name and bus:value var v_nItemChildNode = v_nChildNode.firstChild; while (v_nItemChildNode) { switch(v_nItemChildNode.nodeName) { // bus:name contains the parameter name case "bus:name" : v_oGlassParamStruct.name = this.getNodeText(v_nItemChildNode); break; // bus:value contains type, inclusive, display, and use case "bus:value": var v_nValueNode = v_nItemChildNode.firstChild; while (v_nValueNode) { // we are interested in the item nodes if (v_nValueNode.nodeName == "item") { var v_oGlassParamValue = this.getValuesFromBusValueNode(v_nValueNode.getAttribute("xsi:type"), v_nValueNode.childNodes); v_oGlassParamStruct.value.push(v_oGlassParamValue); } v_nValueNode = v_nValueNode.nextSibling; } break; } v_nItemChildNode = v_nItemChildNode.nextSibling; } v_aGlassParameters.push(v_oGlassParamStruct); } v_nChildNode = v_nChildNode.nextSibling; } } } catch (err) { console.log("cvContentView.getParametersFromViewer failed (%s) ", err.message); } //console.log("GlassParameters = ", v_aGlassParameters); return v_aGlassParameters; }, /** * This method gets the parameters from the application * This is a public method used by other components and as such * must be defined in both rs and cv content views. */ getParameterValues : function( bStripCredentials ) { var v_aParameterValues = this.getParametersFromViewer(); if (bStripCredentials && v_aParameterValues) { var v_aParameters = []; if (v_aParameterValues.length > 0) { v_aParameterValues.forEach( function(v_oParameter) { if (!bStripCredentials || !v_oParameter || !v_oParameter.name || v_oParameter.name.indexOf("credential:") != 0) { v_aParameters.push( v_oParameter ); } }); v_aParameterValues = v_aParameters; } } return v_aParameterValues; }, getValuesFromBusValueNode: function(v_sType, v_nlNodeList) { var v_oGlassParamValue = {}; var v_oStartEnd = {}; // strip off the bus: portion v_oGlassParamValue.type = v_sType.substring(4); for ( var j = 0; j < v_nlNodeList.length; j++ ) { var v_nNode = v_nlNodeList[ j ]; var v_bStart = false; v_oStartEnd = {}; // empty switch(v_nNode.nodeName) { case "bus:start": v_bStart = true; // continue case "bus:end": // get all the children nodes var v_nChildNode = v_nNode.firstChild; while (v_nChildNode) { switch(v_nChildNode.nodeName) { case "bus:inclusive" : v_oStartEnd.inclusive = (this.getNodeText (v_nChildNode) == "true") ? true : false; break; case "bus:display" : v_oStartEnd.display = this.getNodeText (v_nChildNode); break; case "bus:use" : v_oStartEnd.use = this.getNodeText (v_nChildNode); break; } v_nChildNode = v_nChildNode.nextSibling; }; // set start or end in the v_oGlassParamValue if (v_bStart) { v_oGlassParamValue.start = v_oStartEnd; } else { v_oGlassParamValue.end = v_oStartEnd; } break; case "bus:inclusive" : v_oGlassParamValue.inclusive = (this.getNodeText (v_nNode) == "true") ? true : false; break; case "bus:display" : v_oGlassParamValue.display = this.getNodeText (v_nNode); break; case "bus:use" : v_oGlassParamValue.use = this.getNodeText (v_nNode); break; } } // Share is looking for "inclusive" for a range value, as per OM. If missing (seems to always be missing), just add a inclusive=true if ( v_oGlassParamValue.inclusive === undefined && (v_oGlassParamValue.start !== undefined || v_oGlassParamValue.end !== undefined) ) { v_oGlassParamValue.inclusive = true; } return v_oGlassParamValue; }, getNodeText: function(v_nNode) { var ATTRIBUTE_NODE = 2; return v_nNode && (v_nNode.nodeType == ATTRIBUTE_NODE ? v_nNode.nodeValue : v_nNode.textContent); }, deactivate: function() { this.m_bDeactivated = true; this.glassContext.unlockGlass(); //console.log("cvContentView.deactivate()"); }, /** * This method is called from the iframe onunload event. * The problem is that the viewer release method results in a synchronous XMLHttpRequest which browsers complain about. * Ideally we should be using the glass remove() method instead. But when glass calls remove, they have already * removed the content view div which means our iframe has already been unloaded so there is no longer a viewer * on which we can call release(). */ _release: function() { var v_oViewer = this.getCognosViewer(); if (v_oViewer) { // Once viewer releases it's conversation, it is no longer functional // so ensure we can no longer talk to it. this.m_oCognosViewer = null; try { // only call release if the viewer isn't keeping the session alive if( !v_oViewer.getKeepSessionAlive() ) { v_oViewer.release(); delete window[this.m_sCreateCallback]; } } catch(e) { } } this.m_oAppView = null; }, canRun: function() { // this perspective is only used when a report is run return true; }, /** * Called by global parameter flyout to determine what parameters are used by the currently active view. * @return Array of parameter names currently in use by the report. */ getParameters: function() { var v_aParameters = this.getParametersFromViewer(); return rsPromptParameters.convertParameterArrayToObject(v_aParameters); }, /** * Called by global parameter flyout to let content view know what global parameters have changed. * The method determines if any of the modified global parameters are used by the report and if so * the report is refreshed. * @param v_oGlobalParameters The global parameters that have been modified. * @param glassContext The glass context used to access complete global parameters. */ updateGlobalParameters: function( v_oNewGlobalParameters, v_oGlassContext ) { var v_oCognosViewer = this.getCognosViewer(); if (v_oCognosViewer && v_oCognosViewer.updateGlobalParameters && v_oNewGlobalParameters) { var v_aGlobalParametersDelta = rsCommon.convertToArray( v_oNewGlobalParameters ); v_oCognosViewer.updateGlobalParameters( JSON.stringify( v_aGlobalParametersDelta ) ); } }, /** * Called by Collaboration to retrieve the DOM node for screen capturing and report's name. * @returns {array} Array of objects containing the DOM element and the name. */ getShareableItemsForCollaboration: function() { // console.log('cvContentView.getShareableItemsForCollaboration'); if (this.m_bFullyInitialized && this.m_iframeClassicViewer) { var doc = this.m_iframeClassicViewer.contentDocument; var body = doc.body; return [{ el: body, label: this.m_sTitle }]; } return []; }, /** * setFocus will be called whenever glass want to put focus inside contentView. * here, the code will check if clasical viewer is loaded then focus inside report viewer */ setFocus: function setFocus() { if (this.isViewerLoaded()) { var doc = this.m_iframeClassicViewer.contentWindow.document; var rvContent = doc.getElementById("RVContent" + this.getCognosViewer().getId()); if (rvContent) { var el = this.getFirstFocus(rvContent); if (el) { el.focus(); } } } else { cvContentView.inherited('setFocus', this, arguments); } }, getFirstFocus:function(v_elTop, v_elParent) { var v_elParent = v_elParent || v_elTop; var v_elFocus; var v_iLength = v_elParent.childNodes.length; for ( var i = 0; i < v_iLength && !v_elFocus; i++ ) { var v_elChild = v_elParent.childNodes[i]; var v_bDisabled = ( v_elChild.disabled || ( v_elChild.getAttribute && ( v_elChild.getAttribute( "disabled" ) == "true" || v_elChild.getAttribute( "aria-disabled" ) == "true" ) ) ); if ( ( !v_bDisabled || this.isToolbarButton( v_elChild ) ) && v_elChild.style && v_elChild.style.visibility != "hidden" && v_elChild.style.display != "none" ) { if ( v_elChild.nodeName == "INPUT" && v_elChild.type == "radio" ) { v_elFocus = this.getCheckedOrFirstRadioButtonInGroup( v_elTop, v_elChild.getAttribute( "name" ) ); } else if ( this.isOKToFocus( v_elChild ) ) { v_elFocus = v_elChild; } else { v_elFocus = this.getFirstFocus( v_elTop, v_elChild ); } } } return v_elFocus; }, isToolbarButton: function( el ) { if ( el.getAttribute ) { var v_sRole = el.getAttribute( "role" ); if ( v_sRole == "button" || v_sRole == "combobox" ) { for ( ; el; el = el.parentNode ) { if ( el.getAttribute && el.getAttribute( "role" ) == "toolbar" ) { return true; } } } } return false; }, getCheckedOrFirstRadioButtonInGroup: function( v_elContainer, v_sRadioGroupName ) { var v_elRadioChecked; var v_elFirstRadio; var nl = v_elContainer.getElementsByTagName( "INPUT" ); var v_iLength = nl.length; for ( var i = 0; i < v_iLength; i++ ) { var v_el = nl.item( i ); if ( v_el.type == "radio" && v_el.name == v_sRadioGroupName ) { if ( !v_elFirstRadio ) { v_elFirstRadio = v_el; } if ( v_el.checked == true ) { v_elRadioChecked = v_el; break; } } } return v_elRadioChecked || v_elFirstRadio; }, isOKToFocus: function( v_el ) { var v_iTabindex = parseInt( v_el.getAttribute( "tabIndex" ) ); return ( v_iTabindex >= 0 || ( ( v_el.nodeName == "INPUT" || v_el.nodeName == "BUTTON" || v_el.nodeName == "TEXTAREA" ) && v_iTabindex != -1 ) ); } }); return cvContentView; });