As explained here in the known bugs, I experienced on my Nexus 10 tablet, with firefox the following problem :
16 |
window.addEventLister("deviceorientation", handler, false) takes time to apply (sometimes it's needed to put the application in background, reload it, change tab, etc ... to force the connection to the gyroscope+accelerometer).
17 |
Here is a use case
18 |
19 |
take your android device and close firefox
20 |
load this page with firefox
21 |
wait till numbers display or an alert tells that it failed
15 |
16 |
17 |
51 |
52 |
--------------------------------------------------------------------------------
/src/demo.events.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PanoramaSensorsViewer / Events
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/demo.controls.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PanoramaSensorsViewer / Controls
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #PanoramaSensorsViewer
2 |
3 | Maybe you're a developer, so, no more #RTFM, you'll read this later ;-) so **go [straight to the API doc](https://github.com/topheman/PanoramaSensorsViewer/tree/master/src/#panoramasensorsviewer---api-doc)** ! (:wink) ...
4 |
5 | This library will let you make webapps that can browse Google Street View just by moving your mobile device and looking at the panorama like you were inside it.
6 |
7 | * Try [Topheman Street View](http://streetview.topheman.com/ "Topheman Street View"), a website I made based on PanoramaSensorsViewer
8 | * The examples included in this repository can also be [tested online](http://labs.topheman.com/PanoramaSensorsViewer "tested online")
9 |
10 | ##Intro
11 |
12 | PanoramaSensorsViewer takes advantage of Google Maps API v3 to display panoramas and takes care of all the tricky things such as :
13 |
14 | * Init a Google Street View panorama
15 | * At any position
16 | * Taking care of making the connection to the Google Maps API
17 | * Lets you have your custom panoramas (or anything else since you have full access to the Google Maps API)
18 | * Connecting your device's gyroscope+accelerometer to this panorama
19 | * Taking care of your device screen orientation (keeping the movements consistent whatever orientation you're on)
20 |
21 | ##Requirements
22 |
23 | For dependency management purposes (and also safe and simple script lazy load), PanoramaSensorsViewer was built using **RequireJS**, a module loader.
24 |
25 | For those of you who don't know **RequireJS**, I invite you to [check it out](http://requirejs.org/). Anyone who already use RequireJS shouldn't have any problem, however, this repo contains examples of how to use PanoramaSensorsViewer, if you want to make your own app, you can simply clone the repo ...
26 |
27 | PS : I also use RequireJS because it allows me to make builds with r.js via grunt (something you should also check out). For a next version, I'll try to ditch RequireJS via a build step (like the jQuery builder).
28 |
29 | ###RequireJS Setup
30 |
31 | You'll find the RequireJS config file here : [require.config.js](https://github.com/topheman/PanoramaSensorsViewer/blob/master/src/js/require.config.js). Nothing extraordinary here, only the declaration of the **async** plugin which is used to call google.
32 |
33 | If you moved the *topheman-panorama* folder, you can make an alias in this file so that RequireJS will find it.
34 |
35 | ###Grunt Setup (optional)
36 |
37 | Maybe like me you don't start a project without grunt anymore, so there is a Gruntfile.js and a package.json.
38 |
39 | The default task will launch you a server on localhost:9002 ...
40 |
41 | ##Known bugs
42 |
43 | * Chrome for Android : on some devices, the noise filter for the deviceorientation event can be pretty bad, so the image can be jumpy.
44 | * Firefox for Android : the noise filter is ok, however, the connection to the sensors (accelerometer+gyroscope) seems to be erratic - here is [the use case to reproduce the bug](http://labs.topheman.com/PanoramaSensorsViewer/test.bug.android.firefox.html).
45 |
46 | iOs works very well.
--------------------------------------------------------------------------------
/src/js/utils/screenManager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013-2014 Christophe Rosset - https://github.com/topheman/PanoramaSensorsViewer
3 | *
4 | * Released under MIT License :
5 | * https://github.com/topheman/PanoramaSensorsViewer/blob/master/LICENSE
6 | */
7 |
8 | /**
9 | * Little util I made so that your panorama fits and resizes the window
10 | * You're free to use it.
11 | * Good point : when you resizes, it won't resize the panorama at each resize event.
12 | *
13 | * Just include it and launch the two methods when your panorama is initialized - see examples in the repo if necessary.
14 | */
15 |
16 | (function (screenManager){
17 |
18 | if (typeof define === 'function' && define.amd) {
19 | // AMD. Register as an anonymous module.
20 | define(screenManager);
21 | } else {
22 | // Browser globals
23 | window.screenManager = screenManager();
24 | }
25 |
26 | })(function(){
27 |
28 | var screenManager,
29 | _privateHelper,
30 | panoramaDiv,
31 | panoramaManager;
32 |
33 | _privateHelper = {
34 | panoramaFitToWindow: function(){
35 | panoramaDiv.style.width = window.innerWidth;
36 | panoramaDiv.style.height = window.innerHeight;
37 | },
38 | initResizeEvent: function(){
39 | var self = this,
40 | timer = false;
41 | window.addEventListener('resize',function(e){
42 | if(!timer){
43 | timer = setTimeout(function(){
44 | console.log('done resizing');
45 | clearTimeout(timer);
46 | timer = false;
47 | if(panoramaManager.isInit()){
48 | self.panoramaFitToWindow();
49 | panoramaManager.resize();
50 | }
51 | },800);
52 | }
53 | },false);
54 | }
55 | };
56 |
57 | screenManager = {
58 | init: function(panoramaManager){
59 |
60 | var panoramaDiv = panoramaManager.getPanoramaHtmlObject();
61 |
62 | var panoramaFitToWindow = function(){
63 | panoramaDiv.style.width = window.innerWidth+"px";
64 | panoramaDiv.style.height = window.innerHeight+"px";
65 | };
66 | var initResizeEvent = function(){
67 | var timer = false;
68 | window.addEventListener('resize',function(e){
69 | if(!timer){
70 | timer = setTimeout(function(){
71 | console.log('done resizing');
72 | clearTimeout(timer);
73 | timer = false;
74 | if(panoramaManager.isInit()){
75 | panoramaFitToWindow();
76 | panoramaManager.resize();
77 | }
78 | },800);
79 | }
80 | },false);
81 | };
82 |
83 | panoramaFitToWindow();
84 | initResizeEvent();
85 | }
86 | };
87 |
88 | return screenManager;
89 |
90 | });
--------------------------------------------------------------------------------
/src/demo.custompanorama.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PanoramaSensorsViewer / Custom panorama
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/js/topheman-panorama/utils/blockedPopup.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Copyright (C) 2013-2014 Christophe Rosset - https://github.com/topheman
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | *
23 | */
24 |
25 | /**
26 | * We all have popup blockers like AdBlock, so in your application, when you need to provide a popup,
27 | * you can't be sure wether it has opened or been blocked.
28 | * This module provides you two events :
29 | * - isBlocked : so that you could notify your user to allow the popups for your site (for example)
30 | * - onUnblock : when your popup has opened correctly (maybe so that you remove a previous notification - or do nothing ...)
31 | *
32 | */
33 |
34 | (function (blockedPopup){
35 |
36 | if (typeof define === 'function' && define.amd) {
37 | // AMD. Register as an anonymous module.
38 | define(blockedPopup);
39 | } else {
40 | // Browser globals
41 | window.blockedPopup = blockedPopup();
42 | }
43 |
44 | })(function(){
45 |
46 | var blockedPopup;
47 |
48 | blockedPopup = {
49 |
50 | /**
51 | *
52 | * @param {window} popup
53 | * @param {Function} isBlockedCallback
54 | * @param {Function} isUnBlockedCallback
55 | */
56 | isBlocked : function(popup, isBlockedCallback, isUnBlockedCallback){
57 | console.log('blockedPopup.isBlocked()');
58 | if (!popup){
59 | console.log('no popup');
60 | isBlockedCallback();
61 | }
62 | else {
63 | popup.onload = function() {
64 | setTimeout(function() {
65 | if (popup.screenX === 0){
66 | if(typeof isBlockedCallback === "function"){
67 | isBlockedCallback();
68 | }
69 | }
70 | else{
71 | if(typeof isUnBlockedCallback === "function"){
72 | isUnBlockedCallback();
73 | }
74 | }
75 | }, 0);
76 | };
77 | }
78 | },
79 |
80 | /**
81 | *
82 | * @param {window} popup
83 | * @param {Function} callback
84 | */
85 | onUnblock: function(popup, callback){
86 | var check = (function(popup,callback){
87 | return function(){
88 | // console.log('check',popup);
89 | if(popup){
90 | if(popup.screenX === 0){
91 | setTimeout(check,10);
92 | }
93 | else{
94 | if(typeof callback === "function"){
95 | callback();
96 | }
97 | }
98 | }
99 | else{
100 | setTimeout(check,10);
101 | }
102 | };
103 | })(popup,callback);
104 | check();
105 | }
106 |
107 | };
108 |
109 | return blockedPopup;
110 |
111 | });
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | #PanoramaSensorsViewer - API doc
2 |
3 | I made a bunch of examples, this may be the best way to understand how PanoramaSensorsViewer works. [You can test them online here](http://labs.topheman.com/PanoramaSensorsViewer/). I also invite you to checkout the Google Maps API doc.
4 |
5 | Still, here is the API doc.
6 |
7 | ##Require PanoramaManager
8 |
9 | The very first thing you need to do is require the module `topheman-panorama/PanoramaManager`. Here is an example of how to do that (you may have a different way since RequireJS has different ways to load).
10 |
11 | ```html
12 |
13 |
14 |
15 |
22 |
23 | ```
24 |
25 | ##PanoramaManager
26 |
27 | ###Summary
28 |
29 | Creates a JavaScript `PanoramaManager` instance linked to the `DOMObject` **passed in parameter**. At this moment, the instance does pretty much nothing until it is initialized by calling the method `init`.
30 |
31 | ###Constructor
32 |
33 | ```js
34 |
35 | var panoramaManager = new PanoramaManager(document.getElementById('pano'));
36 |
37 | ```
38 |
39 | ###Methods
40 |
41 | ####.init(position,options)
42 |
43 | You can call the `.init` method as many times as you want, however, you need to keep in mind some events and callback options will only happen at the first time you call it, such as :
44 |
45 | * retrieving the Google Maps API
46 | * connecting to the gyroscope+accelerometer (ie `addEventListener("deviceorientation", ...)` )
47 |
48 | This will init your panorama, filling the DOMObject you passed previously.
49 |
50 | #####parameters
51 |
52 | * `position` : 2 possibilities (see bellow when to use either one of them)
53 | * `Object` with latitude and longitude. Example : `{lat:37.869260, lon:-122.254811}`
54 | * `null`
55 | * `options` : this parameter is optional
56 | * `success` : `[Function]` `function(data){ /* scope is the panorama object on which you can call any Google Maps API */ }`
57 | * `error` : `[Function]` `function(error){ /* … */ }`
58 | * `pano` : `[String]` custom panorama id on which you wish to start your panorama (you'll need to provide a panoProvider callback)
59 | * `panoProvider` : `[Function]` custom panorama provider method for full custom panoramas (not linked with any Google Street View panoramas). You'll need to provide a panorama id as options.pano to specify which panorama you want to start with. [See example](https://github.com/topheman/PanoramaSensorsViewer/blob/master/src/demo.custompanorama.html).
60 | * `enableRemotetilt` : `[Boolean]` Will load the deviceorientation emulator if set at true (and if no sensors on your device)
61 | * `remotetiltIsBlocked` : `[Function]` Callback called if the emulator popup has been blocked by a popup blocker such as adBlock
62 | * `remotetiltIsUnblocked` : `[Function]` Callback called if the emulator popup has not been blocked by a popup blocker
63 | * `disableTouchmove` : `[Boolean|Function]` Disables the touchmove. If a function is given, this will be executed on touchmove.
64 | * `firefoxSensorsNotReady` : `[Function]` On some android devices, firefox mobile takes some time to add the event listener on the deviceorientation, this callback lets you spot when it happens to tell your user to reload for example
65 | * `googleApiKey` : `[String]` if you have a google API key and you let PanoramaSensorsViewer load the Google Maps API, you can specify your key here. (note that the API won't be loaded again if you already loaded it yourself).
66 |
67 | #####parameters advanced infos
68 |
69 | * in the `success` callback, you have access to all the Google Maps API that you can apply to your Google panorama which is the current scope `this`.
70 | * You can't use `pano` without `panoProvider` and only for panoramas which aren't connected to Google Street View (you'll be giving a `null` position in this case) ([see example](https://github.com/topheman/PanoramaSensorsViewer/blob/master/src/demo.custompanorama.html)).
71 | * If you want to connect your custom panorama, use the Google Maps API inside the `success` callback : `.registerPanoProvider()` like in [this example](https://github.com/topheman/PanoramaSensorsViewer/blob/master/src/demo.custompanorama.tiles.html).
72 |
73 | ####.isInit()
74 |
75 | returns `true` if the panorama is initialized
76 |
77 | ####.resize()
78 |
79 | triggers the resize event on the panorama (you would like to do that if you resize the DOMObject containing the panorama)
--------------------------------------------------------------------------------
/src/js/topheman-panorama/utils/sensorsChecker.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Copyright (C) 2013-2014 Christophe Rosset - https://github.com/topheman
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | *
23 | */
24 |
25 | /**
26 | * Mostly all recent browsers expose an api for deviceorientation and devicemotion events.
27 | * That doesn't mean the device you're on has sensors (accelerometer+gyroscope) to feed them.
28 | * So to check if the device has sensors, you can't rely on simple feature detection like
29 | * "ondeviceorientation" in window or "ondevicemotion" in window
30 | *
31 | * This module will let you check if there is really an accelerometer+gyroscope to rely on.
32 | */
33 |
34 | (function (sensorsChecker){
35 |
36 | if (typeof define === 'function' && define.amd) {
37 | // AMD. Register as an anonymous module.
38 | define(sensorsChecker);
39 | } else {
40 | // Browser globals
41 | window.sensorsChecker = sensorsChecker();
42 | }
43 |
44 | })(function(){
45 |
46 | var sensorsChecker,
47 | eventsMap = {
48 | "devicemotion" : {
49 | "event" : "DeviceMotionEvent",
50 | "handler" : function(e){
51 | if(e.acceleration && e.acceleration.x !== null && e.acceleration.y !== null && eventsMap.devicemotion.support === false){
52 | eventsMap.devicemotion.support = true;
53 | }
54 | },
55 | support : false
56 | },
57 | "deviceorientation" : {
58 | "event" : "DeviceOrientationEvent",
59 | "handler" : function(e){
60 | if(e.beta !== null && e.gamma !== null && eventsMap.deviceorientation.support === false){
61 | eventsMap.deviceorientation.support = true;
62 | }
63 | },
64 | support : false
65 | }
66 | },
67 | DEFAULT_DELAY = 500;
68 |
69 | sensorsChecker = {
70 |
71 | /**
72 | *
73 | * @param {String} event "devicemotion"|"deviceorientation"
74 | * @param {Function} success
75 | * @param {Function} failure
76 | * @params {options} options @optional
77 | * @config {Number} delay
78 | * @config {RegExp} userAgentCheck
79 | */
80 | check: function(event, success, failure, options){
81 |
82 | options = options ? options : {};
83 | options.delay = options.delay ? options.delay : DEFAULT_DELAY;
84 |
85 | if(!eventsMap[event]){
86 | throw new Error("Only devicemotion or deviceorientation events supported");
87 | }
88 | if(typeof success !== "function"){
89 | throw new Error("success callback missing");
90 | }
91 | if(typeof failure !== "function"){
92 | throw new Error("success callback missing");
93 | }
94 |
95 | if(window[eventsMap[event].event]){
96 | if(options && options.userAgentCheck && options.userAgentCheck instanceof RegExp && options.userAgentCheck.test(window.navigator.userAgent)){
97 | success();
98 | }
99 | else{
100 | window.addEventListener(event, eventsMap[event].handler, false);
101 | setTimeout(function(){
102 | window.removeEventListener(event, eventsMap[event].handler);
103 | if(eventsMap[event].support === true){
104 | success();
105 | }
106 | else{
107 | failure();
108 | }
109 | },options.delay);
110 | }
111 | }
112 | else{
113 | failure();
114 | }
115 |
116 | },
117 |
118 | checkDevicemotion: function(success, failure, options){
119 | this.check('devicemotion',success, failure, options);
120 | },
121 |
122 | checkDeviceorientation: function(success, failure, options){
123 | this.check('deviceorientation',success, failure, options);
124 | }
125 |
126 | };
127 |
128 | return sensorsChecker;
129 |
130 | });
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013-2014 Christophe Rosset - https://github.com/topheman/PanoramaSensorsViewer
3 | *
4 | * Released under MIT License :
5 | * https://github.com/topheman/PanoramaSensorsViewer/blob/master/LICENSE
6 | */
7 |
8 | /**
9 | * Tou know grunt ? It's great ! Two things :
10 | * - npm install (will install the node packages needed)
11 | * - npm install grunt-cli -g (if you don't have it already)
12 | * - grunt server (will start you a local server so that you can test)
13 | */
14 | module.exports = function(grunt) {
15 |
16 | // Load Grunt tasks declared in the package.json file
17 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
18 |
19 | var PanoramaSensorsViewerVersion = require('./package.json').version,
20 | gruntSpecificOptions;
21 |
22 | try{
23 | gruntSpecificOptions = grunt.file.readJSON('./grunt.options.json');//specific options meant to be split from the hard code of the GruntFile.js
24 | }
25 | catch(e){
26 | grunt.log.error("No grunt.options.json file find (you will need it for ftp-deploy)");
27 | }
28 |
29 | // Configure Grunt
30 | grunt.initConfig({
31 |
32 | // grunt-contrib-connect will serve the files of the project
33 | // on specified port and hostname
34 | connect: {
35 | debug: {
36 | options: {
37 | port: 9002,
38 | hostname: "0.0.0.0",
39 | keepalive: true,
40 | base: "src"
41 | }
42 | },
43 | release: {
44 | options: {
45 | port: 9002,
46 | hostname: "0.0.0.0",
47 | keepalive: true,
48 | base: "release"
49 | }
50 | }
51 | },
52 |
53 | // grunt-open will open your browser at the project's URL
54 | open: {
55 | debug: {
56 | path: 'http://localhost:<%= connect.debug.options.port%>/'
57 | },
58 | release: {
59 | path: 'http://localhost:<%= connect.release.options.port%>/'
60 | }
61 | },
62 |
63 | //this part is for building
64 | clean: {
65 | //clean all before any build
66 | all: {
67 | src : ['release']
68 | },
69 | "after-build" : {
70 | src : [
71 | 'release/googleAnalyticsScriptTag.html'
72 | ]
73 | }
74 | },
75 |
76 | copy:{
77 | release:{
78 | files:[
79 | {expand: true, cwd: 'src/', src: ['**'], dest: 'release/'}
80 | ]
81 | }
82 | },
83 |
84 | "git-rev-parse": {
85 | panoramaSensorsViewerRevision: {
86 | options: {
87 | prop: 'PanoramaSensorsViewerRevision',
88 | number: 8
89 | }
90 | }
91 | },
92 |
93 | //https://github.com/jsoverson/preprocess + https://npmjs.org/package/grunt-preprocess
94 | preprocess: {
95 | options:{
96 | context:{
97 | "PanoramaSensorsViewerVersion" : PanoramaSensorsViewerVersion,
98 | "PanoramaSensorsViewerRevision" : '<%= grunt.config.get("PanoramaSensorsViewerRevision") %>',
99 | "PanoramaSensorsViewerVersionAndRevision" : 'v'+PanoramaSensorsViewerVersion+' - r.<%= grunt.config.get("PanoramaSensorsViewerRevision") %>'
100 | }
101 | },
102 | "process-html" : {
103 | files : {
104 | 'release/index.html' : 'src/index.html',
105 | 'release/demo.basic.html' : 'src/demo.basic.html',
106 | 'release/demo.controls.html' : 'src/demo.controls.html',
107 | 'release/demo.custompanorama.html' : 'src/demo.custompanorama.html',
108 | 'release/demo.custompanorama.tiles.html' : 'src/demo.custompanorama.tiles.html',
109 | 'release/demo.events.html' : 'src/demo.events.html',
110 | 'release/demo.deviceorientationhelper.html' : 'src/demo.deviceorientationhelper.html',
111 | 'release/test.bug.android.firefox.html' : 'src/test.bug.android.firefox.html',
112 | }
113 | }
114 | }
115 |
116 | });
117 |
118 | if(gruntSpecificOptions){
119 | grunt.config("ftp-deploy",{
120 | release: {
121 | auth: {
122 | host: gruntSpecificOptions["ftp-deploy"].release.host,
123 | port: gruntSpecificOptions["ftp-deploy"].release.port,
124 | authKey: 'key1'
125 | },
126 | src: 'release',
127 | dest: gruntSpecificOptions["ftp-deploy"].release.dest,
128 | exclusions: ['release/build.txt']
129 | }
130 | });
131 | grunt.registerTask('deploy', ['ftp-deploy:release']);
132 | }
133 |
134 | grunt.registerTask('server', ['open:debug', 'connect:debug']);
135 | grunt.registerTask('default', ['open:debug', 'connect:debug']);
136 |
137 | grunt.registerTask('getGitRevisionNumbers',['git-rev-parse']);
138 |
139 | grunt.registerTask('build', ['clean:all','copy:release','getGitRevisionNumbers','preprocess:process-html','clean:after-build']);
140 | grunt.registerTask('server-release', ['open:release', 'connect:release']);
141 |
142 | };
--------------------------------------------------------------------------------
/src/js/topheman-panorama/utils/deviceorientationHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Copyright (C) 2013-2014 Christophe Rosset - https://github.com/topheman
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | *
23 | */
24 |
25 | (function (deviceorientationHelper){
26 |
27 | if (typeof define === 'function' && define.amd) {
28 | // AMD. Register as an anonymous module.
29 | define(deviceorientationHelper);
30 | } else {
31 | // Browser globals
32 | window.deviceorientationHelper = deviceorientationHelper();
33 | }
34 |
35 | })(function(){
36 |
37 | var deviceorientationHelper,
38 | _getDeviceorientationHandler,
39 | _eventToInfos,
40 | _mozScreenOrientationToWindowOrientation,
41 | deviceorientationHandler;
42 |
43 | _mozScreenOrientationToWindowOrientation = function(orientation){
44 | var result;//@todo cache orientation
45 | switch(orientation){
46 | case "portrait-primary" :
47 | result = 0;
48 | break;
49 | case "portrait-secondary" :
50 | result = 180;
51 | break;
52 | case "landscape-primary" :
53 | result = 90;
54 | break;
55 | case "landscape-secondary" :
56 | result = -90;
57 | break;
58 | default :
59 | result = 0;
60 | break;
61 | };
62 | return result;
63 | };
64 |
65 | _eventToInfos = function(e,infos){
66 | switch(infos.orientation){
67 | case 0 :
68 | infos.tiltLR = e.gamma;
69 | infos.tiltFB = e.beta;
70 | infos.direction = e.alpha;
71 | break;
72 | case 90 :
73 | infos.tiltLR = e.beta;
74 | infos.tiltFB = (-e.gamma >= 0 ? -e.gamma : (360 - e.gamma));
75 | infos.direction = (e.alpha-90)%360;
76 | break;
77 | case -90 :
78 | infos.tiltLR = -e.beta;
79 | infos.tiltFB = e.gamma;
80 | infos.direction = (e.alpha+90)%360;
81 | break;
82 | case 180 :
83 | infos.tiltLR = -e.gamma;
84 | infos.tiltFB = -e.beta;
85 | infos.direction = e.alpha;
86 | break;
87 | };
88 | };
89 |
90 | _eventToInfosFirefox = function(e,infos){
91 | switch(infos.orientation){
92 | case 0 :
93 | infos.tiltLR = -e.gamma;
94 | infos.tiltFB = -e.beta;
95 | infos.direction = -e.alpha;
96 | break;
97 | case 90 :
98 | infos.tiltLR = -e.beta;
99 | infos.tiltFB = e.gamma;
100 | infos.direction = -(e.alpha+90)%360;
101 | break;
102 | case -90 :
103 | infos.tiltLR = e.beta;
104 | infos.tiltFB = -e.gamma;
105 | infos.direction = -(e.alpha+90)%360;
106 | break;
107 | case 180 :
108 | infos.tiltLR = e.gamma;
109 | infos.tiltFB = e.beta;
110 | infos.direction = -e.alpha;
111 | break;
112 | };
113 | };
114 |
115 | _getDeviceorientationHandler = function(callback){
116 | //for android firefox mobile
117 | if(/Android.*(Mobile|Tablet).*Firefox/i.test(window.navigator.userAgent)){
118 | return function(e,infos){
119 | infos = {
120 | "orientation" : _mozScreenOrientationToWindowOrientation(window.screen.mozOrientation),
121 | "heading" : null,
122 | "absolute" : e.absolute
123 | };
124 | _eventToInfosFirefox(e,infos);
125 | callback.call({},e,infos);
126 | };
127 | }
128 | //for others
129 | else{
130 | return function(e,infos){
131 | infos = {
132 | "orientation" : window.orientation || 0,
133 | "heading" : e.compassHeading || e.webkitCompassHeading,
134 | "absolute" : e.absolute
135 | };
136 | _eventToInfos(e,infos);
137 | callback.call({},e,infos);
138 | };
139 | }
140 | };
141 |
142 | deviceorientationHelper = {
143 |
144 | /**
145 | * @description adds a callback like you would do on deviceorientation event
146 | * but gives you a second argument so that you won't have to bother with the orientation of the device
147 | * @param {Function} callback function(e,infos){ ... }
148 | * @param {DeviceOrientationEvent} e
149 | * @param {Object} infos processed deviceorientation infos (according to orientation of the device)
150 | */
151 | init: function(callback){
152 |
153 | if(typeof deviceorientationHandler !== "undefined"){
154 | throw new Error("deviceorientationHelper already initiated, please .destroy() it before reinitiate it");
155 | }
156 |
157 | //init deviceorientationHandler
158 | deviceorientationHandler = _getDeviceorientationHandler(callback);
159 | window.addEventListener('deviceorientation',deviceorientationHandler,false);
160 |
161 | },
162 |
163 | /**
164 | * @description destroys the handlers added at init
165 | */
166 | destroy: function(){
167 |
168 | if(typeof deviceorientationHandler === "undefined"){
169 | throw new Error("deviceorientationHelper already destroy (or nether initiated), please .init() it before destroying it");
170 | }
171 |
172 | window.removeEventListener('deviceorientation',deviceorientationHandler,false);
173 |
174 | }
175 |
176 | };
177 |
178 | return deviceorientationHelper;
179 |
180 | });
--------------------------------------------------------------------------------
/src/demo.custompanorama.tiles.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PanoramaSensorsViewer / Custom panorama tiles
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PanoramaSensorsViewer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Fork me on GitHub
15 |
30 | Here is a list of demos using PanoramaSensorsViewer, so you could understand how to use the Google Maps API inside it.
31 | This will work as well on a desktop as on a mobile device (what you should do is test it on your mobile device and watch the code on your desktop).
32 | For each example, you will find :
33 |
59 |
60 | Alter the controls of your panorama via the setOptions method of Google Maps API for panorama, inside the success callback of PanoramaManager.init (zoom control and address will be hidden).
61 |
62 |
72 |
73 | In the options, setup your own panoProvider and pano (like you would with Google Maps API) and get your own custom panorama with PanoramaSensorsViewer !
74 |
75 |
PanoramaSensorsViewer ships some utils I made that can run in standalone (both non-AMD and AMD compatible). While you use PanoramaSensorsViewer, you don't have to worry about them but, it could come handy in some of your project.
104 |
blockedPopup.js
105 |
We all have popup blockers like AdBlock on our browser, so if your application ever needs popups (I know it's old) and make sure it's been authorized by your user, the little class will let you know.
Mostly all recent browsers expose an api for deviceorientation and devicemotion events. That doesn't mean the device you're on has sensors (accelerometer+gyroscope) to feed them.
109 |
So to check if the device has sensors, you can't rely on simple feature detection like "ondeviceorientation" in window or "ondevicemotion" in window
110 |
This module will let you check if there is really an accelerometer+gyroscope to rely on.