浏览代码

Feature: ECL Playground

New web page which lets users "play" with ECL without installing any
client tools.  Includes ECL editor, Graph Viewer and Result/Syntax
Error viewer.

Signed-off-by: Gordon Smith <gordon.smith@lexisnexis.com>
Gordon Smith 13 年之前
父节点
当前提交
5a9686fcdf

+ 141 - 0
esp/files/ECLPlayground.css

@@ -0,0 +1,141 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+html, body {
+  background: white; 
+  width: 100%;
+  height: 100%;
+  margin: 0px;
+  color: rgb(51, 51, 51); 
+  font-family: Lucida Sans,Lucida Grande,Arial !important; 
+  font-size: 13px !important;
+  padding-top:0px;
+  padding-right:0px;
+  padding-bottom:0px;
+  padding-left:0px;
+}
+button {
+	background-position: top; 
+	padding: 2px 8px 4px; 
+	border-radius: 4px; 
+	border: 1px solid rgb(118, 157, 192); 
+	font-size: 1em; 
+	background-image: url("http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dijit/themes/claro/form/images/button.png"); 
+	background-repeat: repeat-x; 
+	background-color: rgb(228, 242, 255); 
+	-webkit-transition: background-color 0.2s linear; 
+	-moz-border-radius: 4px 4px 4px 4px; 
+	-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
+}
+button:hover {
+	color: rgb(0, 0, 0); 
+	background-color: rgb(175, 217, 255);
+}
+h1 {
+	font-size: 1.5em;
+}
+
+#eclContent {
+	height: 80%;
+}
+.CodeMirror {
+}
+.CodeMirror-scroll {
+	position: absolute;
+	height: 99%;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+}
+
+#title {
+	font-weight: bold;
+	font-size: x-large;
+}
+
+#appLayout {
+	height: 100%;
+}
+.claro .demoLayout .topPanel {
+	background-color: rgb(208, 233, 252);
+}
+.claro .demoLayout .edgePanel {
+	background-color: rgb(208, 233, 252);
+}
+.pageOverlay {
+	left: 0px;
+	top: 0px;
+	width: 100%;
+	height: 100%;
+	display: block;
+	position: absolute;
+	z-index: 1001;
+}
+#loadingOverlay {
+	background: url("/esp/files/img/loadingAnimation.gif") no-repeat 10px 23px rgb(255, 255, 255);
+}
+#loadingOverlay .loadingMessage {
+	padding: 25px 40px;
+	color: rgb(153, 153, 153);
+}
+.searchInputColumn {
+	width: 100%;
+	margin-right: -10em;
+	float: left;
+}
+.searchInputColumnInner {
+	padding-top: 2px;
+	padding-right: 11em;
+}
+.searchButtonColumn {
+	width: 10em;
+	float: left;
+}
+#searchBtn {
+	width: 100%;
+	display: block;
+}
+#searchTerms {
+	width: 100%;
+	display: block;
+}
+.demoImageList {
+	margin: 0px;
+	padding: 0px;
+	background-color: rgb(204, 204, 204);
+}
+.demoImageList li.item img {
+	overflow: hidden;
+	margin-right: 10px;
+	cursor: pointer;
+	max-height: 50px;
+}
+.demoImageList li.item {
+	margin: 0px;
+	padding: 4px 15px 3px 10px;
+	overflow: hidden;
+	border-top-color: rgb(255, 255, 255);
+	border-bottom-color: rgb(153, 153, 153);
+	border-top-width: 2px;
+	border-bottom-width: 1px;
+	border-top-style: solid;
+	border-bottom-style: solid;
+	position: relative;
+	max-height: 60px;
+	background-color: rgb(238, 238, 238);
+}
+

+ 122 - 0
esp/files/ECLPlayground.htm

