Adjusting Notepad++ to work with an ASP VBScript project.

I have a large project in classic ASP, VBScript, and Javascript. It’s a hassle dealing with it because developer tools don’t support it that well or at all. I recently poked around the open source editor Notepad++ and got it adjusted to work decently well with the project.

Check out Notepad++ on the official website here.

In the past I had used Eclipse with an old VBScript ASP syntax highlighter. It wasn’t perfect, but worked alright overall. The main issue is that the highlighting stopped working every so often, and not to mention Eclipse is bulky and a pain to use for various reasons.

Here is a screenshot of Notepad++ adjusted to work with the project:
notepadpp

Here are my main adjustments:
“View” >> “Folder as Workspace”
“View” >> “Document Map”
“View” >> “Function List”
“Settings” >> “Preferences…” >> “Editing” >> “Display line number”

The function list feature won’t work without modification. My project is broken into a large number of “.asp” files and “.js” files. Neither extensions are linked to a function parser by default.

In this case, you have to modify the two functionlist.xml files. Thanks to some enterprising individuals on sourceforge.net, I was able to find a working parser for vbscript.

Here are the approximate locations of the files on Windows:
C:\Program Files (x86)\Notepad++
C:\Users\UserName\AppData\Roaming\Notepad++

Here are the two sections of xml I added to my functionlist.xml files:

<association langID="16" id="vb_function" />
<association langID="18" id="vb_function" />
<association langID="19" id="javascript_function" />

the langID relates to programming code type extensions. In this case 16 = “.vb”, 18 = “.asp”, and 19 = “.js”. I don’t need “.vb” right now, but I put it in there anyways in case I do open them in the future.

Here is the regex/parser code from the Sourceforge.net discussion that I used:

<parser 
  id="vb_function" 
  displayName="Visual Basic (.NET|Script|for Applications)"  
  commentExpr="(?m:((?&lt;=&apos;).*?$)|((?i:REM)([\t ].*?)?$))"> 
	<classRange
		mainExpr="(?s:(?&lt;SCOPE&gt;(?i:CLASS|TYPE)).*?(?i:END[\t ]\k&lt;SCOPE&gt;))"
		displayMode="node" >
	  <className>
		<nameExpr expr="(?i:CLASS|TYPE)[\t ]+(?:[A-Za-z_][\w]*\b)(?:.*?[\r\n])" />
		<nameExpr expr="[\t ]+(?:[A-Za-z_][\w]*\b)" />
		<nameExpr expr="[A-Za-z_][\w]*" />
	  </className>
	  <function mainExpr="(?m:^[\t ]*(?i:(?:(?:PUBLIC|PRIVATE|PROTECTED|FRIEND|PROTECTED FRIEND)[\t ]+)?(?:(?:STATIC|SHARED|SHADOWS|OVERRIDABLE|OVERRIDES|READONLY|WRITEONLY)[\t ]+)?(?:SUB|FUNCTION|PROPERTY)).*?(?:\(|$))" >
		<functionName>
		  <funcNameExpr expr="(?i:(?:(?:PUBLIC|PRIVATE|PROTECTED|FRIEND|PROTECTED FRIEND)[\t ]+)?(?:STATIC[\t ]+)?(?:SUB|FUNCTION|PROPERTY)).*?(?:\(|$)"/>
		  <funcNameExpr expr="(?i:(?:SUB|FUNCTION|PROPERTY)).*?(?:\(|$)"/>
		  <funcNameExpr expr="(?i:(?:GET|LET|SET)[\t ]+)?[A-Za-z_][\w]*(?=[\t ]*\()"/>
		</functionName>
	  </function>
	</classRange>
	<function 
		mainExpr="^[\t ]*(?i:(?:(?:PUBLIC|PRIVATE|PROTECTED|FRIEND|PROTECTED FRIEND)[\t ]+)?(?:(?:STATIC|(?:DECLARE(?:[\t ]+(ANSI|UNICODE|AUTO))?))[\t ]+)?(?:SUB|FUNCTION|PROPERTY)).*?(?:\(|$)" 
		displayMode="$functionName">
	  <functionName>
		<nameExpr expr="(?i:(?:(?:PUBLIC|PRIVATE|PROTECTED|FRIEND|PROTECTED FRIEND)[\t ]+)?(?:(?:STATIC|(?:DECLARE(?:[\t ]+(?:ANSI|UNICODE|AUTO))?))[\t ]+)?(?:SUB|FUNCTION|PROPERTY)).*?(?:\(|$)"/>
			<nameExpr expr="(?i:(?:SUB|FUNCTION|PROPERTY)).*?(?:\(|$)"/>
			<nameExpr expr="(?i:(?:GET|LET|SET)[\t ]+)?[A-Za-z_][\w]*(?i:[\t ]+(LIB|ALIAS)[\t ]+[\w&quot;\.]+)*(?=[\t ]*\()"/>
	  </functionName>
	</function>
