Difference between revisions of "Widget:SpaceAPI"

From Hackerspace ACKspace
Jump to: navigation, search
m (added (hidden) features parameter)
(Added multiple beacon support, added popup (temperature, address, spacefed) + colored temperature polygons)
Line 62: Line 62:
 
         SpaceAPI.prototype._intervalId    = null;
 
         SpaceAPI.prototype._intervalId    = null;
 
         SpaceAPI.prototype._node          = null;
 
         SpaceAPI.prototype._node          = null;
         SpaceAPI.prototype._beacon        = null;
+
         SpaceAPI.prototype._leaflet        = null;
 
         SpaceAPI.prototype._msgLoading    = "Loading..";
 
         SpaceAPI.prototype._msgLoading    = "Loading..";
 
         SpaceAPI.prototype._msgError      = "Error";
 
         SpaceAPI.prototype._msgError      = "Error";
Line 122: Line 122:
 
                 srcNode.addEventListener( "load", function( _evt )
 
                 srcNode.addEventListener( "load", function( _evt )
 
                 {
 
                 {
                     this._beacon = {};
+
                     this._leaflet = {};
                     this._beacon.point = L.latLng( 50.883333, 5.983333 );
+
                     this._leaflet.point = L.latLng( 50.8925,5.9713 );
                     this._beacon.map = L.map( mapNode ).setView( this._beacon.point, 11);
+
                     this._leaflet.map = L.map( mapNode ).setView( this._leaflet.point, 16);
  
 
                     L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
 
                     L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
 
                         attribution: '&copy; <a href="//openstreetmap.org/copyright">OpenStreetMap</a> contributors'
 
                         attribution: '&copy; <a href="//openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                     }).addTo( this._beacon.map );
+
                     }).addTo( this._leaflet.map );
  
 
                     // "Follow" control
 
                     // "Follow" control
                     this._beacon.follow = true;
+
                     this._leaflet.follow = null;
 
                     L.Control.Command = L.Control.extend(
 
                     L.Control.Command = L.Control.extend(
 
                     {
 
                     {
Line 142: Line 142:
 
                         {
 
                         {
 
                             var controlDiv = L.DomUtil.create( "div", "leaflet-bar" );
 
                             var controlDiv = L.DomUtil.create( "div", "leaflet-bar" );
                             var controlUI = L.DomUtil.create( "a", "leaflet-clickable" + (this._beacon.follow ? " toggle" : ""), controlDiv );
+
                             var controlUI = L.DomUtil.create( "a", "leaflet-clickable" + (this._leaflet.follow ? " toggle" : ""), controlDiv );
 
                             controlUI.innerHTML = "&#8982;";
 
                             controlUI.innerHTML = "&#8982;";
 
                             controlUI.style.fontSize = "35px";
 
                             controlUI.style.fontSize = "35px";
Line 149: Line 149:
 
                             L.DomEvent.addListener( controlUI, 'click', function( _evt )
 
                             L.DomEvent.addListener( controlUI, 'click', function( _evt )
 
                             {
 
                             {
                                 this._beacon.follow = !this._beacon.follow;
+
                                 this._leaflet.follow = !this._leaflet.follow;
                                 _evt.currentTarget.className = "leaflet-clickable" + (this._beacon.follow ? " toggle" : "");
+
                                 _evt.currentTarget.className = "leaflet-clickable" + (this._leaflet.follow ? " toggle" : "");
  
 
                                 // Update the map immediately
 
                                 // Update the map immediately
                                 if ( this._beacon.follow )
+
                                 if ( this._leaflet.follow )
                                     this._beacon.map.setView( this._beacon.point, 16, {} );
+
                                {
 +
                                    // Determine the bounding box to 'follow
 +
                                     var bounds = L.latLngBounds( this._leaflet.beacons.map( function( _beacon )
 +
                                    {
 +
                                        return _beacon.point;
 +
                                    } ) );
  
 +
                                    if ( !this._leaflet.beacons.length )
 +
                                        bounds.extend( this._leaflet.point );
 +
 +
                                    this._leaflet.map.fitBounds( bounds );
 +
                                }
 
                             }.bind( this ) );
 
                             }.bind( this ) );
 
                  
 
                  
Line 162: Line 172:
 
                     } );
 
                     } );
  
                     this._beacon.map.addControl( new L.Control.Command() );
+
                     this._leaflet.map.addControl( new L.Control.Command() );
  
 
                     // Icons
 
                     // Icons
                     this._beacon.icons = {
+
                     this._leaflet.icons = {
 
                         "HoaB" : L.icon( {
 
                         "HoaB" : L.icon( {
 
                             iconUrl: '//maps.google.com/intl/en_us/mapfiles/ms/micons/cycling.png',
 
                             iconUrl: '//maps.google.com/intl/en_us/mapfiles/ms/micons/cycling.png',
Line 175: Line 185:
 
                             shadowAnchor: [16, 26]
 
                             shadowAnchor: [16, 26]
 
                         } )
 
                         } )
 +
                    };
 +
                    this._leaflet.descriptions = {
 +
                        "HoaB" : "Hackers on a Bike"
 +
                    };
 +
 +
                    this._leaflet.marker = L.marker( this._leaflet.point ).addTo( this._leaflet.map );
 +
                    this._leaflet.beacons = [];
 +
                    this._leaflet.temperatures = {
 +
                        // outside
 +
                        "28bd7a660500002f" : L.polygon([[50.892537811238,5.9711101056338], [50.892517508729,5.9710966945888], [50.892534427487,5.9710564614536], [50.892715457818,5.9712093473674], [50.892696847256,5.9712790848018], [50.892808510518,5.9713836909534], [50.892788208127,5.9714373351337], [50.892774673195,5.9714319707157], [50.892681620427,5.971697509408], [50.892641015525,5.9716653228998], [50.892559805614,5.971893310666], [50.892569956861,5.9719120861291], [50.892559805614,5.9719496370554], [50.892377082796,5.9717940689325], [50.892399077248,5.9717189670801], [50.89233140198,5.9716545940637], [50.892350012689,5.9716009498835], [50.892326326331,5.9715794922114], [50.892414304169,5.9713032246828], [50.892458293026,5.971340775609]], {stroke:0,fillOpacity:0.8} ).addTo( this._leaflet.map ),
 +
                        // cold zone
 +
                        "28151767050000a0" : L.polygon( [[50.892458293019,5.971340775608], [50.892410920402,5.9713032246818], [50.892461676775,5.9711557031861], [50.892507357464,5.9711959363214]], {stroke:0,fillOpacity:0.8} ).addTo( this._leaflet.map )
 +
                        // hot zone
 +
                        /*"288a13670500002a" : null*/
 
                     };
 
                     };
  
                    this._beacon.marker = L.marker( this._beacon.point, { /*icon: myIcon*/ } ).addTo( this._beacon.map );
 
                    this._beacon.circle = L.circle( this._beacon.point, 80, { stroke: false } ).addTo( this._beacon.map );
 
 
                 }.bind( this ) );
 
                 }.bind( this ) );
  
