001 <%@ page language="java" contentType="text/html; charset=UTF-8"
002 pageEncoding="UTF-8"%>
003 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
004 <html>
005 <head>
006 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
007 <title>BEA WEB2.0 Sample</title>
008 <Style type="text/css"><!--
009 A { text-decoration: none }
010 body {
011 font-family: verdana,arial,helvetica,sans-serif;
012 }
013 table.colorTable {
014 font-family: verdana,arial,helvetica,sans-serif;
015 font-size: 9px;
016 }
017 table.portfolioTable {
018 border-left:1px;
019 border-left-style:solid;
020 border-left-color: #D7D7DE;
021 border-top: 1px;
022 border-top-style:solid;
023 border-top-color: #D7D7DE;
024 }
025
026 table.portfolioTable th {
027 background-color:#EDEDDE;
028 padding: 3px;
029 border-bottom: 1px;
030 border-bottom-style:solid;
031 border-bottom-color: #D7D7DE;
032 border-right: 1px;
033 border-right-style:solid;
034 border-right-color: #D7D7DE;
035 font-family: verdana,arial,helvetica,sans-serif;
036 font-size: 12px;
037 font-weight: bold;
038 color: #000000;
039 cursor: default;
040 text-align: center;
041 }
042 table.portfolioTable td {
043 border-bottom: 1px;
044 border-bottom-style:solid;
045 border-bottom-color: #D7D7DE;
046 border-right: 1px;
047 border-right-style:solid;
048 border-right-color: #D7D7DE;
049 font-family: verdana,arial,helvetica,sans-serif;
050 font-size: 13px;
051 font-weight: normal;
052 color: #000000
053 padding: 2px;
054 }
055 table.portfolioTable tr.alt td {
056 background: #F0F8FF;
057 }
058 table.portfolioTable th.selectedUp {
059 background-image:url(images/AA.gif);
060 background-repeat:no-repeat;
061 background-position:top right;
062 }
063 table.portfolioTable th.selectedDown {
064 background-image:url(images/VV.gif);
065 background-repeat:no-repeat;
066 background-position:bottom right;
067 }
068 input.inputSymbol {
069 font-family: verdana,arial,helvetica,sans-serif;
070 font-size: 13px;
071 text-transform: uppercase;
072 }
073 input.addButton {
074 font-family: verdana,arial,helvetica,sans-serif;
075 font-size: 13px;
076 }
077 .dojoComboBoxOptions {
078 font-family: verdana,arial,helvetica,sans-serif;
079 font-size: 13px;
080 }
081 .title {
082 font-size: 16px;
083 font-weight: bold;
084 }
085 .notes {
086 font-size: 11px;
087 color: #555555;
088 }
089 --></Style>
090 <script type="text/javascript" src="dojo/dojo.js"></script>
091 <script type="text/javascript">
092 dojo.require("dojo.io.cometd");
093 dojo.require("dojo.widget.FilteringTable");
094 dojo.require("dojo.charting.Chart");
095 dojo.require("dojo.lang.timing.Timer");
096 dojo.require("dojo.collections.Store");
097 dojo.require("dojo.io.cookie");
098 dojo.io.cometd.init({}, "/stock/cometd");
099 function $() {
100 return document.getElementById(arguments[0]);
101 }
102 function $$() {
103 return dojo.widget.byId(arguments[0]);
104 }
105 /**************************************************************************************/
106 var itemsStore = null;
107 var seriesStore = null;
108
109 function Item(symbol) {
110 this.symbol = symbol;
111 this.chart = "<input type=\"checkbox\" onclick=\"toggleChart(this, '" + symbol + "');\"/>";
112 this.deletion = "<a href=\"javascript:void(0);\" onclick=\"removeStock('" + symbol + "');\"><img border=\"0\" src=\"images/x.gif\"/></a>";
113 }
114
115 function createSeries(label) {
116 var store = new dojo.collections.Store();
117 store.keyField = "time";
118 var data = new dojo.charting.Series({
119 dataSource: store,
120 bindings: { x:"time", y:"change" },
121 label: label
122 });
123 data.hiddenDataSource = data.dataSource;
124 data.dataSource = new dojo.collections.Store();
125 var s = { label: label, data: data, plotter: dojo.charting.Plotters.CurvedLine };
126 seriesStore.addData(s);
127 return s;
128 }
129
130 function createItem(symbol) {
131 itemsStore.addData(new Item(symbol));
132 }
133 function loadPortfolio() {
134 var items = [];
135 var portfolio = dojo.io.cookie.getCookie("portfolio");
136 if (portfolio) {
137 var symbols = portfolio.split(",");
138 for(var i=0; i<symbols.length; i++) {
139 items.push(new Item(symbols[i]));
140 }
141 }
142 return items;
143 }
144 function savePortfolio() {
145 var portfolio = "";
146 var count = itemsStore.get().length;
147 for (var i=0; i<count; i++) {
148 if(i!=0) portfolio += ",";
149 portfolio += itemsStore.getDataByIndex(i).symbol;
150 }
151 dojo.io.cookie.setCookie("portfolio", portfolio, 100);
152 }
153 function initModel() {
154 itemsStore = stockTable.store;
155 itemsStore.keyField = "symbol";
156 seriesStore = new dojo.collections.Store();
157 seriesStore.keyField = "label";
158 itemsStore.setData(loadPortfolio());
159 var count = itemsStore.get().length;
160 for(var i=0; i<count; i++) {
161 createSeries(itemsStore.getDataByIndex(i).symbol);
162 }
163 }
164
165 /***************************************************************************************/
166 var stockTable = null;
167 var stockChart = null;
168 function initWidgets() {
169 stockTable = dojo.widget.byId("stockTable");
170 dojo.event.connect(stockTable, "fillCell", "onUpdateField");
171
172 stockChart = new StockChart("stockChart");
173 }
174 function initView() {
175 var count = seriesStore.get().length;
176 for (var i=0; i<count; i++) {
177 var s = stockChart.addSeries(seriesStore.getDataByIndex(i));
178 }
179 }
180
181 function updateTable(stock) {
182 var data = itemsStore.getDataByKey(stock.symbol);
183 if (data) {
184 itemsStore.update(data, "openPrice", stock.openPrice);
185 itemsStore.update(data, "price", stock.price);
186 itemsStore.update(data, "quantity", stock.quantity);
187 itemsStore.update(data, "change", stock.change);
188 itemsStore.update(data, "timestamp", stock.timestamp);
189 }
190 }
191
192 function updateChart(stock) {
193 var s = seriesStore.getDataByKey(stock.symbol);
194 if(s) {
195 s.data.hiddenDataSource.addData({time: (stock.timestamp), change: stock.change});
196 if (s.data.dataSource==s.data.hiddenDataSource) {
197 stockChart.render();
198 }
199 }
200 }
201
202 function onUpdate(message) {
203 if (!message.data) {
204 alert("bad message format "+message);
205 return;
206 }
207 var stock = message.data.stock;
208 stock.change = Math.round((stock.price-stock.openPrice)/stock.openPrice*10000)/100;
209 updateTable(stock);
210 updateChart(stock);
211 clearExpiredData(stock.symbol);
212 }
213
214 function onUpdateField(cell, meta, val) {
215 if (val=="") {
216 cell.innerHTML = "-";
217 return;
218 }
219 if (meta.field=="timestamp") {
220 val = new Date(val);
221 if (isNaN(val)){
222 cell.innerHTML = " ";
223 }
224 }
225 if (meta.field=="change") {
226 var color = "#000000";
227 if(val>0) {
228 color = "#008000";
229 } else if(val<0) {
230 color = "#FF0000";
231 }
232 cell.innerHTML = '<font color=\"' + color + '\">' + val + "%</font>";
233 }
234 }
235
236 function showSeries(label) {
237 seriesStore.getDataByKey(label).data.dataSource = seriesStore.getDataByKey(label).data.hiddenDataSource;
238 stockChart.render();
239 displayColorTable();
240 }
241
242 function hideSeries(label) {
243 seriesStore.getDataByKey(label).data.dataSource = new dojo.collections.Store();
244 stockChart.render();
245 displayColorTable();
246 }
247
248 function displayColorTable() {
249 var html = '<table class="colorTable" border="0">';
250 var cols = 8;
251 var count = seriesStore.get().length;
252 var col = 1;
253 for(var i=0; i<count; i++) {
254 var data = seriesStore.getDataByIndex(i).data;
255 if(data.dataSource==data.hiddenDataSource) {
256 if(col == 1) {
257 html += '<tr>';
258 }
259 html += '<td width="20" bgcolor="' + data.color + '"></td>' + '<td>' + data.label + '</td>';
260 if(col == cols || i==count-1) {
261 html += '</tr>'
262 col = 1;
263 } else {
264 col++;
265 }
266 }
267 }
268 html += '</table>';
269 $("seriesColor").innerHTML = html;
270 }
271
272 function clearExpiredData(label) {
273 var dataStore = seriesStore.getDataByKey(label).data.hiddenDataSource;
274 var count = dataStore.get().length;
275 for(var i=0; i<count; i++) {
276 if(dataStore.getDataByIndex(i).time > stockChart.baseline
277 || dataStore.getDataByIndex(i).time > new Date().getTime() - stockChart.X_LABEL_MAX) {
278 if(i>0) {
279 dataStore.removeDataRange(0, i-1);
280 }
281 return;
282 }
283 }
284 }
285
286 function toggleChart(checkbox, label) {
287 checkbox.bgcolor = "#FF0000";
288 if(checkbox.checked) {
289 showSeries(label);
290 } else {
291 hideSeries(label);
292 }
293 }
294
295 function addStock(symbol) {
296 if(!itemsStore.getDataByKey(symbol)) {
297 itemsStore.addData(new Item(symbol));
298 var s = createSeries(symbol);
299 stockChart.addSeries(s);
300 subscribe(symbol);
301 savePortfolio();
302 }
303 }
304
305 function removeStock(symbol) {
306 itemsStore.removeDataByKey(symbol);
307 unsubscribe(symbol);
308 hideSeries(symbol);
309 stockChart.removeSeries(seriesStore.getDataByKey(symbol));
310 seriesStore.removeDataByKey(symbol);
311 savePortfolio();
312 }
313
314 function subscribe(symbol) {
315 dojo.io.cometd.subscribe("/stock/"+symbol, null, "onUpdate");
316 }
317 function unsubscribe(symbol) {
318 dojo.io.cometd.unsubscribe("/stock/"+symbol, null, "onUpdate");
319 }
320 function subscribeAll() {
321 var count = itemsStore.get().length;
322 for (var i=0; i<count; i++) {
323 subscribe(itemsStore.getDataByIndex(i).symbol);
324 }
325 }
326 /*******************************************************************************************/
327 function init() {
328 initWidgets();
329 initModel();
330 initView();
331 subscribeAll();
332 }
333
334 dojo.addOnLoad(init);
335 </script>
336 <script type="text/javascript">
337 function StockChart(containerId) {
338 this.baseline = new Date().getTime();
339 this.X_LABEL_MAX = 105000;
340 this.X_LABEL_DISTANCE = 10000;
341
342 this.axisX = new dojo.charting.Axis();
343 this.axisX.origin="max";
344 this.axisX.range={upper: this.X_LABEL_MAX + this.baseline, lower: this.baseline};
345 this.axisX.showTicks = true;
346 this.axisX.labels = [];
347 this.axisX.label = "Time";
348
349 this.axisY = new dojo.charting.Axis();
350 this.axisY.range={upper:10, lower:-10 };
351 this.axisY.showLines = true;
352 this.axisY.showTicks = true;
353 this.axisY.labels = [];
354 this.axisY.label = "Change"
355
356 this.plot = new dojo.charting.Plot(this.axisX, this.axisY);
357 this.plotArea = new dojo.charting.PlotArea();
358 this.plotArea.size={width:840,height:440};
359 this.plotArea.padding={top:20, right:20, bottom:50, left:60 };
360 this.plotArea.plots.push(this.plot);
361
362 this.chart = new dojo.charting.Chart(null, "Stock chart", "");
363 this.chart.node = dojo.byId(containerId);
364 this.chart.addPlotArea({ x:30,y:30, plotArea:this.plotArea });
365 this.renewAxisX = function() {
366 this.axisX.range={upper:this.X_LABEL_MAX + this.baseline, lower:this.baseline};
367 var offset = 0;
368 var labels = [];
369 while(offset <= this.X_LABEL_MAX) {
370 var timeLabel = dojo.date.strftime(new Date(offset + this.baseline), "%H:%M:%S");
371 labels.push({label: timeLabel, value: offset + this.baseline});
372 offset += this.X_LABEL_DISTANCE;
373 }
374 this.axisX.labels = labels;
375 };
376 this.renewAxisY = function() {
377 var maxLabel = 5;
378 var minLabel = -5;
379 var markerCount = 6;
380 var labels = [];
381 var series = this.plot.series;
382 for(var i=0; i<series.length; i++) {
383 var dataStore = series[i].data.dataSource;
384 var count = dataStore.get().length;
385 for(var j=0; j<count; j++) {
386 if(dataStore.getDataByIndex(j).change>maxLabel) {
387 maxLabel = dataStore.getDataByIndex(j).change;
388 } else if(dataStore.getDataByIndex(j).change<minLabel){
389 minLabel = dataStore.getDataByIndex(j).change;
390 }
391 }
392 }
393 var distance = Math.ceil(Math.max(Math.abs(minLabel), maxLabel)/(markerCount-1));
394 labels.push({label: "0%", value: 0});
395 for(var i=1; i<markerCount; i++) {
396 var val = distance * i;
397 labels.push({label: val + "%", value: val});
398 labels.push({label: "-" + val + "%", value: -1*val});
399 }
400 this.axisY.range = {upper: markerCount*distance, lower: -1*markerCount*distance };
401 this.axisY.labels = labels;
402 }
403 this.addSeries = function(series) {
404 series.data.color = this.plotArea.nextColor();
405 this.plot.addSeries(series);
406 };
407 this.removeSeries = function(series) {
408 for(var i=0; i<this.plot.series.length; i++) {
409 if(series==this.plot.series[i]) {
410 this.plot.series.splice(i, 1);
411 return;
412 }
413 }
414 }
415 this.render = function() {
416 if (new Date().getTime() > this.baseline + this.X_LABEL_MAX) {
417 this.baseline += this.X_LABEL_DISTANCE;
418 this.renewAxisX();
419 }
420 this.renewAxisY();
421 this.plotArea.render();
422 };
423 this.renewAxisX();
424 this.renewAxisY();
425 this.chart.render();
426 }
427
428 </script>
429 </head>
430 <body>
431 <table border="0" align="center" width="900">
432 <tr><td>
433 <table border="0">
434 <tr><td>
435 <img src="images/logo_bea_tl.gif">
436 </td>
437 <td><div class="title">WEB2.0 Sample</div>
438 <br>
439 <div class="notes">The data displayed in the following table is pushed by the server, click the checkbox to show/hide the stock's change line on the chart.</div>
440 </td></tr>
441 </table>
442 </td></tr>
443 <tr><td>
444 <table dojotype="filteringTable" style="width:900px"
445 id="stockTable" multiple="true" alternaterows="true" maxsortable="1"
446 border="0" cellpadding="0" cellspacing="0" align="center"
447 class="portfolioTable">
448 <thead>
449 <tr style="text-align: left">
450 <th field="chart" noSort="true" align="center" width="80">Chart</th>
451 <th field="symbol" width="80" align="center">Symbol</th>
452 <th field="timestamp" dataType="Date" width="80" format="%H:%M:%S" align="center">Time</th>
453 <th field="openPrice" width="100" align="right">Open</th>
454 <th field="price" width="80" align="right">Price</th>
455 <th field="change" dataType="Number" width="80" align="right">Change</th>
456 <th field="quantity" width="100" align="right">Quantity</th>
457 <th field="deletion" noSort="true" align="center" width="80">Delete</th>
458 </tr>
459 </thead>
460 <tbody></tbody>
461 </table>
462 </td></tr>
463 <tr><td>
464 <input type="text" class="inputSymbol" id="symbol" dojoType="combobox" autoComplete="true" dataUrl="data?match=%{searchString}" mode="remote" />
465
466 <input type="button" class="addButton" value="Subscribe" onclick="javascript:addStock($$('symbol').getValue().toUpperCase());">
467
468 <span class="notes">Select a stock and subscribe, the server will start to push the stock's trade information to your browser.</span>
469 </td></tr>
470 <tr><td height="10"></td></tr>
471 <tr><td>
472 <div id="stockChart" style="border:1px solid black;width:900px;height:500px;background-color:#ededde;"></div>
473 </td></tr>
474 <tr><td>
475 <div id="seriesColor" align="center" ></div>
476 </td></tr></table>
477 </body>
478 </html>
|