</parser>

If nothing happens, check your xml syntax. I had at first copied the regex xml without the final closing tag. There were no errors displayed by notepad++, but it just didn’t work. After fixing that it seems to be working fine. Notepad++ has very basic window and panel docking functionality. I would have preferred to have the document map and function lists side-by-side, but it only allows for one panel to be docked on each side of the window. Other than that, it looks to be a decent setup. Having syntax highlighting and a function list is a huge time saver.

Posted in ASP, Development Enviroment, Notepad++ | Tagged , , , , , , , | Comments

Using Raphael the JavaScript graphics library.

In this example, I go over a simple use of Raphael.js. It draws 4 boxes on the page and allows you to move and resize each independently.

Here is the graphics library (licenses under the MIT license):
https://dmitrybaranovskiy.github.io/raphael/

Here is my code on GitHub.

The basic gist of it is to initialize the primary Raphael object and apply it to the page. After that you create the boxes you want to draw and associate them with a “drag” function that is called when the user interacts with each box.

Here is a selection of the code below:

//define 4 boxes to draw on screen
var boxListing = [
{x : '10', y : '10', width : '70', height : '70', boxname : 'Box 1'}, 
{x : '110', y : '10', width : '70', height : '70', boxname : 'Box 2'}, 
{x : '210', y : '10', width : '80', height : '80', boxname : 'Box 3'}, 
{x : '310', y : '10', width : '90', height : '90', boxname : 'Box 4'}
];

//values that define the graphical properties of the page
var raphObject; 
var dragIconSize = 10;

//the primary function of this page
function startup() {
	//initialize the main graphics object that is used to draw the box icons
	raphObject = Raphael('canvasdiv', 1000, 752);
	drawBoxes();
}

//start, move, and up are the drag functions
move_start = function () {
	//storing original coordinates
	this.ox = this.attr('x');
	this.oy = this.attr('y');
	this.attr({opacity: 0.5});
	
	//the resizer box
	this.resizer.ox = this.resizer.attr('x');
	this.resizer.oy = this.resizer.attr('y');
	this.resizer.attr({opacity: 0.5});
	
	//the box text
	this.boxtext.ox = this.attr('x') + (parseInt(this.attr('width')) / 2);
	this.boxtext.oy = this.attr('y') + (parseInt(this.attr('height')) / 2);
	this.boxtext.attr({opacity: 0.5});
};

//visually change the box when it is being moved
move_drag = function (dx, dy) {
	//move will be called with dx and dy
	this.attr({x: this.ox + dx, y: this.oy + dy});
	this.resizer.attr({x: this.resizer.ox + dx, y: this.resizer.oy + dy});  
	this.boxtext.attr({x: this.boxtext.ox + dx, y: this.boxtext.oy + dy});
};

//when the user lets go of the mouse button, reset the square's properties
move_up = function () {
	//restoring the visual state
	this.attr({opacity: 1});
	this.resizer.attr({opacity: 1});
	this.boxtext.attr({opacity: 1});   
	
	//here is where you would update the box's position externally
	//...
};

resize_start = function () {
	//storing original coordinates
	this.ox = this.attr('x');
	this.oy = this.attr('y');        
	
	//the resizer box
	this.resizer.ow = this.resizer.attr('width');
	this.resizer.oh = this.resizer.attr('height'); 
	
	//the box text
	this.boxtext.ox = this.resizer.attr('x') + (parseInt(this.resizer.attr('width')) / 2);
	this.boxtext.oy = this.resizer.attr('y') + (parseInt(this.resizer.attr('height')) / 2);
};