@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<!--
+##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##############################################################################
+-->
+<html>
+
+<head>
+<meta charset="utf-8">
+<title>Demo: Graph Control</title>
+<link href="CodeMirror2/lib/codemirror.css" rel="stylesheet">
+<script src="CodeMirror2/lib/codemirror.js"></script>
+<script src="CodeMirror2/mode/ecl/ecl.js"></script>
+<link href="css/ecl.css" rel="stylesheet">
+<link href="ECLPlayground.css" media="screen" rel="stylesheet">
+<link href="dijit/themes/claro/claro.css" media="screen" rel="stylesheet">
+<link href="dojox/grid/resources/Grid.css" rel="stylesheet">
+<link href="dojox/grid/resources/claroGrid.css" rel="stylesheet">
+<!-- load dojo and provide config via dojoConfig global -->
+<script>
+	var dojoConfig = (function(){
+		var base = location.href.split("/");
+		var developerMode = base[0] == "file:";	//  If URL is local then we are in web developer mode!
+		base.pop();
+		base = base.join("/");
+		
+		return {
+			async: true,
+			isDebug: developerMode,
+			parseOnLoad: true,
+			urlBase: base,
+			serverIP:  developerMode ? "192.168.2.68" : "",	//IP of any ESP server
+			packages: [{
+				name: "main",
+				location: base + "/"  
+			}, {
+				name: "hpcc",
+				location: base + "/scripts/"
+			}]
+		};
+	})();
+	</script>
+<script src="dojo/dojo.js"></script>
+<script>
+	require(["dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dijit/layout/TabContainer", "dijit/layout/AccordionContainer", "dijit/layout/AccordionPane", 
+	"dojo/domReady!"]);
+</script>
+<script>
+	require(["main/ECLPlayground", "dojo/domReady!"], function(app) {
+		app.init();
+	});
+</script>
+</head>
+
+<body class="claro">
+
+<!-- overlay -->
+<div id="loadingOverlay" class="pageOverlay">
+	<div class="loadingMessage">
+		Loading...</div>
+</div>
+<!-- application -->
+<div id="appLayout" class="demoLayout" data-dojo-props="design: 'headline'" data-dojo-type="dijit.layout.BorderContainer">
+	<div id="topPane" class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.layout.ContentPane">
+		<div id="title" style="display:inline-block; vertical-align:middle">ECL Playground</div>
+		<div style="float: right;display :inline-block; vertical-align:middle">
+			<label for="sampleSelect">Sample:</label>
+			<div id="sampleSelect"></div>
+		</div>
+	</div>
+	<div id="tabs" class="centerPanel" data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
+		<div id="eclContent">
+			<textarea id="eclCode">
+/*
+    Example code - use without restriction.  
+*/
+Layout_Person := RECORD
+  UNSIGNED1 PersonID;
+  STRING15  FirstName;
+  STRING25  LastName;
+END;
+
+allPeople := DATASET([ {1,'Fred','Smith'},
+                       {2,'Joe','Blow'},
+                       {3,'Jane','Smith'}],Layout_Person);
+
+somePeople := allPeople(LastName = 'Smith');
+
+//  Outputs  ---
+somePeople;
+			</textarea> 
+		</div>
+	</div>
+	<div id="rightPane" class="edgePanel" data-dojo-props="minSize:120, region: 'right', splitter:true" data-dojo-type="dijit.layout.TabContainer" style="width: 240px;">
+		<div id="graphs" style="width:  100%; height: 100%"></div>
+	</div>
+	<div id="bottomPane" class="edgePanel" data-dojo-props="minSize:120, region: 'bottom', splitter:true, tabPosition: 'bottom'" data-dojo-type="dijit.layout.TabContainer" style="height: 240px">
+	</div>
+	<div id="submitPane" class="edgePanel"  data-dojo-props="region: 'bottom'" data-dojo-type="dijit.layout.ContentPane">
+		<div style="display:inline-block; vertical-align:middle"><button id="submitBtn">Submit</button></div>
+		<div style="float: right;display :inline-block; vertical-align:middle">
+			<div id="status">...</div>
+		</div>
+	</div>
+</div>
+</body>
+
+</html>

+ 162 - 0
esp/files/ECLPlayground.js

