index.jsp
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   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!=0portfolio += ",";
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 = "&nbsp;";
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 &nbsp;
466 <input type="button" class="addButton" value="Subscribe" onclick="javascript:addStock($$('symbol').getValue().toUpperCase());">
467 &nbsp;
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>