027-2014 top seven techniques to create sas® web...

20
1 Paper SAS027-2014 Top Seven Techniques to Create SAS® Web Applications Yogen Joshi, SAS Institute Inc., Cary, NC ABSTRACT Do you often create SAS® web applications? Do you need to update or retrieve values from a SAS data set and display those values in a browser? Do you need to show the results of a SAS® Stored Process in a browser? Are you finding it difficult to figure out how to pass parameters from a web page to a SAS Stored Process? If you answered yes to any of these questions, then look no further. Techniques shown in this paper include: How to take advantage of JavaScript and minimize your use of the PUT statement How to use JavaScript and XMLHTTPRequest to call a SAS Stored Process from your web page How to pass parameters from a web page to a SAS Stored Process and from a SAS Stored Process back to the web page How to use simple Ajax to refresh and update a specific part of a web page without reloading the entire page How to apply CSS style sheets on your web page How to use some of the latest HTML5 features, such as drag and drop How to display run-time graphs in your web page using StatGraph templates and the SGRENDER procedure This paper contains sample code to demonstrate each technique. INTRODUCTION At some point, you might have created ad hoc web contents using SAS web application. The web application could include tabular data, a SAS graph, and more. With the advent of HTML5, there are more opportunities to create interesting and dynamic web contents by combining SAS technologies (such as SAS macros and SAS Stored Processes) and the HTML5 technology. This paper shows the different techniques you can use to create an interactive SAS web application. THE APPLICATION To demonstrate the various techniques, I have created a web application called Regression Application. This application displays the results from the CORR and REG procedures. The application prompts you to select a data set, which contains a dependent variable and a number of independent variables. The goal is to find the correlation between the dependent variable and each of the independent variables. Correlation refers to the relationship of dependence between two variables. This relationship value could range from -1 to +1. The web application uses the CORR procedure in SAS to determine this relationship value. However in reality, the prediction of the dependent variable depends on not a single variable but on a combination of variables. The idea is to model that combination of independent variables so that the value of the dependent variable (in this example, the sales forecast) is close to the actual sales for the previous year. In the web application, the user can drag and drop the set of independent variables and check the trend line to see if this line matches the line for last year’s actual sales. The goal is to choose a combination of values, so that the predicted sales closely match the actual sales. Step 1: Select a data set In the web application, select a data set from the drop-down list.

Upload: vuongdung

Post on 13-Apr-2018

217 views

Category:

Documents


1 download

TRANSCRIPT

1

Paper SAS027-2014

Top Seven Techniques to Create SAS® Web Applications

Yogen Joshi, SAS Institute Inc., Cary, NC

ABSTRACT

Do you often create SAS® web applications? Do you need to update or retrieve values from a SAS data set and display those values in a browser? Do you need to show the results of a SAS® Stored Process in a browser? Are you finding it difficult to figure out how to pass parameters from a web page to a SAS Stored Process?

If you answered yes to any of these questions, then look no further. Techniques shown in this paper include:

How to take advantage of JavaScript and minimize your use of the PUT statement

How to use JavaScript and XMLHTTPRequest to call a SAS Stored Process from your web page

How to pass parameters from a web page to a SAS Stored Process and from a SAS Stored Process back tothe web page

How to use simple Ajax to refresh and update a specific part of a web page without reloading the entire page

How to apply CSS style sheets on your web page

How to use some of the latest HTML5 features, such as drag and drop

How to display run-time graphs in your web page using StatGraph templates and the SGRENDER procedure

This paper contains sample code to demonstrate each technique.

INTRODUCTION

At some point, you might have created ad hoc web contents using SAS web application. The web application could include tabular data, a SAS graph, and more. With the advent of HTML5, there are more opportunities to create interesting and dynamic web contents by combining SAS technologies (such as SAS macros and SAS Stored Processes) and the HTML5 technology. This paper shows the different techniques you can use to create an interactive SAS web application.

THE APPLICATION