@@ -0,0 +1,162 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([ 
+	"dojo/_base/config", 
+	"dojo/_base/fx", 
+	"dojo/_base/window", 
+	"dojo/dom", 
+	"dojo/dom-style",
+	"dojo/dom-geometry", 
+	"dojo/on",
+	"hpcc/EclEditorControl", 
+	"hpcc/GraphControl", 
+	"hpcc/ResultsControl",
+	"hpcc/SampleSelectControl", 
+	"hpcc/ESPWorkunit"
+], function(baseConfig, baseFx, baseWindow, dom, domStyle, domGeometry, on, EclEditor, GraphControl, ResultsControl, Select, Workunit) {
+			var editorControl = null, 
+			graphControl = null, 
+			resultsControl = null, 
+			sampleSelectControl = null, 
+
+			initUi = function() {
+				on(dom.byId("submitBtn"), "click", doSubmit);
+				initSamples();
+				initEditor();
+				initResultsControl();
+				//  ActiveX will flicker if created before initial layout
+				setTimeout(function(){
+					initGraph();
+				}, 1);
+			},
+
+			initSamples = function() {
+				sampleSelectControl = new Select({
+					id : "sampleSelect",
+					samplesURL : "ecl/ECLPlaygroundSamples.json",
+					onNewSelection : function(eclText) {
+						resetPage();
+						editorControl.setText(eclText);
+					}
+				});
+			},
+
+			initEditor = function() {
+				editorControl = new EclEditor({
+					domId : "eclCode"
+				});
+			},
+
+			initGraph = function() {
+				graphControl = new GraphControl({
+					id : "gvc",
+					width : "100%",
+					height : "100%",
+					onInitialize : function() {
+					},
+
+					onLayoutFinished : function() {
+						graphControl.obj.setMessage('');
+						graphControl.obj.centerOnItem(0, true);
+					},
+
+					onMouseDoubleClick : function(item) {
+						graphControl.obj.centerOnItem(item, true);
+					}
+
+				}, dom.byId("graphs"));
+			},
+
+			initResultsControl = function() {
+				var _resultsControl = resultsControl = new ResultsControl({
+					resultsSheetID : "bottomPane",
+					onErrorClick : function(line, col) {
+						editorControl.setCursor(line, col);
+					}
+				});
+			},
+
+			resetPage = function() {
+				editorControl.clearErrors();
+				graphControl.clear();
+				resultsControl.clear();
+			},
+
+			doSubmit = function(evt) {
+				resetPage();
+				wu = new Workunit({
+					wuid : "",
+					
+					onCreate : function() {
+						wu.update(editorControl.getText());
+					},
+					onUpdate : function() {
+						wu.submit("hthor");
+					},
+					onSubmit : function() {
+					},
+					onMonitor : function() {
+						dom.byId("status").innerHTML = wu.state;						
+						if (wu.isComplete())
+							wu.getInfo();
+					},
+					onGetInfo : function() {
+						if (wu.errors.length) {
+							editorControl.setErrors(wu.errors);
+							resultsControl.addExceptionTab(wu.errors);
+						}
+						wu.getResults();
+						wu.getGraphs();
+					},
+					onGetGraph : function(idx) {
+						graphControl.loadXGMML(wu.graphs[idx].xgmml);
+					},
+					onGetResult : function(idx) {
+						resultsControl.addDatasetTab(wu.results[idx].dataset);
+					}
+				});
+			},
+
+			startLoading = function(targetNode) {
+				var overlayNode = dom.byId("loadingOverlay");
+				if ("none" == domStyle.get(overlayNode, "display")) {
+					var coords = domGeometry.getMarginBox(targetNode || baseWindow.body());
+					domGeometry.setMarginBox(overlayNode, coords);
+					domStyle.set(dom.byId("loadingOverlay"), {
+						display : "block",
+						opacity : 1
+					});
+				}
+			},
+
+			endLoading = function() {
+				baseFx.fadeOut({
+					node : dom.byId("loadingOverlay"),
+					onEnd : function(node) {
+						domStyle.set(node, "display", "none");
+					}
+				}).play();
+			}
+
+			return {
+				init : function() {
+					startLoading();
+					initUi();
+					endLoading();
+				}
+			};
+		});

+ 27 - 0
esp/files/css/ecl.css

