OSDN Git Service

Merge branch 'master' of https://scm.sourceforge.jp/gitroot/h58pcdgame/GameScriptCore...
[h58pcdgame/GameScriptCoreLibrary.git] / www / corelib / core.js
1 /*
2 ****PCD-2013 GameScriptCoreLibrary****
3 Tokyo Gakugei University Senior High School.
4
5 <クライアント要件>
6 Mac OSX: Safari, Chrome
7 Windows: Chrome, IE(10 or later)
8
9 以下の技術が実行可能である必要があります:
10 HTML5
11         Canvas
12         FormData
13
14 <Usage>
15 HTMLソース側に、
16 <div id="MainArea">
17 </div>
18 <div id="Resources">
19 </div>
20 が必要。
21 */
22
23 //
24 //定数
25 //
26
27 //var URL_PCD_Root = "http://localhost/pcd2013dev/www/";
28 //var URL_PCD_Root = "http://192.168.6.242/pcd2013dev/www/";
29 //var URL_PCD_Root = "http://192.168.0.3/PCD2013GSCL/www/";
30 var URL_PCD_Root = "http://192.168.6.242/pcd2013hikarupsp/pcd2013dev/www/";
31 var URL_PCD_Auth = URL_PCD_Root + "auth.php";
32 var URL_PCD_Stage = URL_PCD_Root + "stage/";
33 var URL_PCD_Stage_Local = "stage/";
34
35 //
36 //ゲームマネージャー
37 //
38
39 function GameManager(){
40         //****コンストラクタ****
41         //**インスタンス変数宣言・初期化**
42         //ユーザーID
43         this.userID = 0;
44         //ネットワークマネージャーの設定
45         this.networkManager = new NetworkManager();
46         //必要最低限のCanvasとコンテキストの設定
47         this.mainCanvas = createCanvas("MainCanvas", 640, 480, 0, 0, 1, "MainArea");
48         this.mainCanvas.style.border = "solid 1px";
49         this.debugCanvas = createCanvas("DebugCanvas", 640, 480, 0, 480, 2, "MainArea");
50         this.mainContext = this.mainCanvas.getContext('2d');
51         this.debugContext = this.debugCanvas.getContext('2d');
52         this.debugText = document.getElementById('DebugText');
53         //実行中のGameStageオブジェクトを格納
54         this.runningStage = null;
55         //現在存在しているWidghetのリストを格納
56         this.runningWidghets = [];
57         //タイマーカウントを初期化
58         this.tickCount = 0;
59         this.timeStamp = 0;
60         //タイマーカウントの秒あたりの回数を設定
61         this.tickPerSecond = 60;
62         this.updateIntervalMs = 128;
63         //キーボード状態を格納するプロパティの設定
64         this.keyState = new Object();
65         this.keyState.upArrow = false;
66         this.keyState.downArrow = false;
67         this.keyState.leftArrow = false;
68         this.keyState.rightArrow = false;
69         
70         //ブラウザチェック
71         this.checkBrowser();
72         
73         //**描画コンテキスト取得、設定・HTML5対応チェック**
74         if(!this.mainCanvas || !this.mainCanvas.getContext){
75                 //HTML5未対応の場合
76                 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザでアクセスしてください...。");
77                 return false;
78         }
79         
80         //**Canvas描画コンテキストの初期設定**
81         //mainContext
82         this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
83         this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
84         //debugContext
85         this.debugContext.fillStyle = "rgb(255,255,255)";
86         this.debugContext.strokeStyle = "rgb(0, 0, 0)";
87         this.debugContext.font = "normal 20px sans-serif";
88         
89         // pauseStage()が呼ばれたときにnullじゃなくなる
90         this.stagePausedFunction = null;
91         
92         //**イベントリスナー設定**
93         //コールバックを行うために、イベントリスナーのmanagerプロパティにGameManagerのインスタンスを代入する。
94         //keyDown
95         keyDownEventListener.manager = this;
96         window.addEventListener('keydown', keyDownEventListener, true);
97         //keyUp
98         keyUpEventListener.manager = this;
99         window.addEventListener('keyup', keyUpEventListener, true);
100         //timerTick
101         timerTickEventListener.manager = this;
102         window.setInterval(timerTickEventListener, 1000/this.tickPerSecond);
103         timeStampTimerTickEventListener.manager = this;
104         window.setInterval(timeStampTimerTickEventListener, this.updateIntervalMs);
105 }
106 GameManager.prototype = {
107         //****プロトタイプ宣言****
108         //prototype以下のプロパティは、新規インスタンスに参照が引き継がれる。
109         keyDown: function(event){
110                 //****keyDown****
111                 //コールバックではなくコールバック関数(keyDownEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
112                 keyCode = event.keyCode;
113                 switch(event.keyCode){
114                         case 38:
115                                 //上カーソル
116                                 this.keyState.upArrow = true;
117                                 break;
118                         case 40:
119                                 //下カーソル
120                                 this.keyState.downArrow = true;
121                                 break;
122                         case 37:
123                                 //左カーソル
124                                 this.keyState.leftArrow = true;
125                                 break;
126                         case 39:
127                                 //右カーソル
128                                 this.keyState.rightArrow = true;
129                                 break;
130                 }
131                 
132                 //実行中のステージに通知
133                 if(this.runningStage){
134                         this.runningStage.keyDown(event);
135                         switch(event.keyCode){
136                                 case 38:
137                                 case 40:
138                                 case 37:
139                                 case 39:
140                                         event.preventDefault();
141                                         break;
142                         }
143                 }
144         },
145         keyUp: function(event){
146                 //****keyUp****
147                 //コールバックではなくコールバック関数(keyUpEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
148                 keyCode = event.keyCode;
149                 switch(event.keyCode){
150                         case 38:
151                                 //上カーソル
152                                 this.keyState.upArrow = false;
153                                 break;
154                         case 40:
155                                 //下カーソル
156                                 this.keyState.downArrow = false;
157                                 break;
158                         case 37:
159                                 //左カーソル
160                                 this.keyState.leftArrow = false;
161                                 break;
162                         case 39:
163                                 //右カーソル
164                                 this.keyState.rightArrow = false;
165                                 break;
166                 }
167                 //実行中のステージに通知
168                 if(this.runningStage){
169                         this.runningStage.keyUp(event);
170                 }
171         },
172         timerTick: function(){
173                 //****timerTick****
174                 this.tickCount++;
175                 //実行中のステージに通知
176                 
177                 if(this.runningStage){
178                         if(this.stagePausedFunction == null)
179                         {
180                                 this.runningStage.timerTick();
181                                 for(var i = 0; i < this.runningWidghets.length; i++){
182                                         var w = this.runningWidghets[i];
183                                         if(!w.tick()){
184                                                 // Widghetのtick()からfalseで帰ってきたらWidghetを開放
185                                                 this.runningWidghets.splice(i, 1);
186                                                 i--;
187                                         }
188                                 }
189                         }else
190                         {
191                                 this.stagePausedFunction();
192                         }
193                         this.runningStage.draw();
194                         for(var w in this.runningWidghets){
195                                 w.draw();
196                         }
197                 }
198         },
199         timeStampTimerTick: function(){
200                 //サーバーとの同期カウンタ・タイマー
201                 this.timeStamp += this.updateIntervalMs;
202                 
203                 //サーバーに追加情報をアップデート
204                 //update
205                 if(this.userID != 0){
206                         request = this.networkManager.CreateRequestObject();
207                         //同期モード
208                         request.open('POST', URL_PCD_Root + "update.php?uid=" + this.userID + "&action=refresh", false);
209                         request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
210                         //送信データを準備
211                         aData = new RequestData();
212                         for(i = 0; i < this.runningStage.globalStageObjectList.length; i++){
213                                 anObj = this.runningStage.globalStageObjectList[i];
214                                 if(anObj.ownerUID == this.userID){
215                                         aData.append(i, anObj.objectID + "," + anObj.origin.x + "," + anObj.origin.y + "," + anObj.movingSpeed.x + "," +  anObj.movingSpeed.y+ "," + anObj.attribute);
216                                 }
217                         }
218                         
219                         this.networkManager.RequestObjectDisableCache(request);
220                         request.send(aData.data);
221                         
222                         if(request.status == 0){
223                                 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
224                         }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
225                                 var res = request.responseText;
226                                 if(isValidResponseText(res)){
227                                         var retArray = eval(res);
228                                         this.timeStamp = retArray[0];
229                                         //削除処理
230                                         for(var i = 0; i < retArray[3].length; i++){
231                                                 this.debugOut("Network:removeObject:ObjectID=" + retArray[3][i] + "\n");
232                                                 var anObject = this.runningStage.getGlobalStageObject(retArray[3][i]);
233                                                 if(anObject){
234                                                         this.runningStage.removeStageObject(anObject);
235                                                 }
236                                         }
237                                         //更新処理
238                                         for(var i = 0; i < retArray[1].length; i++){
239                                                 //this.debugOut("Network:refreshObject:ObjectID=" + retArray[1][i] + "\n");
240                                                 anArray = retArray[1][i];
241                                                 var anObject = this.runningStage.getGlobalStageObject(anArray[0]);
242                                                 if(anObject){
243                                                         anObject.origin.x = anArray[1];
244                                                         anObject.origin.y = anArray[2];
245                                                         anObject.movingSpeed.x = anArray[3];
246                                                         anObject.movingSpeed.y = anArray[4];
247                                                 }
248                                         }
249                                         //追加処理
250                                         for(var i = 0; i < retArray[2].length; i++){
251                                                 anArray = retArray[2][i];
252                                                 var args = eval(anArray[7]);
253                                                 var anObject = eval("new " + anArray[5] + "(this.runningStage, args);");
254                                                 if(anObject){
255                                                         this.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
256                                                         anObject.objectID = anArray[0];
257                                                         anObject.origin.x = anArray[1];
258                                                         anObject.origin.y = anArray[2];
259                                                         anObject.movingSpeed.x = anArray[3];
260                                                         anObject.movingSpeed.y = anArray[4];
261                                                         this.runningStage.addStageObject(anObject, true);
262                                                 }
263                                         }
264                                 }
265                         }else{
266                                 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
267                         }
268                         
269                 }
270         },
271         addWidghet: function(w){
272                 w.attached();
273                 this.runningWidghets.push(w);
274         },
275         runStage: function(stage){
276                 //****新たなステージを開始する****
277                 //実行中のステージがあれば終了処理を行わせる。
278                 if(this.runningStage){
279                         this.stopStage();
280                 }
281                 //**新たに開始するステージの初期化**
282                 //GameManager側の情報をGameStageに渡す。
283                 stage.manager = this;
284                 stage.mainCanvas = this.mainCanvas
285                 stage.debugCanvas = this.debugCanvas
286                 stage.mainContext = this.mainContext
287                 stage.debugContext = this.debugContext
288                 //GameStage側の初期化処理を行わせる。
289                 stage.runStage();
290                 //サーバーに追加情報をアップデート
291                 //update
292                 if(this.userID != 0){
293                         request = this.networkManager.CreateRequestObject();
294                         //同期モード
295                         request.open('POST', URL_PCD_Root + "update.php?uid=" + this.userID + "&action=add", false);
296                         request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
297                         //送信データを準備
298                         aData = new RequestData();
299                         for(i = 0; i < stage.globalStageObjectList.length; i++){
300                                 anObj = stage.globalStageObjectList[i];
301                                 aData.append(i, anObj.origin.x.toString() + ":" + anObj.origin.y.toString() + ":" + anObj.movingSpeed.x.toString() + ":" +  anObj.movingSpeed.y.toString() + ":" + anObj.className + ":" + parseArrayToStringSource(anObj.attribute) + ":" + parseArrayToStringSource(anObj.constructorArgs));
302                         }
303                         
304                         this.networkManager.RequestObjectDisableCache(request);
305                         request.send(aData.data);
306                         
307                         if(request.status == 0){
308                                 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
309                         }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
310                                 var res = request.responseText;
311                                 if(isValidResponseText(res)){
312                                         var retArray = eval(res);
313                                         this.timeStamp = retArray[0];
314                                         for(var i = 0; i < stage.globalStageObjectList.length; i++){
315                                                 stage.globalStageObjectList[i].objectID = retArray[1][i];
316                                         }
317                                         for(var i = 0; i < retArray[2].length; i++){
318                                                 var anArray = retArray[2][i];
319                                                 var args = eval(anArray[7]);
320                                                 var anObject = eval("new " + anArray[5] + "(stage, args);");
321                                                 if(anObject){
322                                                         this.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
323                                                         anObject.objectID = anArray[0];
324                                                         anObject.origin.x = anArray[1];
325                                                         anObject.origin.y = anArray[2];
326                                                         anObject.movingSpeed.x = anArray[3];
327                                                         anObject.movingSpeed.y = anArray[4];
328                                                         anObject.attribute = eval(anArray[6]);
329                                                         stage.addStageObject(anObject, true);
330                                                 }
331                                         }
332                                 }
333                         }else{
334                                 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
335                         }
336                         
337                 }
338                 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
339                 this.runningStage = stage;
340         },
341         pauseStage: function(func){
342                 //ステージの実行を一時停止する。一時停止中、funcに指定された関数が毎tick毎に呼ばれる
343                 if(this.stagePausedFunction == null)
344                 {
345                         this.stagePausedFunction = func;
346                         return true;
347                 }else
348                 {
349                         //ステージが一時停止中のfunc()の中から二重にpauseStage()を呼んではいけない
350                         return false;
351                 }
352         },
353         resumeStage: function(){
354                 if(this.stagePausedFunction != null)
355                 {
356                         //必ずpauseStage()の引数に指定したfunc()の中から呼ばれる・・・はず。
357                         this.stagePausedFunction = null;
358                         return true;
359                 }else
360                 {
361                         return false;
362                 }
363         },
364         stopStage: function(){
365                 //****現在実行中のステージを終了する****
366                 if(this.runningStage){
367                         //runningStageから解除することで、イベントの通知は行われなくなる。
368                         var aGameStage = this.runningStage;
369                         this.runningStage = null;
370                         //GameStage側の終了処理を行わせる。
371                         aGameStage.stopStage();
372                         //GameStageインスタンスからGameManagerの情報を削除する。
373                         aGameStage.manager = null;
374                         aGameStage.mainCanvas = null;
375                         aGameStage.debugCanvas = null;
376                         aGameStage.mainContext = null;
377                         aGameStage.debugContext = null;
378                 }
379         },
380         loadStageFromLocal: function(code, onLoaded){
381                 var ____stage = eval(code);
382                 mainManager.runStage(____stage);
383         },
384         loadStageFromNetwork: function(name){
385                 //urlに存在するjavascriptファイルを利用してステージを作成する。
386                 request = this.networkManager.CreateRequestObject();
387                 //同期モード
388                 request.open('GET', URL_PCD_Stage + name + ".js", false);
389                 this.networkManager.RequestObjectDisableCache(request);
390                 request.send(null);
391                 if(request.status == 0){
392                         alert("ネットワークにアクセスできません。" + request.status + ":" + request.statusText);
393                 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
394                         var stage = eval(request.responseText);
395                         mainManager.runStage(stage);
396                 }else{
397                         alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
398                 }
399         },
400         debugOut: function(str){
401                 if(!/*@cc_on!@*/false)
402                 {
403                         this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
404                 } else{
405                         this.debugText.innerHTML = str + this.debugText.value;
406                 }
407         },
408         checkBrowser: function(){
409                 //http://d.hatena.ne.jp/Naotsugu/20110927/1317140891
410                 var userAgent = window.navigator.userAgent.toLowerCase();
411                 var appVersion = window.navigator.appVersion.toLowerCase();
412                 
413                 if (userAgent.indexOf('opera') != -1) {
414                         //opera
415                         this.debugOut("Browser:Opera\n");
416                 } else if (userAgent.indexOf('msie') != -1) {
417                         if (appVersion.indexOf("msie 9.") != -1) {
418                                 //ie9
419                                 this.debugOut("Browser:IE9\n");
420                         } else if (appVersion.indexOf("msie 8.") != -1) {
421                                 //ie8
422                                 this.debugOut("Browser:IE8\n");
423                         } else if (appVersion.indexOf("msie 7.") != -1) {
424                                 //ie7
425                                 this.debugOut("Browser:IE7\n");
426                         } else if (appVersion.indexOf("msie 6.") != -1) {
427                                 //ie6
428                                 this.debugOut("Browser:IE6\n");
429                         } else{
430                                 this.debugOut("Browser:IE?\n");
431                         }
432                 } else if (userAgent.indexOf('chrome') != -1) {
433                         //chrome
434                         this.debugOut("Browser:Chrome\n");
435                 } else if (userAgent.indexOf('safari') != -1) {
436                         //safari
437                         this.debugOut("Browser:Safari\n");
438                 } else if (userAgent.indexOf('gecko') != -1) {
439                         //gecko
440                         this.debugOut("Browser:Gecko\n");
441                 } else {
442                         //unknown
443                         this.debugOut("Browser:Unknown\n");
444                 }
445                 this.debugOut(userAgent + "::" + appVersion + "\n");
446         },
447 };
448
449 //
450 //その他のクラス
451 //
452
453 function Point2D(x, y){
454         this.x = x;
455         this.y = y;
456 }
457 Point2D.prototype = {
458         
459 }
460
461 function Rectangle(x, y, width, height){
462         this.origin = new Point2D(x,y);
463         this.size = new Point2D(width,height);
464 }
465 Rectangle.prototype = {
466         
467 }
468
469 function ResourceManager(){
470         //Not implemented.
471     this.resourceObjectList = new Array();
472     
473         this.ResourceTag.prototype = {
474         
475     }
476 }
477 ResourceManager.prototype = {
478         //Not implemented.
479     addAudioResource: function(id, src){
480         dobj = document.createElement("audio");
481         parent = document.getElementById("Resources");
482         dobj.id = id;
483         parent.appendChild(dobj);
484         
485         this.resourceObjectList.push(dobj);
486         
487         dobj.isLoaded = false;
488         dobj.onload = this.resourceLoaded;
489         dobj.src = src;
490     },
491     resourceLoaded: function(){
492         //コールバック関数のthisはコールバック関数の設定先オブジェクト(DOMObject)となる。
493         this.isLoaded = true;
494     },
495     waitForLoadResource: function(){
496         for(;;){
497             for(i = 0; i < resourceObjectList.length; i++){
498                 if(!resourceObjectList[i].isLoaded){
499                     
500                     break;
501                 }
502             }
503             if(i == resourceObjectList.length){
504                 return;
505             }
506         }
507     },
508 }
509
510 function NetworkManager(){
511
512 }
513 NetworkManager.prototype = {
514 //from http://hakuhin.jp/js/xmlhttprequest.html
515         CreateRequestObject: function(){
516                 var rq = null;
517                 // XMLHttpRequest
518                 try{
519                         // XMLHttpRequest オブジェクトを作成
520                         rq = new XMLHttpRequest();
521                 }catch(e){}
522                 // Internet Explorer
523                 try{
524                         rq = new ActiveXObject('MSXML2.XMLHTTP.6.0');
525                 }catch(e){}
526                 try{
527                         rq = new ActiveXObject('MSXML2.XMLHTTP.3.0');
528                 }catch(e){}
529                 try{
530                         rq = new ActiveXObject('MSXML2.XMLHTTP');
531                 }catch(e){}
532                 if(rq == null){
533                         return null;
534                 }
535                 return rq;
536         },
537         RequestObjectDisableCache: function(rq){
538                 //call after open request.
539                 //disable cache
540                 //http://vird2002.s8.xrea.com/javascript/XMLHttpRequest.html
541                 rq.setRequestHeader('Pragma', 'no-cache');                              // HTTP/1.0 における汎用のヘッダフィールド
542                 rq.setRequestHeader('Cache-Control', 'no-cache');               // HTTP/1.1 におけるキャッシュ制御のヘッダフィールド
543                 rq.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
544                 
545         },
546 }
547
548 function RequestData(){
549         this.data = "";
550 }
551 RequestData.prototype = {
552         append: function(name, data){
553                 this.data += this.getURLEncodedString(name) + "=" + this.getURLEncodedString(data) + "&";
554         },
555         getURLEncodedString: function(str){
556                 return encodeURIComponent(str).replace(/%20/g, '+');
557         }
558 }
559
560 //
561 //サブルーチン
562 //
563
564 function createCanvas(id, width, height, x, y, z, parent)
565 {
566         //識別名idで
567         //width * heightの大きさのCanvasを
568         //(x,y,z)に生成する。
569         //parentには、Canvasタグを包含することになるDOMオブジェクトのidを指定する。
570         canvas = document.createElement("canvas");
571         parent = document.getElementById(parent);
572
573         canvas.id = id;
574
575         parent.appendChild(canvas);
576
577         canvas.style.position = "absolute";
578         canvas.style.top = y + "px";
579         canvas.style.left = x + "px";
580         canvas.style.zIndex = z;
581
582         canvas.width = width;
583         canvas.height = height;
584
585         return canvas;
586 }
587
588 function createDOMObject(typestr, idstr, parentidstr)
589 {
590         dobj = document.createElement(typestr);
591         parentObj = document.getElementById(parentidstr);
592         
593         dobj.id = idstr;
594         parentObj.appendChild(dobj);
595         
596         return dobj;
597 }
598
599 function destroyDOMObjectByID(id)
600 {
601         //識別名idのDOMオブジェクトを破棄する。
602         object = document.getElementById(id);
603         parentObj = object.parentNode;
604
605         parentObj.removeChild(object);
606 }
607
608 function removeObjectFromArray(anArray, anObject)
609 {
610         //anArray中にある全てのanObjectを削除し、空いた部分は前につめる。
611         for(var i = 0; i < anArray.length; i++){
612                 if(anArray[i] == anObject){
613                         anArray.splice(i, 1);
614                 }
615         }
616 }
617
618 function parseArrayToStringSource(anArray){
619         //parseArrayToStringSource([1,"321a", "abc", ["cder", "", 554]]);
620         if(!anArray){
621                 return "null";
622         }
623
624         var srcstr = "[";
625
626         for(var i = 0; i < anArray.length; i++){
627                 if(anArray[i] instanceof Array){
628                         srcstr += parseArrayToStringSource(anArray[i]) + ",";
629                 } else if(!isNaN(anArray[i]) && anArray[i].toString().replace(/\s+/g, "").length > 0){
630                         //isNaNだけでは数値判定できないので、文字列化後の空白文字を削除した長さも検査している。
631                         srcstr += anArray[i] + ",";
632                 } else{
633                         srcstr += "'" + anArray[i] + "',";
634                 }
635         }
636         
637         if(srcstr !== "["){
638                 //最後の余計なカンマを削除
639                 srcstr = srcstr.slice(0, srcstr.length - 1);
640         }
641         srcstr += "]";
642         
643         return srcstr;
644 }
645
646 function isValidResponseText(res)
647 {
648         if(res.indexOf("Warning") == -1){
649                 if(res.indexOf("error") == -1){
650                         return true;
651                 }
652         }
653         alert("サーバースクリプトがエラーを返しました。:" + res);
654         mainManager.stopStage();
655         return false;
656 }
657
658 //
659 //イベントリスナー
660 //
661 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトになり、通常とは異なるので注意。
662
663 function keyDownEventListener(event)
664 {
665         keyDownEventListener.manager.keyDown(event);
666 }
667
668 function keyUpEventListener(event)
669 {
670         keyUpEventListener.manager.keyUp(event);
671         event.preventDefault();
672 }
673
674 function timerTickEventListener(event)
675 {
676         timerTickEventListener.manager.timerTick(event);
677 }
678
679 function timeStampTimerTickEventListener(event)
680 {
681         timerTickEventListener.manager.timeStampTimerTick(event);
682 }
683
684 //
685 //Canvas直接描画関連
686 //
687
688 function drawText(gcontext, text, x, y)
689 {
690         //背景をfillStyle
691         //前景をstrokeStyleで塗りつぶした文字列を描画する
692         //塗りつぶし高さは20px固定
693         //fillTextの座標は文字列の左下!
694         textsize = gcontext.measureText(text);
695         gcontext.fillRect(x, y, textsize.width, 20);
696         gcontext.save();
697         gcontext.fillStyle = gcontext.strokeStyle;
698         gcontext.fillText(text, x, y + 20 - 1);
699         gcontext.restore();
700 }
701
702 function drawArcDegree(gcontext, radius, startAngleDegree, endAngleDegree, x, y, anticlockwise)
703 {
704         //半径radius, 中心座標(x,y)の円弧の、
705         //startAngleDegreeからendAngleDegreeまでの範囲を、
706         //(!anticlockwise)=時計回り
707         //(anticlockwise) =反時計回り
708         //に描画する。
709         //角度は度を利用する。
710         startAngleRadian = startAngleDegree * Math.PI / 180;
711         endAngleRadian = endAngleDegree * Math.PI / 180;
712         
713         gcontext.beginPath();
714         gcontext.arc(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(radius), startAngleRadian, endAngleRadian, anticlockwise);
715         gcontext.fill();
716         gcontext.stroke();
717         gcontext.closePath();
718 }
719
720 function fillRect(gContext, x, y, w, h)
721 {
722         gContext.fillRect(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(w), drawCoordinatesInInteger(h));
723 }
724
725 function strokeRect(gContext, x, y, w, h)
726 {
727         gContext.strokeRect(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(w), drawCoordinatesInInteger(h));
728 }
729
730 function drawCoordinatesInInteger(coordinateElement)
731 {
732         //from http://www.html5rocks.com/ja/tutorials/canvas/performance/
733         // With a bitwise or.
734         return ((0.5 + coordinateElement) | 0);
735 }
736