Line 187: Line 209:
 
             // Update the space state immediately
 
             // Update the space state immediately
 
             setTimeout( this._fetchState.bind( this ), 1 );
 
             setTimeout( this._fetchState.bind( this ), 1 );
 +
        };
 +
        SpaceAPI.prototype._determineColor = function( _temperature )
 +
        {
 +
            var tempteratureColors = [
 +
                [ -10,  0,  0,  0], // black
 +
                [  0,  0,  0,255], // blue
 +
                [  15, 255,255,  0], // yellow
 +
                [  35, 255,  0,  0], // red
 +
                [  45, 255,255,255], // white
 +
                [5000, 255,255,255]  // white, blink
 +
            ];
 +
            var index;
 +
            var ratio;
 +
 +
            for ( var nTemp = tempteratureColors.length - 1; nTemp; nTemp-- )
 +
            {
 +
                if ( _temperature >= tempteratureColors[ nTemp ][0] )
 +
                    break;
 +
 +
                ratio = 0;
 +
                if ( index = nTemp )
 +
                    ratio = (_temperature - tempteratureColors[ nTemp - 1 ][0]) / ( tempteratureColors[ nTemp ][0] - tempteratureColors[ nTemp - 1 ][0] );
 +
            }
 +
 +
            var lo = tempteratureColors[ index ? index - 1 : 0 ];
 +
            var hi = tempteratureColors[ index ];
 +
 +
            return "rgb("+Math.round( (lo[1] * (1 - ratio) + hi[1] * ratio) ) +","+Math.round( (lo[2] * (1 - ratio) + hi[2] * ratio))+","+Math.round( (lo[3] * (1 - ratio) + hi[3] * ratio))+")";
 +
        };
 +
 +
        SpaceAPI.prototype._nlsTime = function( _time )
 +
        {
 +
            var postfix;
 +
 +
            if ( _time > 31556952 )
 +
            {
 +
                _time /= 31556952;
 +
                postfix = "Year";
 +
            }
 +
            else if ( _time > 2629746 )
 +
            {
 +
                _time /= 2629746;
 +
                postfix = "Month";
 +
            }
 +
            else if ( _time > 604800 )
 +
            {
 +
                _time /= 604800;
 +
                postfix = "Week";
 +
            }
 +
            else if ( _time > 86400 )
 +
            {
 +
                _time /= 86400;
 +
                postfix = "day";
 +
            }
 +
            else if ( _time > 3600 )
 +
            {
 +
                _time /= 3600;
 +
                postfix = "hour";
 +
            }
 +
            else if ( _time > 60 )
 +
            {
 +
                _time /= 60;
 +
                postfix = "minute";
 +
            }
 +
            else
 +
            {
 +
                postfix = "second";
 +
            }
 +
 +
            _time = Math.round( _time );
 +
            return _time + " " + postfix + (_time !== 1 ? "s" : "");
 
         };
 
         };
  