@@ -0,0 +1,27 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+.cm-s-default span.cm-comment { color: #008000; }
+.cm-s-default span.cm-keyword { color: #0000ff; }
+.cm-s-default span.cm-variable { font-weight: bold; color: #000080; }
+.cm-s-default span.cm-variable-2 { color: #800000; }
+.cm-s-default span.cm-variable-3 { color: #800000; }
+.cm-s-default span.cm-builtin { color: #800080; }
+.cm-s-default span.cm-string { color: #808080; }
+.cm-s-default span.cm-number, .cm-s-default span.cm-atom { color: #000000; }
+.cm-s-default span.cm-meta {color: #004080;}
+.ErrorLine {background: #cc0000 !important;}
+.WarningLine {background: #ffff99 !important;}

+ 9 - 0
esp/files/ecl/ECLPlaygroundSamples.json

@@ -0,0 +1,9 @@
+{
+	"identifier": "filename",
+	"label": "name",
+	"items": [
+		{ "name": "Simple Filter", 	"filename": "default.ecl",	selected: true },
+		{ "name": "Hello World", 	"filename": "hello.ecl" },
+		{ "name": "Simple Sort", 	"filename": "simplesort.ecl" }
+	]
+}

+ 17 - 0
esp/files/ecl/default.ecl

@@ -0,0 +1,17 @@
+/*
+    Example code - use without restriction.  
+*/
+Layout_Person := RECORD
+  UNSIGNED1 PersonID;
+  STRING15  FirstName;
+  STRING25  LastName;
+END;
+
+allPeople := DATASET([ {1,'Fred','Smith'},
+                       {2,'Joe','Blow'},
+                       {3,'Jane','Smith'}],Layout_Person);
+
+somePeople := allPeople(LastName = 'Smith');
+
+//  Outputs  ---
+somePeople;

+ 5 - 0
esp/files/ecl/hello.ecl

@@ -0,0 +1,5 @@
+/*
+    Example code - use without restriction.  
+*/
+
+'Hello and Welcome!';

+ 17 - 0
esp/files/ecl/simplesort.ecl

@@ -0,0 +1,17 @@
+/*
+    Example code - use without restriction.  
+*/
+Layout_Person := RECORD
+  UNSIGNED1 PersonID;
+  STRING15  FirstName;
+  STRING25  LastName;
+END;
+
+Person := DATASET([ {1,'Fred','Smith'},
+                    {2,'Joe','Blow'},
+                    {3,'Jane','Smith'}],Layout_Person);
+
+SortedPerson := SORT(Person, LastName, FirstName);
+
+//  Outputs  ---
+SortedPerson;

二进制
esp/files/img/loadingAnimation.gif


+ 117 - 0
esp/files/scripts/ESPBase.js

@@ -0,0 +1,117 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([
+	"dojo/_base/config", 
+	"dojo/_base/declare"
+], function(baseConfig, declare) {
+	return declare(null, {
+
+		constructor : function(args) {
+			declare.safeMixin(this, args);
+		},
+		
+		getParam : function(key) {
+			var value = dojo.queryToObject(dojo.doc.location.search.substr((dojo.doc.location.search[0] === "?" ? 1 : 0)))[key];
+			if(value)
+				return value;
+			return baseConfig[key];
+		},
+		
+		getBaseURL : function() {
+			var serverIP = this.getParam("serverIP");
+			if(serverIP)
+				return "http://" + serverIP + ":8010/WsWorkunits";
+			return "/WsWorkunits";
+		},
+
+		parseKeyValue : function(xmlDom, nodeLabel) {
+			var items = xmlDom.getElementsByTagName(nodeLabel);
+			if(items.length && items[0].childNodes.length) {
+				return items[0].childNodes[0].nodeValue;
+			}
+			return "";
+		},
+		
+		parseKeyChildren : function(xmlDom, nodeLabel) {
+			var items = xmlDom.getElementsByTagName(nodeLabel);
+			if(items.length && items[0].childNodes.length) {
+				return items[0].childNodes;
+			}
+			return null;
+		},
+		
+		parseRows : function(xmlDom, nodeLabel) {
+			var rows = [];
+			var items = xmlDom.getElementsByTagName(nodeLabel);
+			for(var i = 0; i < items.length; ++i) {
+				var item = items[i];
+				var cols = {};
+				for(var c = 0; c < item.childNodes.length; ++c) {
+					colNode = item.childNodes[c];
+					if(colNode.childNodes.length && colNode.childNodes[0].nodeValue) {
+						cols[colNode.nodeName] = colNode.childNodes[0].nodeValue;
+					} else {
+						cols[colNode.nodeName] = "";
+					}
+				}
+				rows.push(cols);
+			}
+			return rows;
+		},
+		
+		//  <XXX><YYY/><YYY/><YYY/><YYY/></XXX>
+		parseDataset : function(xmlDom, _name, nodeLabel) {
+			var retVal = {};
+			var retValRows = this.parseRows(xmlDom, nodeLabel);
+			var retValHeader = [];
+			if(retValRows.length) {
+				for(var key in retValRows[0]) {
+					retValHeader.push(key)
+				}
+			}
+			retVal = {
+				name : _name,
+				header : retValHeader,
+				rows : retValRows
+			};
+			return retVal;
+
+		},
+		
+		parseDatasets : function(xmlDom, nodeLabel, innerNodeLabel) {
+			var retVal = [];
+			var datasets = xmlDom.getElementsByTagName(nodeLabel);
+			for(var d = 0; d < datasets.length; ++d) {
+				var dataset = datasets[d];
+				var retValRows = this.parseRows(dataset, innerNodeLabel);
+				var retValHeader = [];
+				if(retValRows.length) {
+					for(var key in retValRows[0]) {
+						retValHeader.push(key)
+					}
+				}
+
+				retVal.push({
+					name : dataset.getAttribute("name"),
+					header : retValHeader,
+					rows : retValRows
+				});
+			}
+			return retVal;
+		}
+	});
+});

+ 267 - 0
esp/files/scripts/ESPWorkunit.js

@@ -0,0 +1,267 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([
+	"dojo/_base/config", 
+	"dojo/_base/declare", 
+	"dojo/_base/xhr", 
+	"hpcc/ESPBase"], function(baseConfig, declare, baseXhr, ESPBase) {
+	return declare(ESPBase, {
+		wuid : "",
+		stateID : 0,
+		state : "",
+
+		resultCount : 0,
+		results : [],
+
+		graphNameIndex : [],
+		graphs : [],
+
+		exceptions : [],
+		errors : [],
+		timers : [],
+
+		onCreate : function() {
+		},
+		onUpdate : function() {
+		},
+		onSubmit : function() {
+		},
+		onMonitor : function() {
+		},
+		onComplete : function() {
+		},
+		onGetInfo : function() {
+		},
+		onGetGraph : function(name) {
+		},
+		constructor : function(args) {
+			declare.safeMixin(this, args);
+
+			if(!this.wuid) {
+				this.create();
+			}
+		},
+		isComplete : function() {
+			switch (this.stateID) {
+				case '3':
+				//WUStateCompleted:
+				case '4':
+				//WUStateFailed:
+				case '5':
+				//WUStateArchived:
+				case '7':
+					//WUStateAborted:
+					return true;
+			}
+			return false;
+		},
+		monitor : function() {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUQuery",
+				handleAs : "xml",
+				content : request,
+				load : function(xmlDom) {
+					context.stateID = context.parseKeyValue(xmlDom, "StateID");
+					context.state = context.parseKeyValue(xmlDom, "State");
+					context.onMonitor();
+					if(!context.isComplete()) {
+						setTimeout(function() {
+							context.monitor();
+						}, 200);
+					}
+				},
+				error : function() {
+					done = true;
+				}
+			});
+		},
+		create : function(ecl, _sync) {
+			var request = {};
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUCreate",
+				handleAs : "xml",
+				content : request,
+				load : function(xmlDom) {
+					context.wuid = context.parseKeyValue(xmlDom, "Wuid");
+					context.onCreate();
+				},
+				error : function() {
+				}
+			});
+		},
+		update : function(ecl, _sync) {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['QueryText'] = ecl;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUUpdate",
+				handleAs : "xml",
+				content : request,
+				sync : _sync,
+				load : function(xmlDom) {
+					context.onUpdate();
+				},
+				error : function() {
+				}
+			});
+		},
+		submit : function(target, _sync) {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['Cluster'] = target;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUSubmit",
+				handleAs : "xml",
+				content : request,
+				sync : _sync,
+				load : function(xmlDom) {
+					context.onSubmit();
+					context.monitor();
+				},
+				error : function() {
+				}
+			});
+		},
+		getInfo : function(_sync) {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['IncludeExceptions'] = true;
+			request['IncludeGraphs'] = true;
+			request['IncludeSourceFiles'] = false;
+			request['IncludeResults'] = true;
+			request['IncludeResultsViewNames'] = false;
+			request['IncludeVariables'] = false;
+			request['IncludeTimers'] = true;
+			request['IncludeDebugValues'] = false;
+			request['IncludeApplicationValues'] = false;
+			request['IncludeWorkflows'] = false;
+			request['SuppressResultSchemas'] = true;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUInfo",
+				handleAs : "xml",
+				content : request,
+				sync : _sync,
+				load : function(xmlDom) {
+					context.exceptions = context.parseRows(xmlDom, "Exception");
+					context.errors = context.parseRows(xmlDom, "ECLException");
+					context.timers = context.parseRows(xmlDom, "ECLTimer");
+					context.graphs = context.parseRows(xmlDom, "ECLGraph");
+					for(var i = 0; i < context.graphs.length; ++i) {
+						context.graphNameIndex[context.graphs[i].Name] = i;						
+					}
+					context.results = context.parseRows(xmlDom, "ECLResult");
+					context.onGetInfo();
+				},
+				error : function() {
+				}
+			});
+		},
+		getGraphs : function() {
+			for(var i = 0; i < this.graphs.length; ++i) {
+				this.getGraph(i);
+			}
+		},
+		getGraph : function(idx, _sync) {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['GraphName'] = this.graphs[idx].Name;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUGetGraph",
+				handleAs : "xml",
+				content : request,
+				sync : _sync,
+				load : function(xmlDom) {
+					context.graphs[idx].xgmml = context.parseKeyValue(xmlDom, "Graph");
+					context.onGetGraph(idx);
+				},
+				error : function() {
+				}
+			});
+		},
+		getResults : function() {
+			for(var i = 0; i < this.results.length; ++i) {
+				this.getResult(i);
+			}
+		},
+		getResult : function(idx, _sync) {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['Sequence'] = this.results[idx].Sequence;
+			request['Start'] = 0;
+			request['Count'] = 999;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUResult",
+				handleAs : "xml",
+				content : request,
+				sync : _sync,
+				load : function(xmlDom) {
+					var name = context.parseKeyValue(xmlDom, "Name");
+					var resultDom = xmlDom.getElementsByTagName("Result");
+					if(resultDom.length) {
+						context.results[idx].dataset = context.parseDataset(resultDom[0], name, "Row");
+					}
+					context.onGetResult(idx);
+				},
+				error : function() {
+				}
+			});
+		},
+		getInfoFast : function(_sync) {
+			var request = {};
+			request['Wuid'] = this.wuid;
+			request['rawxml_'] = "1";
+
+			var context = this;
+			baseXhr.post({
+				url : this.getBaseURL() + "/WUQuery",
+				handleAs : "xml",
+				content : request,
+				sync : _sync,
+				load : function(xmlDom) {
+					context.stateID = context.parseKeyValue(xmlDom, "StateID");
+					context.state = context.parseKeyValue(xmlDom, "State");
+				},
+				error : function() {
+					done = true;
+				}
+			});
+		}
+	});
+});