resize_drag = function (dx, dy) {
	// move will be called with dx and dy
	this.attr({x: this.ox + dx, y: this.oy + dy});
	this.resizer.attr({width: this.resizer.ow + dx, height: this.resizer.oh + dy});
	this.boxtext.attr({x: this.boxtext.ox + (dx / 2), y: this.boxtext.oy + (dy / 2)});
};

resize_up = function () {
	//here is where you would update the box's position externally
	//...
};

//draw all of the boxes in the json object
function drawBoxes() {
	//working arrays
	boxList = new Array();
	boxListText = new Array();
	boxListDrag = new Array();
	
	//loop through all boxes in the json object
	for (var i = 0; i < boxListing.length; i++) {
		//extract the positional data from the json array
		var px = boxListing[i].x;
		var py = boxListing[i].y;
		var pw = boxListing[i].width;
		var ph = boxListing[i].height;
		var textx = 0;
		var texty = 0;
		
		//we need to make sure that the values are numerical for calculations later
		px = parseInt(px);
		py = parseInt(py);
		pw = parseInt(pw);
		ph = parseInt(ph);
		
		//position text in the center of the box
		textx = px + (pw / 2);
		texty = py + (ph / 2);

		//deal with this individual box, position it and show a status based on color
		boxList[i] = raphObject.rect(px, py, pw, ph)
			.attr('fill', '#aaaaaa');
		
		boxListText[i] = raphObject.text(textx, texty, boxListing[i].boxname)
			.attr('font', '12px Arial')
			.attr('fill', '#000000');
		
		//position the drag icon
		var dragBoxX = px + pw - dragIconSize;
		var dragBoxY = py + ph - dragIconSize;
		
		//create the drag icon for this box
		boxListDrag[i] = raphObject.rect(dragBoxX, dragBoxY, dragIconSize, dragIconSize)
		.attr({
			fill: '#00aabb',
			stroke: 'solid',
			opacity: 1
		});
		
		//define relations and functions for moving and positioning
		//move the boxes
		boxList[i].drag(move_drag, move_start, move_up);
		boxList[i].resizer = boxListDrag[i];
		boxList[i].boxtext = boxListText[i];
		
		//resize the boxes
		boxListDrag[i].drag(resize_drag, resize_start, resize_up);
		boxListDrag[i].resizer = boxList[i];
		boxListDrag[i].boxtext = boxListText[i];
		
	} //for loop
}
Posted in Graphics, Javascript | Tagged , , , , , | Comments

C#: Using WebClient on CSV Formatted Stock Data

In this example I wrote a program to pull historic stock data from the ticker “DIS” over the past 10 days from the current date (it will be less than 10 results because weekends don’t have data). I used the .NET Framework WebClient object to request and return the data as a string that I parse into a generic Dictionary. It pulls the data from Google’s finance website as well as Yahoo’s finance site. Of course, check out their terms of use before doing anything with the code.

Here is the code that I wrote on my GitHub.

I created a class to handle communication with Google’s and Yahoo’s CSV export links. It handles constructing proper links as well as requesting the data and parsing it into a Dictionary that is structured by date. From that point, you could build an application around the use of that data, but for this example we just translate it back into strings for display in text boxes.

class WebClientForStockFinanceHistory
{
    WebClient webConnector;

    //See Google's terms of use before accessing their services
    //https://www.google.com/finance/historical?q=NYSE%3ADIS&ei=7O4nV9GdJcHomAG02L_wCw
    const string googleAddress = 
        "http://www.google.com/finance/historical?q=[-|ticker|-]&startdate=[-|sdate|-]&enddate=[-|edate|-]&num=30&output=csv";

    //See Yahoo's terms of use before accessing their services
    //http://finance.yahoo.com/q/hp?s=DIS+Historical+Prices
    const string yahooAddress = 
        "http://real-chart.finance.yahoo.com/table.csv?s=[-|ticker|-]&[-|sdate|-]&[-|edate|-]&g=d&ignore=.csv";
        
    /// <summary>
    /// request data from google's historic stock page
    /// </summary>
    /// <param name="market">eg. NYSE</param>
    /// <param name="ticker">eg. DIS</param>
    /// <param name="startDate">start date</param>
    /// <param name="endDate">end date</param>
    /// <returns></returns>
    public Dictionary<DateTime, StockDataItem> getStockDataFromGoogle(string market, string ticker, DateTime startDate, DateTime endDate)
    {
        return fillDataDictionary(getData(constructGoogleLink(market, ticker, startDate, endDate)));
    }

