df5b6328ed3d6762e751dd5fd74fba90b747bdf0
[apps/web/sample/SensorBall.git] / project / js / main.js
1 /*
2  *      Copyright 2013  Samsung Electronics Co., Ltd
3  *
4  *      Licensed under the Flora License, Version 1.1 (the "License");
5  *      you may not use this file except in compliance with the License.
6  *      You may obtain a copy of the License at
7  *
8  *              http://floralicense.org/license/
9  *
10  *      Unless required by applicable law or agreed to in writing, software
11  *      distributed under the License is distributed on an "AS IS" BASIS,
12  *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *      See the License for the specific language governing permissions and
14  *      limitations under the License.
15  */
16
17 /*global $: false, tizen: false, navigator: false, app: true*/
18
19 var app = {
20         sensor: null,
21         R: 2000.0, // // gravity constant * m * M
22
23         gameWidth: 0,
24         gameHeight: 0,
25         ballWidth: 0,
26         ballHeight: 0,
27         sunWidth: 0,
28         sunHeight: 0,
29         ballX: 0,
30         ballY: 0,
31         dX: 0,
32         dY: 0,
33         sunX: 0,
34         sunY: 0,
35
36         backgroundWidth: 0,
37         backgroundHeight: 0,
38         backgroundTop: 0,
39         backgroundLeft: 0,
40
41         resistance: 0.98, // air
42         friction: 0.90, // bounce
43         sideFriction: 0.95,
44         frictionC: 0.002,
45         repulse: 0.6,
46         cdd: -0.3,
47
48         timeout: null,
49         current: 'ball',
50         event: null,
51         animationInterval: 40,
52
53         /**
54          * Draw earth background position
55          * @param {int} x current x earth position
56          * @param {int} y current y earth position
57          */
58         earthUpdateBackgroundPosition: function earthUpdateBackgroundPosition(x, y) {
59                 "use strict";
60                 var rX, rY,
61                         cX, cY,
62                         tX, tY,
63                         bdX, bdY,
64                         br;
65
66                 rX = -30.0;
67                 rY = -30.0;
68
69                 cX = (this.gameWidth - this.backgroundWidth) / 2;
70                 cY = (this.gameHeight - this.backgroundHeight) / 2;
71
72                 tX = cX + (-x * rX);
73                 tY = cY + (y * rY);
74
75                 bdX = tX - this.backgroundLeft;
76                 bdY = tY - this.backgroundTop;
77
78                 br = 0.2;
79
80                 this.backgroundLeft += bdX * br;
81                 this.backgroundTop += bdY * br;
82
83                 $('.background').css('background-position', (this.backgroundLeft - 330) + 'px ' + (this.backgroundTop - 330) + 'px');
84         },
85
86         /**
87          * Draw sun position
88          * @param {int} x current x earth position
89          * @param {int} y current y earth position
90          */
91         earthUpdateSunPosition: function earthUpdateSunPosition(x, y) {
92                 "use strict";
93                 var rX, rY,
94                         cX, cY,
95                         tX, tY,
96                         bdX, bdY,
97                         br;
98
99                 rX = -8.0;
100                 rY = -8.0;
101
102                 cX = (this.gameWidth - this.sunWidth) / 2;
103                 cY = (this.gameHeight - this.sunHeight) / 2;
104
105                 tX = cX + (-x * rX);
106                 tY = cY + (y * rY);
107
108                 bdX = tX - this.sunX;
109                 bdY = tY - this.sunY;
110
111                 br = 0.2;
112
113                 this.sunX += bdX * br;
114                 this.sunY += bdY * br;
115
116                 $('#sun').css('left', this.sunX + 'px');
117                 $('#sun').css('top', this.sunY + 'px');
118                 $('#sun').show();
119         },
120
121         /**
122          * Deceleration - used when the earth leaves the Sun's gravitation
123          */
124         deceleration: function deceleration() {
125                 "use strict";
126                 this.dX *= 0.6;
127                 this.dY *= 0.6;
128         },
129
130         /**
131          * Draw the next animation frame for the 'earth' tab
132          */
133         earthEvents: function earthEvents() {
134                 "use strict";
135
136                 var event, borderTolerance,
137                         x, y,
138                         dXl, dYl,
139                         d,
140                         d2,
141                         ddx, ddy,
142                         ratio;
143
144                 event = this.event;
145                 borderTolerance = 30;   // when Earth reach a border, then Earth is "moving"
146
147                 x = -event.accelerationIncludingGravity.x;
148                 y = -event.accelerationIncludingGravity.y;
149
150                 // calculate X and Y distances between the Sun and Earth
151                 dXl = (this.sunX + this.sunWidth / 2 - (this.ballX + (this.ballWidth / 2))); // x distance
152                 dYl = (this.sunY + this.sunHeight / 2 - (this.ballY + (this.ballHeight / 2))); // y distance
153
154                 if (Math.abs(dXl) < 1) {
155                         dXl = dXl < 0 ? -1 : 1; // round to 1 * sign
156                 }
157                 if (Math.abs(dYl) < 1) {
158                         dYl = dYl < 0 ? -1 : 1; // round to 1 * sign
159                 }
160
161                 // distance squared
162                 d2 = Math.pow(dXl, 2) + Math.pow(dYl, 2);
163                 // distance
164                 d = Math.sqrt(d2);
165
166                 // acceleration is proportional to 1/d2 [a=GM/r^2]
167                 // X component is also proportional to dXl / d
168                 ddx = (this.R * dXl) / (d2 * d);
169                 ddy = (this.R * dYl) / (d2 * d);
170
171                 // apply acceleration to speed
172                 this.dX += ddx;
173                 this.dY += ddy;
174
175                 ratio = Math.sqrt(Math.pow(this.dX, 2) + Math.pow(this.dY, 2)) / 25; // max speed
176                 if (ratio > 1) { // speed limit achieved
177                         this.dX /= ratio;
178                         this.dY /= ratio;
179                 }
180
181                 // apply speed to Earth position
182                 this.ballX += this.dX;
183                 this.ballY += this.dY;
184
185                 // What do it when the earth leaves gravitation of the Sun?;
186                 if (this.ballX > (this.gameWidth + borderTolerance)) {
187                         this.ballX = -borderTolerance; this.deceleration();
188                 }
189                 if (this.ballY > (this.gameHeight + borderTolerance)) {
190                         this.ballY = -borderTolerance; this.deceleration();
191                 }
192                 if (this.ballX < -borderTolerance) {
193                         this.ballX = this.gameWidth + borderTolerance; this.deceleration();
194                 }
195                 if (this.ballY < -borderTolerance) {
196                         this.ballY = this.gameHeight + borderTolerance; this.deceleration();
197                 }
198
199                 // update Earth position
200                 $('.ball').css('left', this.ballX + 'px');
201                 $('.ball').css('top', this.ballY + 'px');
202
203                 // relative depth Sun / Earth
204                 if (this.dY > 0) {
205                         $('.ball').css('z-index', 100);
206                 } else {
207                         $('.ball').css('z-index', 20);
208                 }
209
210                 this.earthUpdateBackgroundPosition(x, y);
211                 this.earthUpdateSunPosition(x, y);
212         },
213
214         /**
215          *  Checks if the ball already was on the edge in the previous step
216          *
217          *  If so, this is not a 'real' bounce - the ball is just laying on the edge
218          *  Uses globals: ballX, ballY, ballWidth, ballHeight, gameWidth, gameHeight
219          *
220          *  @return {Object}
221          */
222         shouldVibrateIfHitsEdge: function shouldVibrateIfHitsEdge() {
223                 "use strict";
224                 var ret = {
225                         x: true,
226                         y: true
227                 };
228
229                 if (this.ballX <= 0) {
230                         ret.x = false;
231                 } else if ((this.ballX + this.ballWidth) >= this.gameWidth) {
232                         ret.x = false;
233                 }
234                 if (this.ballY <= 0) {
235                         ret.y = false;
236                 } else if ((this.ballY + this.ballHeight) >= this.gameHeight) {
237                         ret.y = false;
238                 }
239
240                 return ret;
241         },
242
243         /**
244          * Draw the next animation frame for the 'ball' tab
245          */
246         ballEvents: function ballEvents() {
247                 "use strict";
248                 var event,
249                         x,
250                         y,
251                         stickTop = 0,
252                         stickLeft = 0,
253                         stickBottom = 0,
254                         stickRight = 0,
255                         rX,
256                         rY,
257                         ddx,
258                         ddy,
259                         shouldVibrate = null,
260                         isHittingEdge = null;
261
262                 event = this.event;
263
264                 x = -event.accelerationIncludingGravity.x;
265                 y = -event.accelerationIncludingGravity.y;
266
267                 stickTop = 0;
268                 stickLeft = 0;
269                 stickBottom = 0;
270                 stickRight = 0;
271
272                 rX = this.ballX;
273                 rY = this.ballY;
274                 ddx = x * -this.cdd;
275                 ddy = y * this.cdd;
276                 this.dX += ddx;
277                 this.dY += ddy;
278                 this.dX *= this.resistance;
279                 this.dY *= this.resistance;
280
281                 shouldVibrate = this.shouldVibrateIfHitsEdge();
282
283                 this.ballX += this.dX;
284                 this.ballY += this.dY;
285
286                 if (this.ballX < 0) {
287                         this.ballX = 0;
288                         this.dX = Math.abs(this.dX) * this.friction - this.frictionC;
289                         this.dY *= this.sideFriction;
290                         stickLeft = 1;
291                 } else if ((this.ballX + this.ballWidth) > this.gameWidth) {
292                         this.ballX = this.gameWidth - this.ballWidth;
293                         this.dX = -Math.abs(this.dX) * this.friction + this.frictionC;
294                         this.dY *= this.sideFriction;
295                         stickRight = 1;
296                         if (this.ballX < 0) {
297                                 this.ballX = 0;
298                         }
299                 }
300
301                 if (this.ballY < 0) {
302                         this.ballY = 0;
303                         this.dY = Math.abs(this.dY) * this.friction - this.frictionC;
304                         this.dX *= this.sideFriction;
305                         stickTop = 1;
306                 } else if ((this.ballY + this.ballHeight) > this.gameHeight) {
307                         this.ballY = this.gameHeight - this.ballHeight;
308                         this.dY = -Math.abs(this.dY) * this.friction + this.frictionC;
309                         this.dX *= this.sideFriction;
310                         stickBottom = 1;
311                         if (this.ballY < 0) {
312                                 this.ballY = 0;
313                         }
314                 }
315
316                 isHittingEdge = {
317                         x: (stickLeft || stickRight) && Math.abs(this.dX) > 1,
318                         y: (stickTop || stickBottom) && Math.abs(this.dY) > 1
319                 };
320
321                 // if on the edge and the hitting speed is high enough
322                 if ((shouldVibrate.x && isHittingEdge.x) || (shouldVibrate.y && isHittingEdge.y)) {
323                         if (typeof navigator.webkitVibrate === 'function') {
324                                 navigator.webkitVibrate(100);
325                         } else {
326                                 navigator.vibrate(100);
327                         }
328                 }
329
330                 $('.ball').css('left', this.ballX + 'px');
331                 $('.ball').css('top', this.ballY + 'px');
332
333                 rX = this.ballX - rX;
334                 rY = this.ballY - rY;
335
336         },
337
338         /**
339          * Draw the next animation frame
340          */
341         fun: function fun() {
342                 "use strict";
343                 if (this.event) {
344                         switch (this.current) {
345                         case 'ball':
346                                 this.ballEvents();
347                                 break;
348                         case 'earth':
349                                 this.earthEvents();
350                                 break;
351                         case 'baloon':
352                                 this.ballEvents();
353                                 break;
354                         default:
355                                 console.warn("Incorrect current mode");
356                                 this.ballEvents();
357                         }
358                 }
359
360                 // animation - go to next step;
361                 if (this.timeout !== null) {
362                         clearTimeout(this.timeout);
363                 }
364                 this.timeout = setTimeout(this.fun.bind(this), this.animationInterval);
365         },
366
367         /**
368          * Switch to the 'ball' tab
369          */
370         startBall: function startBall() {
371                 "use strict";
372
373                 $('.ui-content').removeClass('background1 background2 background3').addClass('background1');
374                 $(':jqmData(role="controlbar")').find('.ui-btn').removeClass('ui-btn-hover-s ui-btn-down-s');
375                 this.gameHeight = $('.background').outerHeight();
376
377                 this.cdd = -0.3;
378                 this.resistance = 0.98;
379                 this.friction = 0.90;
380                 this.sideFriction = 0.95;
381                 this.frictionC = 0.002;
382
383                 this.current = 'ball';
384
385                 $('#sun').remove();
386                 $('.ball').attr('src', './images/ball1.png');
387                 $('.ball').css('width', '86px');
388                 $('.ball').css('height', '86px');
389
390                 $('.background').css('background-position', '0px -90px');
391
392                 this.ballWidth = parseInt($('.ball').css('width'), 10);
393                 this.ballHeight = parseInt($('.ball').css('height'), 10);
394         },
395
396         /**
397          * Switch to the 'sky' tab
398          */
399         startSky: function startSky() {
400                 "use strict";
401                 $('.ui-content').removeClass('background1 background2 background3').addClass('background2');
402                 $(':jqmData(role="controlbar")').find('.ui-btn').removeClass('ui-btn-hover-s ui-btn-down-s');
403                 this.gameHeight = $('.background').outerHeight();
404
405                 this.cdd = 0.05;
406                 this.resistance = 0.90;
407                 this.friction = 0.98;
408                 this.sideFriction = 0.95;
409                 this.frictionC = 0.002;
410
411                 this.current = 'baloon';
412
413                 $('#sun').remove();
414                 $('.ball').attr('src', './images/balloon.png');
415                 $('.ball').css('width', '100px');
416                 $('.ball').css('height', '100px');
417
418                 $('.background').css('background-position', '0px -80px');
419
420                 this.ballWidth = parseInt($('.ball').css('width'), 10);
421                 this.ballHeight = parseInt($('.ball').css('height'), 10);
422         },
423
424         /**
425          * Switch to the 'space' tab
426          */
427         startSpace: function startSpace() {
428                 "use strict";
429                 var backgroundPosition, arrayPos;
430
431                 $('.ui-content').removeClass('background1 background2 background3').addClass('background3');
432                 $(':jqmData(role="controlbar")').find('.ui-btn').removeClass('ui-btn-hover-s ui-btn-down-s');
433
434                 this.gameHeight = $('.background').outerHeight();
435
436                 this.friction = 0.60; // bounce
437                 this.sideFriction = 0.95;
438                 this.frictionC = 0.0;
439
440                 this.current = 'earth';
441
442                 $('.ball').attr('src', './images/earth.png');
443                 $('#main').append('<img id="sun" class="sun" src="./images/sun.png" style="display: none;"></img>');
444
445                 this.sunX = (this.gameWidth - parseInt($('#sun').css('width'), 10)) / 2;
446                 this.sunY = (this.gameHeight - parseInt($('#sun').css('height'), 10)) / 2;
447                 $('.ball').css('width', '50px');
448                 $('.ball').css('height', '50px');
449
450                 $('.background').css('background-position', '0px 0px');
451
452                 this.ballWidth = parseInt($('.ball').css('width'), 10);
453                 this.ballHeight = parseInt($('.ball').css('height'), 10);
454                 this.sunWidth = parseInt($('#sun').css('width'), 10);
455                 this.sunHeight = parseInt($('#sun').css('height'), 10);
456
457                 backgroundPosition = $('.background').css('background-position');
458
459                 arrayPos = backgroundPosition.split(' ');
460                 this.backgroundTop = parseInt(arrayPos[0], 10);
461                 this.backgroundLeft = parseInt(arrayPos[1], 10);
462                 this.backgroundWidth = parseInt($('.background').css('width'), 10);
463                 this.backgroundHeight = parseInt($('.background').css('height'), 10);
464         },
465
466         saveSensorData: function saveSensorData(event) {
467                 "use strict";
468                 this.event = event;
469         }
470 };
471
472 $(document).ready(function () {
473         "use strict";
474         var img,
475                 contentHeight = screen.availHeight - $('div[data-role="header"]').outerHeight() - $('div[data-role="footer"]').outerHeight();
476
477         $('div[data-role="content"]').css('height', contentHeight - 33);
478         app.gameWidth = screen.availWidth;
479         app.ballWidth = parseInt($('.ball').css('width'), 10);
480         app.ballHeight = parseInt($('.ball').css('height'), 10);
481
482         window.addEventListener('devicemotion', app.saveSensorData.bind(app), false);
483
484         app.fun();
485
486         $(window).on('tizenhwkey', function (e) {
487                 if (e.originalEvent.keyName === "back") {
488                         tizen.application.getCurrentApplication().exit();
489                 }
490         });
491
492         $('#btnBall').bind('click', function (event) {
493                 preventSecClick(this);
494                 app.startBall();
495         });
496
497         $('#btnSky').bind('click', function (event) {
498                 preventSecClick(this);
499                 app.startSky();
500         });
501
502         $('#btnSpace').bind('click', function () {
503                 preventSecClick(this);
504                 app.startSpace();
505         });
506
507         function preventSecClick(current) {
508                 $('#sun').remove();
509                 $("#footerControls li a.ui-disabled").removeClass('ui-disabled');
510                 $(current).addClass("ui-disabled");
511         }
512
513         $('#mainPage').on('pageshow', function () {
514                 app.startBall();
515         });
516
517         document.addEventListener('webkitvisibilitychange', function (event) {
518                 if (document.webkitVisibilityState === 'visible') {
519                         app.fun();
520                 }
521         });
522
523         // Preload backgrounds;
524         img = $('<img>').hide();
525         img.attr('src', 'images/background1.png');
526 });
527
528 $(window).resize(function () {
529         'use strict';
530         app.gameWidth = screen.availWidth;
531         app.gameHeight = $('.background').outerHeight();
532 });