+ 64 - 0
esp/files/scripts/EclEditorControl.js

@@ -0,0 +1,64 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([
+	"dojo/_base/declare"
+], function(declare) {
+	return declare(null, {
+		domId : "",
+		editor : null,
+		markers : [],
+
+		// The constructor    
+		constructor : function(args) {
+			declare.safeMixin(this, args);
+			var _editor = this.editor = CodeMirror.fromTextArea(document.getElementById(this.domId), {
+				tabMode : "indent",
+				matchBrackets : true,
+				gutter : true,
+				lineNumbers : true
+			});
+		},
+
+		clearErrors : function(errWarnings) {
+			for ( var i = 0; i < this.markers.length; ++i) {
+				this.editor.clearMarker(this.markers[i]);
+			}
+			this.markers = [];
+		},
+
+		setErrors : function(errWarnings) {
+			for ( var i = 0; i < errWarnings.length; ++i) {
+				this.markers.push(this.editor.setMarker(parseInt(
+						errWarnings[i].LineNo, 10) - 1, "",
+						errWarnings[i].Severity + "Line"));
+			}
+		},
+
+		setCursor : function(line, col) {
+			this.editor.setCursor(line - 1, col - 1);
+			this.editor.focus();
+		},
+
+		setText : function(ecl) {
+			this.editor.setValue(ecl);
+		},
+
+		getText : function() {
+			return this.editor.getValue();
+		}
+	});
+});