    /// <summary>
    /// form a link to request info from google's historic stock data export
    /// </summary>
    /// <param name="market">eg. NYSE</param>
    /// <param name="ticker">eg. DIS</param>
    /// <param name="startDate">start date</param>
    /// <param name="endDate">end date</param>
    /// <returns></returns>
    string constructGoogleLink(string market, string ticker, DateTime startDate, DateTime endDate)
    {
        string constructedUri = googleAddress;

        constructedUri = constructedUri.Replace("[-|ticker|-]", market + "%3A" + ticker);

        string constructedStartDate = startDate.ToString("MMM") + "+" + startDate.Day.ToString() + "%2C+" + startDate.ToString("yyyy");
        string constructedEndDate = endDate.ToString("MMM") + "+" + endDate.Day.ToString() + "%2C+" + endDate.ToString("yyyy");

        constructedUri = constructedUri.Replace("[-|sdate|-]", constructedStartDate);
        constructedUri = constructedUri.Replace("[-|edate|-]", constructedEndDate);

        return constructedUri;
    }

    /// <summary>
    /// request historic stock information from Yahoo
    /// </summary>
    /// <param name="ticker">eg. DIS</param>
    /// <param name="startDate">start date</param>
    /// <param name="endDate">end date</param>
    /// <returns></returns>
    public Dictionary<DateTime, StockDataItem> getStockDataFromYahoo(string ticker, DateTime startDate, DateTime endDate)
    {
        return fillDataDictionary(getData(constructYahooLink(ticker, startDate, endDate)));
    }

    /// <summary>
    /// constructs a html link to request data from Yahoo
    /// </summary>
    /// <param name="ticker">eg. DIS</param>
    /// <param name="startDate">start date</param>
    /// <param name="endDate">end date</param>
    /// <returns></returns>
    string constructYahooLink(string ticker, DateTime startDate, DateTime endDate)
    {
        string constructedUri = yahooAddress;

        constructedUri = constructedUri.Replace("[-|ticker|-]", ticker);

        string constructedStartDate = 
            "a=" + (startDate.Month - 1).ToString() + 
            "&b=" + startDate.Day.ToString() + 
            "&c=" + startDate.Year.ToString();

        string constructedEndDate = 
            "d=" + (endDate.Month - 1).ToString() + 
            "&e=" + endDate.Day.ToString() + 
            "&f=" + endDate.Year.ToString();

        constructedUri = constructedUri.Replace("[-|sdate|-]", constructedStartDate);
        constructedUri = constructedUri.Replace("[-|edate|-]", constructedEndDate);

        return constructedUri;
    }

    /// <summary>
    /// pull data based on a passed url text
    /// </summary>
    /// <param name="webpageUriString"></param>
    /// <returns></returns>
    string getData(string webpageUriString)
    {
        string tempStorageString = "";

        if (webpageUriString != "")
        {
            //create a new instance of the class
            //using should properly close and dispose so we don't have to bother
            using (webConnector = new WebClient())
            {
                using (Stream responseStream = webConnector.OpenRead(webpageUriString))
                {
                    using (StreamReader responseStreamReader = new StreamReader(responseStream))
                    {
                        //extract the response we got from the internet server
                        tempStorageString = responseStreamReader.ReadToEnd();

                        //change the unix style line endings so they work here
                        tempStorageString = tempStorageString.Replace("\n", Environment.NewLine);
                    }
                }
            }
        }

        return tempStorageString;
    }