Line 262: Line 355:
 
                 this._updateState( message || this._msgUnknown, this._colorUnknown, title );
 
                 this._updateState( message || this._msgUnknown, this._colorUnknown, title );
  
             if ( this._beacon )
+
             if ( this._leaflet )
 
             {
 
             {
 +
                // Handle temperatures
 +
                if ( this.data.sensors && this.data.sensors.temperature && this.data.sensors.temperature.length )
 +
                {
 +
                    // Iterate the sensors and match a local sensor name
 +
                    this.data.sensors.temperature.forEach( function( _apiTemp )
 +
                    {
 +
                        var temp = this._leaflet.temperatures[ _apiTemp.name ];
 +
                        if ( temp )
 +
                        {
 +
                            temp.setStyle({color: this._determineColor( _apiTemp.value )});
 +
 +
                            var delta = (Date.now() - new Date( _apiTemp.ext_lastchange * 1000 )) / 1000;
 +
                            temp.bindPopup( "Description: " + _apiTemp.description + "<br/>Location: " + _apiTemp.location + "Value: " + _apiTemp.value + _apiTemp.unit + "<br/>Last update: " + this._nlsTime( delta ) + " ago" );
 +
                        }
 +
                    }, this );
 +
                }
 +
 +
                // Handle beacons
 
                 if ( this.data.sensors && this.data.sensors.beacon && this.data.sensors.beacon.length )
 
                 if ( this.data.sensors && this.data.sensors.beacon && this.data.sensors.beacon.length )
 
                 {
 
                 {
                     // Draw the beacons (for now, only draw the first one)
+
                     var bHoaB = false;
                     this._beacon.point = L.latLng( this.data.sensors.beacon[0].location.lat, this.data.sensors.beacon[0].location.lon );
+
                    var beacons = this.data.sensors.beacon.map( function( _apiBeacon )
                    this._beacon.circle.setRadius( this.data.sensors.beacon[0].location.accuracy );
+
                     {
 +
                        var beacon = {};
 +
 
 +
                        beacon.point = L.latLng( _apiBeacon.location.lat,_apiBeacon.location.lon );
 +
                        beacon.marker = L.marker( beacon.point, { icon: this._leaflet.icons[ _apiBeacon.name ] || new L.Icon.Default() } ).addTo( this._leaflet.map );
 +
                        beacon.circle = L.circle( beacon.point, _apiBeacon.location.accuracy, {stroke:0} ).addTo( this._leaflet.map );
 +
 
 +
                        var delta = (Date.now() - new Date( _apiBeacon.ext_lastchange * 1000 )) / 1000;
 +
 
 +
                        // Closure variable
 +
                        if ( ( delta < 3600 ) && ( _apiBeacon.name === "HoaB" ) )
 +
                            bHoaB = true;
 +
 
 +
                        if ( this._leaflet.icons[ _apiBeacon.name ] )
 +
                            this._leaflet.icons[ _apiBeacon.name ].options.className = delta > 60 ? "disconnected" : "";
 +
 
 +
                        var popup = beacon.marker.getPopup();
 +
                        if ( !popup )
 +
                            popup = beacon.marker.bindPopup().getPopup();
 +
 
 +
                        popup.setContent( ( this._leaflet.descriptions[ _apiBeacon.name ] || _apiBeacon.name ) + "<br/>Last update: " + this._nlsTime( delta ) + " ago" );
 +
 
 +
                        return beacon;
 +
                    }, this );
  
                     var delta = (Date.now() - new Date( this.data.sensors.beacon[0].ext_lastchange * 1000 )) / 1000;
+
                     // TODO: clean up old beacons!!
                     var postfix = "s";
+
                     this._leaflet.beacons.forEach( function( _beacon )
                    if ( this._beacon.icons[ this.data.sensors.beacon[0].name ] )
 
                        this._beacon.icons[ this.data.sensors.beacon[0].name ].options.className = delta > 60 ? "disconnected" : "";
 
                    if ( delta > 31556952 )
 
 
                     {
 
                     {
                         delta /= 31556952;
+
                         // Destroy popup
                         postfix = "Y";
+
                        _beacon.marker.unbindPopup( );
                     }
+
 
                     else if ( delta > 2629746 )
+
                        // Remove marker and circle
 +
                        this._leaflet.map.removeLayer( _beacon.marker );
 +
                         this._leaflet.map.removeLayer( _beacon.circle );
 +
                     }, this );
 +
                    /*
 +
                     for ( var b = 0; b < Math.min( this._leaflet.beacons.length, beacons, length ); b++ )
 
                     {
 
                     {
                         delta /= 2629746;
+
                         // Update position, icon, radius, tooltip
                         postfix = "M";
+
                         this._leaflet.beacons[ b ]
 
                     }
 
                     }
                     else if ( delta > 604800 )
+
                     */
 +
                    this._leaflet.beacons = beacons;
 +
 
 +
                    // Only follow beacons automatically initially if there is a HoaB among it
 +
                    if ( bHoaB && this._leaflet.follow === null )
 
                     {
 
                     {
                         delta /= 604800;
+
                         this._leaflet.follow = true;
                         postfix = "W";
+
                         document.querySelector( "div.leaflet-bar > a.leaflet-clickable" ).className = "leaflet-clickable toggle";
 
                     }
 
                     }
                    else if ( delta > 86400 )
+
                }
                    {
+
 
                        delta /= 86400;
+
 
                        postfix = "d";
+
                // TODO: Update if coordinate is incorrect
                    }
+
                if ( this._leaflet.follow === null )
                    else if ( delta > 3600 )
+
                {
                    {
+
                    this._leaflet.follow = false;
                        delta /= 3600;
+
 
                        postfix = "h";
+
                     // Update location
                     }
+
                     this._leaflet.point = L.latLng( this.data.location.lat, this.data.location.lon );
                     else if ( delta > 60 )
+
                     this._leaflet.marker.setLatLng( this._leaflet.point );
                     {
+
 
                        delta /= 60;
+
                     // Set popup data and open it
                        postfix = "m";
+
                     var popup = this._leaflet.marker.getPopup();
                     }
 
                    else
 
                    {
 
                        postfix = "s";
 
                    }
 
                     var popup = this._beacon.marker.getPopup();
 
 
                     if ( !popup )
 
                     if ( !popup )
                         popup = this._beacon.marker.bindPopup( "." ).getPopup();
+
                         popup = this._leaflet.marker.bindPopup().getPopup();
  
                     popup.setContent( "Hackers on a Bike<br/>Last update: " + Math.round( delta ) + postfix + " ago" );
+
                     var info = "<img src='" + this.data.logo + "'><br/>";
 +
                    var l = this.data.location, s = this.data.spacefed;
 +
                    info += l.address;
  
                     this._beacon.marker.setIcon( this._beacon.icons[ this.data.sensors.beacon[0].name ] || new L.Icon.Default() );
+
                     if ( l.ext_floor )
 +
                        info += ", floor " + l.ext_floor;
 +
                    if ( l.ext_room )
 +
                        info += ", room " + l.ext_room;
 +
                    info += "<br/>" + (s.spacenet ? "&#x2714;" : "&#x274C;") + ' <a target="blank" href="Spacenet">spacenet</a>';
 +
                    info += "<br/>" + (s.ext_spacenet5g ? "&#x2714;" : "&#x274C;") + " spacenet (5GHz)";
 +
                    info += "<br/>" + (s.spacesaml ? "&#x2714;" : "&#x274C;") + " spacesaml";
 +
                    info += "<br/>" + (s.ext_spaceconnect ? "&#x2714;" : "&#x274C;") + " spaceconnect";
 +
                    info += "<br/>" + (s.spacephone ? "&#x2714;" : "&#x274C;") + ' <a target="blank" href="Spacephone">spacephone</a>';
 +
                    if ( s.ext_spacephone_extension )
 +
                        info += ": +" + s.ext_spacephone_extension;
 +
 
 +
                    popup.setContent( info );
 +
 
 +
                    this._leaflet.marker.openPopup();
 +
                    //popup.update();
 
                 }
 
                 }
                 else
+
 
 +
                 if ( this._leaflet.follow )
 
                 {
 
                 {
                     // Assume mandatory location of the space with a 8m radius
+
                     // Determine the bounding box to 'follow
                     this._beacon.point = L.latLng( this.data.location.lat, this.data.location.lon );
+
                     var bounds = L.latLngBounds( this._leaflet.beacons.map( function( _beacon )
                     this._beacon.circle.setRadius( 8 );
+
                     {
                     this._beacon.marker.setIcon( new L.Icon.Default() );
+
                        return _beacon.point;
                     this._beacon.marker.unbindPopup( );
+
                    } ) );
 +
 
 +
                     // If we don't have Hackers on a Bike, include the home location together with the other beacons
 +
                    if ( !bHoaB )
 +
                        bounds.extend( this._leaflet.point );
 +
 
 +
                     this._leaflet.map.fitBounds( bounds );
 
                 }
 
                 }
 
                this._beacon.marker.setLatLng( this._beacon.point );
 
                this._beacon.circle.setLatLng( this._beacon.point );
 
                if ( this._beacon.follow )
 
                    this._beacon.map.setView( this._beacon.point, 16, {} );
 
 
             }
 
             }
 
         };
 
         };