To demonstrate the various techniques, I have created a web application called Regression Application. This application displays the results from the CORR and REG procedures. The application prompts you to select a data set, which contains a dependent variable and a number of independent variables. The goal is to find the correlation between the dependent variable and each of the independent variables. Correlation refers to the relationship of dependence between two variables. This relationship value could range from -1 to +1. The web application uses the CORR procedure in SAS to determine this relationship value. However in reality, the prediction of the dependent variable depends on not a single variable but on a combination of variables. The idea is to model that combination of independent variables so that the value of the dependent variable (in this example, the sales forecast) is close to the actual sales for the previous year. In the web application, the user can drag and drop the set of independent variables and check the trend line to see if this line matches the line for last year’s actual sales. The goal is to choose a combination of values, so that the predicted sales closely match the actual sales.

Step 1: Select a data set In the web application, select a data set from the drop-down list.

2

Display 1. Select a data set

Step 2: Add data After you select the data set, the web application generates a table with as many cells as the number of independent variables in the data set. Each cell has Add Data button.

Display 2. Add Data

Step 3: Select the variables In a cell, click Add Data to view the list of available variables and the proc corr button. Even though there is a stored process call, notice how the proc corr button and the drop-down lists appear without refreshing or reloading the

page. For this example, select the dependent variable (Sales) in the top drop-down list. In the bottom drop-down lists, select the independent variables as shown in the following screen shot.

3

Display 3. Select the variables

Step 4: Calculating the correlation In each cell, click proc corr. The dependent variable (Sales) and one of the independent variables (either Store

Area, Inventory, Advert, Area, or Competing Store depending on the cell) are passed as parameters to the SAS stored process. The stored process uses the CORR procedure to calculate the correlation coefficient. The stored process sends the coefficient value back to the web page. Notice that the coefficient value and thumbnail appear on the page without reloading the page.

Display 4. Calculating the Correlation

Step 5: Calculating the Regression Drag and drop the coefficient values to the area under the proc reg button. When you click prog reg, the REG

procedure runs. The results include a graph that shows the regression line and a table. In the graph, the red line is the calculated regression data for the various years, and the blue line is the actual sales for those years. The set of variables for which the actual and calculated trend lines match are used to model the regression.

4

Display 5. Calculating the Regression

ORGANIZE YOUR SAS CODE You can organize your code into these files:

- SAS stored process files - JavaScript files - CSS files - image files

You can organize the files using the directory structure in Figure 1. This demo is using the second maintenance release of SAS 9.3. You can use any version of SAS® Enterprise Business Intelligence, as long as the files are in a location where server can locate them.

The JavaScript file is saved in the js directory. Style sheets are saved in the css directory, and images and xml

documents are stored in the img directory.

- server | |-- SASServer1 | |--deploy_sas | |--sas.storedprocess9.3.ear | |--sas.storedprocess.war | |--css |--img |--js

5

Figure 2. File organization

Let us start with the landing page (for the web application), which is created by running a SAS stored process called Regression.sas.

data _NULL_;

put '<html>';

put '<head>';

put '<script src="js/yScript.js"> </script> ';

put '<script src="js/yRegression.js" > </script> ';

put '<link rel="stylesheet" type="text/css" href="css/yStyle.css">';

put '</head>';

run;

data _null_;

file _webout;

put '<body id="yBody">';

----- html code -----

----- html code -----

put ' </body>';

put '</html>';

run;

This is the minimum amount of code that you need to create the landing page. To view all of the code, see Appendix

2. yScript.js and yRegression.js are two JavaScript files saved in the js directory. yStyle.css is the

CSS file saved in the css directory. Observe the HTML syntax for including the Javascript and CSS files in your web

page. This is the only place where a PUT statement is required. No more PUT statements after this! Next register this SAS stored process into the metadata. For more information about how to register a stored process, see Appendix 1.

Technique 1: How to take advantage of JavaScript and minimize the use of PUT statements

To minimize the use of the PUT statement, do not put the JavaScript and CSS content inline or as a part of the SAS stored process. By externalizing and separating the Javascript and CSS files, the code is less cluttered and easier to maintain.

put '<script src="js/yScript.js"> </script> ';

put '<script src="js/yRegression.js" > </script> ';

put '<link rel="stylesheet" type="text/css" href="css/yStyle.css">';

If you look at the full code in the appendix, you see that the whole application uses less than 40 PUT statements.

Technique 2: How to use JavaScript and XMLHTTPRequest to call a SAS Stored Process from your web page