    /// <summary>
    /// take csv data and push it into a dictionary
    /// </summary>
    /// <param name="csvData">data from Google or Yahoo in CSV format</param>
    /// <returns></returns>
    Dictionary<DateTime, StockDataItem> fillDataDictionary(string csvData)
    {
        Dictionary<DateTime, StockDataItem> parsedStockData = new Dictionary<DateTime, StockDataItem>();

        using (StringReader reader = new StringReader(csvData))
        {
            string csvLine;

            //drop the first row because it is a header we don't need
            reader.ReadLine();

            while ((csvLine = reader.ReadLine()) != null)
            {
                string[] splitLine = csvLine.Split(',');

                if (splitLine.Length >= 6)
                {
                    StockDataItem newItem = new StockDataItem();

                    //parse all values from the downloaded csv file
                    double tempOpen;
                    if (Double.TryParse(splitLine[1], out tempOpen))
                    {
                        newItem.open = tempOpen;
                    }
                    double tempHigh;
                    if (Double.TryParse(splitLine[2], out tempHigh))
                    {
                        newItem.high = tempHigh;
                    }
                    double tempLow;
                    if (Double.TryParse(splitLine[3], out tempLow))
                    {
                        newItem.low = tempLow;
                    }
                    double tempClose;
                    if (Double.TryParse(splitLine[4], out tempClose))
                    {
                        newItem.close = tempClose;
                    }
                    double tempVolume;
                    if (Double.TryParse(splitLine[5], out tempVolume))
                    {
                        newItem.volume = tempVolume;
                    }

                    //if we parse the date successfully, we add the
                    //new StockDataItem to our result set
                    DateTime tempDate;
                    if (DateTime.TryParse(splitLine[0], out tempDate))
                    {
                        parsedStockData.Add(tempDate, newItem);
                    }
                }
            }
        }

        return parsedStockData;
    }
}

The user can request information from Yahoo or Google, which basically results in the same thing because I opted to drop Yahoo’s last row of data that wasn’t included in Google’s. The most important line of code in the entire class is in getData() where I check and replace unix styled line endings with Windows ones. If you don’t do that then the parsing system won’t be able to extract out each day of information.

The main form manages the process with a Background Worker and pushes the result into two text boxes:

public partial class frmWebClientExample : Form
{
    public frmWebClientExample()
    {
        InitializeComponent();
    }

    string googleDataResult = "";
    string yahooDataResult = "";

    private void btnStartExample_Click(object sender, EventArgs e)
    {
        //attempt to start the request to the two websites
        googleDataResult = "";
        yahooDataResult = "";
        if(!bwProcessor.IsBusy)
        {
            btnStartExample.Enabled = false;
            txtGoogleResult.Text = "";
            txtYahooResult.Text = "";
            bwProcessor.RunWorkerAsync();
        } else
        {
            MessageBox.Show("The background worker is busy...");
        }
    }

    private void bwProcessor_DoWork(object sender, DoWorkEventArgs e)
    {
        DateTime tenDaysPast = DateTime.Now.Subtract(TimeSpan.FromDays(10));
        StringBuilder textString = new StringBuilder();
        WebClientForStockFinanceHistory wc1 = new WebClientForStockFinanceHistory();
        Dictionary<DateTime, StockDataItem> downloadedStockData;

        //request data from Google for the past 10 days
        downloadedStockData = wc1.getStockDataFromGoogle("NYSE", "DIS", tenDaysPast, DateTime.Now);

        textString.AppendLine("Data on DIS from Google:");

        foreach (KeyValuePair<DateTime, StockDataItem> singleItem in downloadedStockData)
        {
            textString.AppendLine(singleItem.Key.ToShortDateString() +
                " [" +
                singleItem.Value.open.ToString("0.0") + "; " +
                singleItem.Value.close.ToString("0.0") + "; " +
                singleItem.Value.high.ToString("0.0") + "; " +
                singleItem.Value.low.ToString("0.0") + "; " +
                singleItem.Value.volume +
                "]");
        }

        //save the Google result to a string on the UI thread
        googleDataResult = textString.ToString();
            
        textString.Clear();
        textString.AppendLine("Data on DIS from Yahoo:");

        //request data from Yahoo for the past 10 days
        downloadedStockData = wc1.getStockDataFromYahoo("DIS", tenDaysPast, DateTime.Now);

        foreach (KeyValuePair<DateTime, StockDataItem> singleItem in downloadedStockData)
        {
            textString.AppendLine(singleItem.Key.ToShortDateString() +
                " [" +
                singleItem.Value.open.ToString("0.0") + "; " +
                singleItem.Value.close.ToString("0.0") + "; " +
                singleItem.Value.high.ToString("0.0") + "; " +
                singleItem.Value.low.ToString("0.0") + "; " +
                singleItem.Value.volume +
                "]");
        }

        //save the Yahoo result to a string on the UI thread
        yahooDataResult = textString.ToString();
    }

    private void bwProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        btnStartExample.Enabled = true;
        txtGoogleResult.Text = googleDataResult;
        txtYahooResult.Text = yahooDataResult;
    }
}
Posted in C# | Tagged , , , , , , , , , , , , | Comments