+ 168 - 0
esp/files/scripts/GraphControl.js

@@ -0,0 +1,168 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([    
+	"dojo/_base/declare",
+	"dojo/_base/sniff",
+	"dojo/dom"
+], function(declare, has, dom) {
+	return declare(null, {
+		id: "gvc",
+		width: "",
+		height: "",
+		installed:  false,
+		markup: "",
+		obj: {},
+		eventsRegistered: false,
+
+		onInitialize: function(){
+			//  Creater can override.
+		},
+		
+		onLayoutFinished: function() {
+			//  Creater can override.
+		},
+
+		onMouseDoubleClick: function(item) {
+			//  Creater can override.
+		},
+
+		onSelectionChanged: function(items) {
+			//  Creater can override.
+		},
+	
+		// The constructor    
+		constructor: function(args, parentNode){
+			declare.safeMixin(this, args);
+			
+			if(has("ie")){
+				this.installed = (function(){
+					try{
+						var o = new ActiveXObject("HPCCSystems.HPCCSystemsGraphViewControl.1");
+						return true;
+					} catch(e){ }
+					return false;
+				})();
+		
+				if(!this.installed){ 
+					this.markup = this.getInstallMarkup(); 
+				} else {
+					this.markup = '<object type="application/x-hpccsystemsgraphviewcontrol" '
+						+ 'id="' + this.id + '" '
+						+ 'width="' + this.width + '" '
+						+ 'height="' + this.height + '"></object>';
+				}
+			} else {
+				this.installed = (function(){
+					for(var i=0, p=navigator.plugins, l=p.length; i<l; i++){
+						if(p[i].name.indexOf("HPCCSystemsGraphViewControl")>-1){
+							return true;
+						}
+					}
+					return false;
+				})();
+			
+				if(!this.installed){ 
+					this.markup = this.getInstallMarkup(); 
+				} else {
+					this.markup = '<embed type="application/x-hpccsystemsgraphviewcontrol" '
+						+ 'id="' + this.id + '" '
+						+ 'name="' + this.id + '" '
+						+ 'width="' + this.width + '" '
+						+ 'height="' + this.height + '"></embed>';
+				}
+			}
+			parentNode.innerHTML = this.markup;
+			this.obj = dom.byId(this.id);
+			var context = this;
+			setTimeout(function(){
+				context.onInitialize();
+			}, 20);
+		},
+		
+		getInstallMarkup: function() {
+			return "<h4>Graph View</h4>" +
+			"<p>To enable graph views, please install the Graph View Control plugin:</p>" + 
+			"<a href=\"http://graphcontrol.hpccsystems.com/stable/SetupGraphControl.msi\">Internet Explorer + Firefox (32bit)</a><br>" + 
+			"<a href=\"http://graphcontrol.hpccsystems.com/stable/SetupGraphControl64.msi\">Internet Explorer + Firefox (64bit)</a><br>" + 
+			"<a href=\"https://github.com/hpcc-systems/GraphControl\">Linux/Other (sources)</a>";
+		},
+
+		clear: function() {
+			if (this.obj) {
+				this.obj.clear();
+			}
+		},
+		
+		loadXGMML: function(xgmml) {
+			if (this.obj) {
+				this.registerEvents();
+				this.obj.setMessage("Loading Data...");
+				this.obj.loadXGMML(xgmml);
+				this.obj.setMessage("Performing Layout...");
+				this.obj.startLayout("dot");
+			}
+		},
+		
+		loadDOT: function(dot) {
+			this.load(dot, "dot");
+		},
+		
+		load: function(dot, layout) {
+			if (this.obj) {
+				this.registerEvents();
+				this.obj.setMessage("Loading Data...");
+				this.obj.loadDOT(dot);
+				this.obj.setMessage("Performing Layout...");
+				this.obj.startLayout(layout);
+			}
+		},
+		
+		setLayout: function(layout) {
+			if (this.obj) {
+				this.registerEvents();
+				this.obj.setMessage("Performing Layout...");
+				this.obj.startLayout(layout);
+			}
+		},
+		
+		centerOn : function(globalID) {
+			var item = this.obj.getItem(globalID);
+			this.obj.centerOnItem(item, true);
+			var items = [item];
+			this.obj.setSelected(items, true);
+		},
+		
+		registerEvents: function() {
+			if (!this.eventsRegistered) {
+				this.eventsRegistered = true;
+				this.registerEvent("LayoutFinished", this.onLayoutFinished);
+				this.registerEvent("MouseDoubleClick", this.onMouseDoubleClick);
+				this.registerEvent("SelectionChanged", this.onSelectionChanged);
+			}
+		},		
+		
+		registerEvent: function(evt, func) {
+			if (this.obj) {
+				if(has("ie")){
+					this.obj.attachEvent("on" + evt, func);
+				} else {
+					this.obj.addEventListener(evt, func, false);
+				}
+			}
+		}
+	});
+});