Line 338: Line 491:
 
         }
 
         }
 
     }
 
     }
 
+
    var state;
     var state = new SpaceAPI( "<!--{$width|escape:html|default:auto}-->", "<!--{$height|escape:html|default:auto}-->", "<!--{$float|escape:html|default:none}-->", "<!--{$padding|escape:html|default:8px}-->", "<!--{$url|escape:urlpathinfo}-->", <!--{$interval|validate:int|default:0}-->, "<!--{$features|escape:'quotes'}-->" );
+
    //state = new SpaceAPI( "auto", "auto", "none", "8px-->", "//ackspace.nl/spaceAPI/", 15, "beacon" );
 +
     state = new SpaceAPI( "<!--{$width|escape:html|default:auto}-->", "<!--{$height|escape:html|default:auto}-->", "<!--{$float|escape:html|default:none}-->", "<!--{$padding|escape:html|default:8px}-->", "<!--{$url|escape:urlpathinfo}-->", <!--{$interval|validate:int|default:0}-->, "<!--{$features|escape:'quotes'}-->" );
 
     state.start();
 
     state.start();
  

Revision as of 15:54, 15 March 2017

This widget allows you to display the Space API data (provided as JSON)

Created by Xopr

Using this widget

To insert this widget, use the following code:

{{#widget:SpaceAPI
|url=/spaceAPI/
|width=260px
|height=20px
|padding=8px
|interval=20
|float=right
|features=
}}

This will give the following result:

Notes

  • url is mandatory, the rest is optional (leave out interval to make the data static).
    it also must be written without protocol since colon (:) is not allowed, and may be relative, for example: //ackspace.nl/spaceAPI/ or /spaceAPI/
  • You must provide a unit for the sizes (i.e. px, %, etc.)

Copy to your site

To use this widget on your site, just install MediaWiki Widgets extension and copy full source code of this page to your wiki as Widget:SpaceAPI article.