In Display 1 when you select a data set from the drop-down list, the stored process CorrStp.sas needs to be invoked, and the data set name needs to be sent to the stored process for processing. The following code snippet from the yScript.js file shows this technique.

/*First function to be called onChange() of dropdown containing data set names*/

1. function generateData(){

2. var dsName=document.getElementById('dataset').value;

3. var cols=0;

/*This xmlHttpRequest is used call the stored process CorrStp.*/

4. var xhr=false;

5. if(window.XMLHttpRequest){

6. xhr = new XMLHttpRequest();

7. }

8. }

9 xhr.open('GET',

'?odsstyle=Plateau&dataName='+dsName+'&_program=%2FProducts%2Fstp%2FCorrStp', true);

6

10 xhr.send(null);

} Here is an explanation of the code:

Line 2 is called when a data set is selected from the drop-down list. The value selected from the drop-down list is stored in the dsName variable.

Lines 5 and 6 create an instance of XMLHttpRequest.

Line 9 is a call to the open() method, which is used to invoke the stored process. The general specification is xhr.open('GET|PUT|POST|DELETE', 'URL', true|false). The URL specified in the

open() method is the URL that invokes the stored process that you registered in the metadata. In this example, the stored process is registered at /Products/stp/CorrStp.sas.

Techniques 3 and 4: How to pass parameters from web page to a SAS Stored Process and from a SAS Stored Process back to the web page and How to use simple Ajax to refresh and update a specific part of a web page without reloading the entire page

For these techniques, see Display 1 and Display 4. As shown in technique 1, when you select a data set name from the drop-down list, you are not only invoking the stored process at /Products/stp/CorrStp.sas, but you are

also passing the data set name as a parameter (dsName) in the URL:

‘?odsstyle=Plateau&dataName='+dsName+'&_program=%2FProducts%2Fstp%2FCorrStp’

The stored process CorrStp takes the data set name and calculates these values:

- the number of columns using the CONTENTS procedure - the correlation between the dependent variable and all the independent variable using the CORR procedure.

The output is saved as an XML file in a location that can be read by the server.

Here is the code for the CorrStp.sas stored process: /*dataName comes from url params*/

1. %let table = &dataName;

/*Calculates the number of cols for the table*/

/*Calls %calcCorr macro*/

2. %macro contents(table);

proc contents data = neon.&table

out = neon.vars(keep = varnum name)

noprint;

run;

proc sql noprint;

select name

into :cols separated by ' '

from neon.vars

order by varnum;

quit;

%let allCols=&cols;

%let colCount = %sysfunc(countc(&cols, ' '));

%let dependentVar=%scan(&cols, 1);

%let cols = %sysfunc(tranwrd(&cols, &dependentVar,));

%calcCorr(&cols, &dependentVar, &table);

%mend contents;

/*Calculates correlation for a given table and writes it to table neon.corr*/

/*Creates an xml output for neon.corr in below mentioned location so that the AJAX can

reference the xml using url*/

/*libname xmlout xml C:\..\img\corr.xml*/ 3. %macro calcCorr(cols, dependentVar, table);

7

proc corr data=neon.&table outp=neon.corr;

var &cols;

with &dependentVar;

run;

quit;

4. data xmlout.grades;

set neon.Corr;

where _TYPE_='CORR';

run;

%mend calcCorr;

Here is an explanation of the code:

- In line 1, the value of of the URL parameter, dataName, is assigned to the variable table. - In line 2, %macro contents creates the data set Neon.Vars.

- In line 3, %macro calcCorr calculates the correlation and writes the correlation values to the Neon.Corr

data set. - In line 4, the correlation values are written to an external XML file, which is shown below.

<?xml version="1.0" encoding="windows-1252" ?>

<TABLE>

<GRADES>

<_TYPE_> CORR </_TYPE_>

<_NAME_> Sales </_NAME_>

<StoreArea> 0.89409208080468 </StoreArea>

<Inventory> 0.94550362547614 </Inventory>

<Advert> 0.91402407460007 </Advert>

<Area> 0.95368306035399 </Area>

<CompetingStore> -0.91223639210945 </CompetingStore>

</GRADES>

</TABLE>

Output 1. Output from xmlout

Here is the relevant code from the yScript.js file:

var xhr=false;