+ 111 - 0
esp/files/scripts/ResultsControl.js

@@ -0,0 +1,111 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([    
+	"dojo/_base/declare",
+	"dojo/dom", 
+	"dojo/store/Memory",
+	"dojo/data/ObjectStore",
+	"dojox/grid/DataGrid",
+	"dijit/registry",
+	"dijit/layout/ContentPane"
+], function(declare, dom, Memory, ObjectStore, DataGrid, registry, ContentPane) {
+	return declare(null, {
+		paneNum: 0,
+		resultsSheetID: "",
+
+		//  Callbacks
+		onErrorClick: function(line, col) {
+		},
+
+		// The constructor    
+		constructor: function(args){
+			declare.safeMixin(this, args);
+		},
+
+		clear: function() {
+			var resultSheet = registry.byId(this.resultsSheetID);
+			var tabs = resultSheet.getChildren();
+			for(var i = 0; i < tabs.length; ++i) {
+				resultSheet.removeChild(tabs[i]);
+			}
+		},
+
+		addTab: function(label) {
+			var resultSheet = registry.byId(this.resultsSheetID);
+			var paneID = "Pane_" + ++this.paneNum;
+			var pane = new ContentPane({
+				title: label,
+				id: paneID,
+				closable: "true",
+				style: {padding: "0px"},
+				content : "<div id=\"Div_" + paneID + "\"></div>"
+			});
+			resultSheet.addChild(pane);
+			return dom.byId(pane.id);
+		},
+
+		addDatasetTab: function(dataset) {
+			var resultNode = this.addTab(dataset.name);
+			
+			var gridLayout = [];
+			for (var h = 0; h < dataset.header.length; ++h) {
+				gridLayout.push({
+					name: dataset.header[h],
+					field: dataset.header[h], 
+					width: "auto"
+				});
+			}
+			store = new Memory({ data: dataset.rows });
+			dataStore = new ObjectStore({ objectStore: store });
+
+			grid = new DataGrid({
+				store: dataStore ,
+				query: {},
+				structure: gridLayout
+			}, "Div_" + resultNode.id);
+			grid.startup();
+		},
+
+		addExceptionTab: function(errors) {
+			var resultNode = this.addTab("Error(s)");
+			store = new Memory({ data: errors });
+			dataStore = new ObjectStore({ objectStore: store });
+			
+			grid = new DataGrid({
+				store: dataStore ,
+				query: {},
+				structure: [
+					{name: "Severity", field: "Severity"}, 
+					{name: "Line", field: "LineNo"}, 
+					{name: "Column", field: "Column"}, 
+					{name: "Code", field: "Code"}, 
+					{name: "Message", field: "Message", width: "auto"}
+					]
+			}, "Div_" + resultNode.id);
+			grid.startup();
+			
+			var context = this;
+			grid.on("RowClick", function(evt){
+				var idx = evt.rowIndex;
+				var item = this.getItem(idx);
+				var line = parseInt(this.store.getValue(item, "LineNo"), 10);
+				var col = parseInt(this.store.getValue(item, "Column"), 10);
+				context.onErrorClick(line, col);
+			}, true);
+		}
+	});
+});