xhr = new XMLHttpRequest();

xhr.onreadystatechange=function(){

if (xhr.readyState===4 && xhr.status===200){

xmlDoc=xhr.responseXML;

var x=xmlDoc.getElementsByTagName(varY)

cellId.innerHTML = "<div id='blah' draggable='true' ondragstart='drag(event)'>"

+cellId.innerHTML + '<BR>'

+x[0].textContent.substring(0,6);

var image = document.createElement('img');

image.setAttribute('id', 'regImg_'+cellId.id);

image.setAttribute('src', img/regression.png');

cellId.appendChild(image);

}

};

xhr.open('GET', img/corr.xml', true);

xhr.send(null);

Here is an explanation of the code:

8

The onreadystatechange event specifies what happens when the server response is ready to be processed.

xhr.open() reads the XML, and assigns the content to the xmlDoc object by using xmlDoc=xhr.responseXML.

Property Description

onreadystatechange Stores a function (or the name of a function) to be called automatically each time the readyState property changes

readyState Holds the status of the XMLHttpRequest. Changes from 0 to 4: 0: request not initialized 1: server connection established 2: request received 3: processing request 4: request finished and response is ready

status 200: "OK" 404: Page not found

Technique 5: How to apply CSS style sheets on your web page

Display 2 shows a set of Add Data buttons. Although it looks like an HTML button or an image button, the Add Data

button is actually a simple anchor tag, and the anchor text ‘Add Data’ is styled to look like a button. When you hover or click the Add Data button, you can see the changes in the shadow effect and gradient. The detailed CSS code for

this button is in Appendix 3. <td id=”d03”>

<a id=”a_d03” class=”button add” href=”#” onclick=”createCombo(d03,5)”>Add

Data</a>

<br>

</td>

Technique 6: How to use some of the latest HTML5 features, like drag and drop

You can use the latest HTML5 drag and drop functionality to create interactive web content. In Display 5, you drag two or more correlation values below the proc reg button. First, you must declare the text that can be dragged in the

<div> tag, as shown below.

<td id="d00" class="generalTxt">

<div id="bar" draggable="true" ondragstart="drag(event)">

"Area"

<br>

"Sales"

"0.920"

</div>

</td>

9

//===========drag and drop code===========

function allowDrop(event){

event.preventDefault();

}

function drag(event){

event.dataTransfer.setData("Text",event.target.innerHTML);

}

function drop(event){

event.preventDefault();

var data=event.dataTransfer.getData("Text");

var regressionList = document.getElementById('regList'); /* will not exist before

1st dnd */

var regList = generateRegressionList(data);

regressionList.appendChild(regList);

}

Here is an explanation of the code:

First, you declare the text that can be dragged within the <div> tag and set the attribute

draggable=’true’.

By default, data elements cannot be dropped in other elements. To allow a drop, use event.preventDefault() to prevent the default handling of the element.

The ondragstart attribute calls the drag(event) function to specify what data can be dragged.

The dataTransfer.setData() method sets the data type and the value of the dragged data.

Technique 7: How to display run-time graphs in your web page using StatGraph templates and the SGENDER procedure

Here is the relevant code from the RegressionImgStp.sas file:

proc template;

define statgraph Graph;

ods graphics on / imagename= 'regplot';

ods listing close;

ods html path= 'C:\..\img'

gpath= 'C:\.. \img' (url='')

file= 'timeSeries.html';

proc sgrender data=NEON.Time&table template=Graph;

dynamic _YEAR="YEAR" _SALES="SALES" _YEAR2="YEAR" _SALE1="model";

run;

ods html close;

ods listing;

10

Here is an explanation of the code:

Use the TEMPLATE procedure to define statgraph.

Specify the name of the image as regplot.png, and specify the path where the image is saved.

Specify the HTML file name as timeSeries.html. This HTML wraps the regplot.png image. For all of the code, see Appendix 4.

CONCLUSION

Using HTML 5 and SAS technologies (such as SAS macros and SAS Stored Processes), you can create interesting and dynamic web applications. Both technologies can be used to complement each other and to create interactive web contents.

ACKNOWLEDGMENTS

I would like to thank Prasad Kulkarni for inspiring me to do more.

RECOMMENDED READING

Ajax Patterns. (2014) XMLHttpRequest Available at http://ajaxpatterns.org/XMLHttpRequest_Call.

Manharry, Dan. “The HTML5 Drag and Drop API.” developerFusion, July 25, 2012. Available at http://www.developerfusion.com/article/144828/the-html5-drag-and-drop-api/

Underwood, Paul. “CSS Buttons With Icons But No Images.” Available at http://www.paulund.co.uk/css-buttons-

with-icons-but-no-images. .

CONTACT INFORMATION

Your comments and questions are valued and encouraged. Contact the author::

Yogen Joshi 100 SAS Campus Drive Cary, NC 27513 SAS Institute Inc. [email protected] http://www.sas.com

SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration.

Other brand and product names are trademarks of their respective companies.

11

Appendix 1: Register a SAS Stored Process in the Metadata

1. Open SAS Management Console and connect to a server. 2. In the Folders tab, right-click the Products folder and select New --> Folder. 3. Create a new folder called stp. You are going to register the stored process under Products/stp. 4. Right-click the stp folder and select New -->Stored Process. The New Stored Process Wizard opens. 5. In the first step, specify the name of the stored process (for example, CorrStp). Click Next.

6. In the second step, set the configuration options as shown below.

12

Appendix 2: Code for yScript.js File

var primary='';/*forcasted variable(ex. sales, forecast)*/

/*generatetable

* Based uponet the number of dependant variables , creates tables with those many

cells.

* Creates <a href with button class with +. id = a_d_00... a_d_05

* a_d_10... a_d_15

* on click of button createCombo(cell id)

* */

function generateTable(cols){

/*Check if table already exists. If true than delete. Want only 1 table.*/

var yTb=document.getElementById('yT');

if(yTb!==null){

yTb.parentNode.removeChild(yTb);

}

var x;

if(cols===0 || cols===null || cols===''){

x=document.getElementById("e4");

}else{

x=cols;

}

var nCells = Number(x);

var nrCols=0;

var nrRows=0;

if(nCells<=5){

var nrCols=nCells;

var nrRows=1;

}

if(nCells>5 & nCells<=10 ){

var nrCols=5;

var nrRows=2;

}

if(nCells>10 & nCells<=15 ){

var nrCols=5;

var nrRows=3;

}

var root=document.getElementById('yBody');

var tab=document.createElement('table');

tab.className="ytable";

tab.setAttribute('id','yT');

var tbo=document.createElement('tbody');

var row, cell, anchor;

for(var i=0;i<nrRows;i++){

row=document.createElement('tr');

for(var j=0;j<nrCols;j++){

cell=document.createElement('td');

cell.setAttribute('id', 'd'+i+j);

anchor=document.createElement('a');

anchor.setAttribute('href', '#');

anchor.setAttribute('id', 'a_d'+i+j);

anchor.setAttribute('onclick', 'createCombo(d'+i+j +','+cols+')');

anchor.setAttribute('class', 'button add');

anchor.innerHTML='Add Data';

cell.appendChild(anchor);

var br=document.createElement('BR');

cell.appendChild(br);

row.appendChild(cell);

13

}

tbo.appendChild(row);

}

tab.appendChild(tbo);

root.appendChild(tab);

/*Create another table called canvas, below to the table, for dnd regression cells*/

var tabCanvas=document.createElement('table');

tabCanvas.className="yCanvasTable";

tabCanvas.setAttribute('id','yCT');

var tbodyCanvas = document.createElement('tbody');

var rowCanvas = document.createElement('tr');

var cellCanvas1 = document.createElement('td');

cellCanvas1.setAttribute('id', 'cell1');

cellCanvas1.setAttribute('style', 'width: 25%');

cellCanvas1.setAttribute('ondrop', 'drop(event)');

cellCanvas1.setAttribute('ondragover', 'allowDrop(event)');

var regressionList = document.createElement('ul');//<ul>

regressionList.setAttribute('id', 'regList');//<ul>

var listItem = document.createElement('li');

regressionList.appendChild(listItem);//<ul> <li>

var regButton = document.createElement('a');//<a class="ovalbutton"

href="#"><span>Submit</span></a>

regButton.setAttribute('class', 'regressionButton');

regButton.setAttribute('href', '#');

regButton.setAttribute('id', 'regB');

var regSpan = document.createElement('span');regSpan.innerHTML='proc reg';

regButton.appendChild(regSpan);

regButton.setAttribute('onclick', 'procReg()');

var brk = document.createElement('br');

listItem.appendChild(regButton);

listItem.innerHTML = listItem.innerHTML+'<br><br>';

cellCanvas1.appendChild(regressionList);//<td><ul>

var cellCanvas2 = document.createElement('td');

cellCanvas2.setAttribute('id', 'cell2');cellCanvas2.setAttribute('style', 'width:

40%');

var cellCanvas3 = document.createElement('td');

cellCanvas3.setAttribute('id', 'cell3');cellCanvas3.setAttribute('style', 'width:

35%');

rowCanvas.appendChild(cellCanvas1);

rowCanvas.appendChild(cellCanvas2);

rowCanvas.appendChild(cellCanvas3);

tbodyCanvas.appendChild(rowCanvas);

tabCanvas.appendChild(tbodyCanvas);

root.appendChild(tabCanvas);

}

function createComboInternal(cols, colName, combox, comboy, _cell){

var selOptx = new Array(cols);

var selOpty = new Array(cols);

for(var i=0; i<=cols; i++){//create 2 combos

selOptx[i] = document.createElement('option');

selOptx[i].setAttribute('value',colName[i]);

selOptx[i].text=colName[i];

selOpty[i] = document.createElement('option');

selOpty[i].setAttribute('value',colName[i]);

selOpty[i].text=colName[i];

combox.appendChild(selOptx[i]);

comboy.appendChild(selOpty[i]);

14

}//for

var cell=document.getElementById(_cell.id);

cell.appendChild(combox);

var br=document.createElement('BR');

cell.appendChild(br);

cell.appendChild(comboy);

cell.removeChild(cell.firstChild);

var anchor=document.createElement('a');

anchor.setAttribute('href', '#');

anchor.setAttribute('id', 'a_b_'+cell.id);

anchor.setAttribute('onclick', 'doCorr('+_cell.id +')');

anchor.setAttribute('class', 'button');

anchor.innerHTML='proc corr';

if(cell.firstChild)

cell.insertBefore(anchor, cell.firstChild);

else

cell.appendChild(anchor);

}

function createCombo(_cell, cols){

var combox=document.createElement('select');

var comboy=document.createElement('select');

combox.setAttribute('id', 'combox');

comboy.setAttribute('id', 'comboy');

combox.setAttribute('class', 'combo');

comboy.setAttribute('class', 'combo');

var colName = new Array();

/*This xmlHttpRequest is used to read the colName.txt file containing column name*/

var xmlhttp=false;

if(window.XMLHttpRequest){

xmlhttp = new XMLHttpRequest();

} else if(window.ActiveXObject){

xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

}

xmlhttp.onreadystatechange=function(){

if (xmlhttp.readyState===4 && xmlhttp.status===200){

colStr=xmlhttp.responseText;

colName=colStr.split(' '); primary = colName[0];

createComboInternal(cols, colName, combox, comboy, _cell);

}

};

xmlhttp.open('GET',

'http://sinm605a.na.sas.com:8080/SASStoredProcess/img/colName.txt', false);

xmlhttp.send(null);

}

function doCorr(cellId){

var cx = cellId.querySelector('#combox');

var cy = cellId.querySelector('#comboy');

cellId.setAttribute('class', 'generalTxt');

cellId.innerHTML = cy.options[cy.selectedIndex].value;

cellId.innerHTML = cellId.innerHTML + '<BR>' +cx.options[cx.selectedIndex].value;

var varX = cx.options[cx.selectedIndex].value;

var varY = cy.options[cy.selectedIndex].value;

ajaxCallCorrStp(varX, varY, cellId);

}

/*create ajax request to run stored process*/

15

function ajaxCallCorrStp(varX, varY, cellId){

var xhr=false;

if(window.XMLHttpRequest){

xhr = new XMLHttpRequest();

} else if(window.ActiveXObject){

xhr = new ActiveXObject("Microsoft.XMLHTTP");

}

xhr.onreadystatechange=function(){

if (xhr.readyState===4 && xhr.status===200){

xmlDoc=xhr.responseXML;

if(varY.indexOf("\r")>0){

varY= varY.replace(/(\r\n|\n|\r)/gm,""); //For some reason the last

item comes as 'CompetingStore\r\n'

}

var x=xmlDoc.getElementsByTagName(varY);

//cellId.innerHTML = cellId.innerHTML + '<BR>' +x[0].textContent;

cellId.innerHTML = "<div id='blah' draggable='true'

ondragstart='drag(event)'>"+cellId.innerHTML + '<BR>'

+x[0].textContent.substring(0,6);

var image = document.createElement('img');

image.setAttribute('id', 'regImg_'+cellId.id);

image.setAttribute('src',

'http://sinm605a.na.sas.com:8080/SASStoredProcess/img/regression.png');

cellId.appendChild(image);

}

};

xhr.open('GET', 'http://sinm605a.na.sas.com:8080/SASStoredProcess/img/corr.xml',

true);

xhr.send(null);

}

//===========drag and drop code===========

function allowDrop(ev){

ev.preventDefault();

}

function drag(ev){

ev.dataTransfer.setData("Text",ev.target.innerHTML);

}

function drop(ev){

ev.preventDefault();

var data=ev.dataTransfer.getData("Text");

//ev.target.innerHTML=data; ** original code

var regressionList = document.getElementById('regList'); /* will not exist before

1st dnd */

var regList = generateRegressionList(data);

regressionList.appendChild(regList);

//ev.target.innerHTML = ev.target.innerHTML+ generateRegressionList(data);

//ev.target.innerHTML = generateRegressionList(data);

}

//============dnd end =============

/*Returns a list of dragged variables */

function generateRegressionList(newData){

var arrayData = newData.split('<br>');

var tabCell1 = document.getElementById('cell1');

var regressionList = document.getElementById('regList'); /*may or may not exist*/

if(regressionList === null || regressionList.innerHTML===''){

var regressionList = document.createElement('ul');

regressionList.setAttribute('id', 'regList');

16

tabCell1.appendChild(regressionList);

}

var image = document.createElement('img');

image.setAttribute('src',

'http://sinm605a.na.sas.com:8080/SASStoredProcess/img/delBut.png');

image.setAttribute('onclick', 'del(this.parentNode)');

var listItem = document.createElement('li');

listItem.appendChild(image);

listItem.innerHTML = listItem.innerHTML + ('&nbsp;'+arrayData[0]+ '|--

|'+arrayData[2]);

regressionList.appendChild(listItem);

tabCell1.appendChild(regressionList);

//return listItem.innerHTML;

return regressionList;

}

function del(element){

var elem = document.getElementById('regList');

elem.removeChild(element);

}

/**********************************

* function del(element){

var elem = document.getElementById('forum');

elem.removeChild(element);

}

<ul id="forum">

<li><img onclick="del(this.parentNode);" src="DeleteButton.jpg" alt="some

text" />&nbsp;Coffee</li>

*

*************************************/

/*First function to be called onChange() of combo containing data set names*/

function generateData(){

var dsName=document.getElementById('dataset').value;

var cols=0;

/*This xmlHttpRequest is used call the stored process CorrStp.*/

var xhr=false;

if(window.XMLHttpRequest){

xhr = new XMLHttpRequest();

} else if(window.ActiveXObject){

xhr = new ActiveXObject("Microsoft.XMLHTTP");

}

xhr.open('GET',

'http://sinm605a.na.sas.com:8080/SASStoredProcess/do?odsstyle=Plateau&dataName='+dsNam

e+'&_program=%2FProducts%2Fstp%2FCorrStp', false);

xhr.send(null);

/*This xmlHttpRequest is used to read the yCols.txt file containing column count*/

var xmlhttp=false;

if(window.XMLHttpRequest){

xmlhttp = new XMLHttpRequest();

} else if(window.ActiveXObject){

xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

}

xmlhttp.onreadystatechange=function(){

if (xmlhttp.readyState===4 && xmlhttp.status===200){

cols=xmlhttp.responseText;

}

};

xmlhttp.open('GET',

'http://sinm605a.na.sas.com:8080/SASStoredProcess/img/yCols.txt', false);

17

xmlhttp.send(null);

this.generateTable(cols);

}

18

Appendix 3: Code for the CSS Button

.button{

display: inline-block;

white-space: nowrap;

background-color: #ccc;

background-image: -webkit-gradient(linear, left top, left bottom, from(#eee),

to(#ccc));

background-image: -webkit-linear-gradient(top, #eee, #ccc);

background-image: -moz-linear-gradient(top, #eee, #ccc);

background-image: -ms-linear-gradient(top, #eee, #ccc);

background-image: -o-linear-gradient(top, #eee, #ccc);

background-image: linear-gradient(top, #eee, #ccc);

filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee',

EndColorStr='#cccccc');

border: 1px solid #777;

padding: 0 1.5em;

margin: 0.5em;

font: bold 0.75em/1.5em Arial, Helvetica;

text-decoration: none;

color: #333;

text-shadow: 0 1px 0 rgba(255,255,255,.8);

-moz-border-radius: .2em;

-webkit-border-radius: .2em;

border-radius: .2em;

-moz-box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3);

-webkit-box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0

rgba(0,0,0,.3);

box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3);

}

.button:hover{

background-color: #ddd;

background-image: -webkit-gradient(linear, left top, left bottom, from(#fafafa),

to(#ddd));

background-image: -webkit-linear-gradient(top, #fafafa, #ddd);

background-image: -moz-linear-gradient(top, #fafafa, #ddd);

background-image: -ms-linear-gradient(top, #fafafa, #ddd);

background-image: -o-linear-gradient(top, #fafafa, #ddd);

background-image: linear-gradient(top, #fafafa, #ddd);

filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',

EndColorStr='#dddddd');

}

.button:active{

-moz-box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset;

-webkit-box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset;

box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset;

position: relative;

top: 1px;

}

.button:focus{

outline: 0;

background: #fafafa;

}

.button:before{

background: #ccc;

background: rgba(0,0,0,.1);

float: left;

width: 1em;

19

text-align: center;

font-size: 1.5em;

margin: 0 1em 0 -1em;

padding: 0 .2em;

-moz-box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5);

-webkit-box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5);

box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5);

-moz-border-radius: .15em 0 0 .15em;

-webkit-border-radius: .15em 0 0 .15em;

border-radius: .15em 0 0 .15em;

pointer-events: none;

}

/* Hexadecimal entities for the icons */

.add:before{

content: "\271A";

}

20

Appendix 4: Code for RegressionImgStp.sas

%macro timeSeries();

proc template;

define statgraph Graph;

/*Used for Time series with overlay*/

dynamic _YEAR _SALES _YEAR2 _SALE1;

begingraph / designwidth=400 designheight=300;

entrytitle halign=center 'Actual and Calculated Sale';

layout lattice / rowdatarange=data columndatarange=data rowgutter=10

columngutter=10;

layout overlay / xaxisopts=( type=discrete);

seriesplot x=_YEAR y=_SALES / name='series' legendlabel='Actual Sales'

display=(markers) connectorder=xaxis lineattrs=(color=CX0000FF )

markerattrs=(weight=normal );

seriesplot x=_YEAR2 y=_SALE1 / name='series2' legendlabel='Calculated Sale'

display=(markers) connectorder=xaxis lineattrs=(color=CXFF0000 )

markerattrs=(color=CX639A21 symbol=X weight=normal );

endlayout;

sidebar / align=bottom spacefill=false;

discretelegend 'series' 'series2' / opaque=true border=true halign=center

valign=center displayclipped=true order=rowmajor;

endsidebar;

endlayout;

endgraph;

end;

run;

ods graphics on / imagename= 'regplot';

ods listing close;

ods html path= 'C:\jboss-eap-4.3\jboss-

as\server\SASServer1\deploy_sas\sas.storedprocess9.3.ear\sas.storedprocess.war\img'

gpath= 'C:\jboss-eap-4.3\jboss-

as\server\SASServer1\deploy_sas\sas.storedprocess9.3.ear\sas.storedprocess.war\img'

(url='')

file= 'timeSeries.html';

proc sgrender data=NEON.Time&table template=Graph;

dynamic _YEAR="YEAR" _SALES="SALES" _YEAR2="YEAR" _SALE1="model";

run;

ods html close;

ods listing;

%mend timeSeries;