+ 62 - 0
esp/files/scripts/SampleSelectControl.js

@@ -0,0 +1,62 @@
+/*##############################################################################
+#    Copyright (C) 2011 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+define([    
+	"dojo/_base/declare",
+	"dojo/_base/xhr",
+	"dojo/data/ItemFileReadStore",
+	"dijit/form/Select"
+], function(declare, baseXhr, ItemFileReadStore, Select) {
+	return declare(null, {
+		id: null,
+		samplesURL: null,
+	
+		onNewSelection: function(eclText) {
+		},
+	
+		constructor: function(args){
+			declare.safeMixin(this, args);
+			var sampleStore = new dojo.data.ItemFileReadStore({
+				url: this.samplesURL
+			});
+			
+			var context = this;
+			var select = new dijit.form.Select({
+				name: this.id,
+				store: sampleStore,
+				value: "default.ecl",
+				//maxHeight: -1 // tells _HasDropDown to fit menu within viewport
+				onChange: function(){
+					var filename = dijit.byId(this.id).get("value");
+					baseXhr.get({
+						url: "ecl/" + filename,
+						handleAs: "text",				
+						load: function(eclText) {
+							context.onNewSelection(eclText);
+						},
+						error: function() {
+						}
+					});
+				}
+			}, this.id);
+			try {
+				select.startup();
+			} catch(e) {
+			}
+		}
+		
+	});
+});