Changeset f74371 in indico


Ignore:
Timestamp:
11/08/10 15:47:59 (3 years ago)
Author:
Jose Benito <jose.benito.gonzalez@…>
Branches:
master, burotel, hello-world-walkthrough, ipv6, v0.98-series, v0.98.2, v0.98.3, v0.98b1, v0.98b2, v0.99, 051b2622c51afb171a1dedb46a0df4fbb0cbd02e, 4c7d4152dff271ba5df5a8606605969cab454080
Children:
8968bb
Parents:
6b1cbe
git-author:
Nikolche Mihajlovski <nmihajlo@…> (08/17/10 19:58:33)
git-committer:
Jose Benito <jose.benito.gonzalez@…> (11/08/10 15:47:59)
Message:

[FTR] Powerful filtering mechanism on the map

Location:
indico
Files:
2 added
17 edited

Legend:

Unmodified
Added
Removed
  • indico/MaKaC/fossils/room.py

    r975bdf rf74371  
    4747        """ ID of the responsible person for the room """ 
    4848 
     49    def getTipPhotoURL(self): 
     50        """ URL of the tip photo of the room """ 
     51 
    4952    def isActive(self): 
    5053        """ Is the room active? """ 
     
    6164    def getMarkerDescription(self): 
    6265        """ Room description for the map marker """ 
     66 
     67    def needsAVCSetup(self): 
     68        """ Setup for for audio and video conference """ 
     69 
     70    def getAvailableVC(self): 
     71        """ Available equipment for audio and video conference """ 
  • indico/MaKaC/plugins/RoomBooking/default/room.py

    r975bdf rf74371  
    413413        """ Room URL """ 
    414414        return str(urlHandlers.UHRoomBookingRoomDetails.getURL(target=self)) 
    415         #return 'http://indico.cern.ch/roomBooking.py/roomDetails?roomLocation=CERN&roomID=3' 
    416415 
    417416    def getMarkerDescription(self): 
    418417        """ Room description for the map marker """ 
    419         return _("Capacity") + ": %s " % self.capacity + _("people") 
     418        infos = [] 
     419        if self.capacity: 
     420            infos.append("%s %s" % (self.capacity , _("people"))) 
     421        if self.isReservable: 
     422            infos.append(_("public")) 
     423        else: 
     424            infos.append(_("private")) 
     425        if self.resvsNeedConfirmation: 
     426            infos.append(_("needs confirmation")) 
     427        else: 
     428            infos.append(_("auto-confirmation")) 
     429        if self.needsAVCSetup: 
     430            infos.append(_("video conference")) 
     431        return ", ".join(infos) 
     432 
     433    def getTipPhotoURL(self): 
     434        """ URL of the tip photo of the room """ 
     435        from MaKaC.webinterface.urlHandlers import UHRoomPhoto 
     436        return str(UHRoomPhoto.getURL(self._doGetPhotoId(force=True))) 
    420437 
    421438    def getIsAutoConfirm(self): 
  • indico/MaKaC/rb_location.py

    r56899a rf74371  
    4747        root[_ROOM_BOOKING_LOCATION_LIST] = [ ] 
    4848 
     49def mapper(src, dest, properties): 
     50    if isinstance(src, dict) and not isinstance(dest, dict): 
     51        # map from dictionary keys to object attributes 
     52        for prop in properties: 
     53            setattr(dest, prop, src.get(prop, None)) 
     54    elif not isinstance(src, dict) and isinstance(dest, dict): 
     55        # map from object attributes to dictionary keys 
     56        for prop in properties: 
     57            dest[prop] = getattr(src, prop, None) 
     58    return dest 
     59 
     60class MapAspect(Persistent, object): 
     61    """ 
     62    Map aspect is a structure of data related to a view of a Google Map of rooms. 
     63    The rooms that are specific to a certain location can be viewed from several aspects. 
     64    A map aspect can be percieved as a combination of map coordinates and zoom level. 
     65    """ 
     66 
     67    def __str__(self): 
     68        s = _(""" _("Id"): """) + self.id + "\n" 
     69        s += _(""" _("Name"): """) + self.name + "\n" 
     70        return s 
     71 
     72    def __cmp__(self, second): 
     73        return cmp(self.id, second.id) 
     74 
     75    id = None                    # str - aspects's ID 
     76    name = None                  # str - aspects's name 
     77    defaultOnStartup = False     # bool - is this aspect the default one on start-up 
     78    centerLatitude = None        # str - the latitude of the aspects's perspective center 
     79    centerLongitude = None       # str - the longitude of the aspects's perspective center 
     80    zoomLevel = None             # str - the zoom level of the aspects's perspective 
     81    topLeftLatitude = None       # str - the latitude of the aspects's left corner 
     82    topLeftLongitude = None      # str - the longitude of the aspects's left corner 
     83    bottomRightLatitude = None   # str - the latitude of the aspects's right corner 
     84    bottomRightLongitude = None  # str - the longitude of the aspects's right corner 
     85 
     86    __ATTRIBUTES = ['id', 'name', 'defaultOnStartup', 'centerLatitude', 'centerLongitude', 'zoomLevel', 
     87                    'topLeftLatitude', 'topLeftLongitude', 'bottomRightLatitude', 'bottomRightLongitude'] 
     88 
     89    def toDictionary(self): 
     90        return mapper(self, {}, MapAspect.__ATTRIBUTES) 
     91 
     92    def updateFromDictionary(self, aspectDict): 
     93        return mapper(aspectDict, self, MapAspect.__ATTRIBUTES) 
    4994 
    5095class Location( Persistent, object ): 
     
    84129        self.factory = factory 
    85130        self._avcSupportEmails = [] 
     131        self._aspects = {} 
    86132 
    87133    def __str__( self ): 
     
    181227        return factories 
    182228 
     229    # Aspects management ==================================================== 
     230 
     231    def addAspect(self, aspect): 
     232        self.aspects[aspect.id] = aspect 
     233        self._p_changed = 1 
     234 
     235    def removeAspect(self, aspectId): 
     236        del self.aspects[aspectId] 
     237        self._p_changed = 1 
     238 
     239    def getAspect(self, aspectId): 
     240        return self.aspects[aspectId] 
     241 
     242    def getAspects(self): 
     243        return sorted(self.aspects.values()) 
     244 
     245    def _getAspects(self): 
     246        if getattr(self, '_aspects', None) is None: 
     247            self._aspects = {} 
     248        return self._aspects 
     249 
     250    aspects = property(_getAspects) 
     251 
    183252    # Properties ============================================================ 
    184253 
     
    214283            emailList.remove( email ) 
    215284        self._avcSupportEmails = emailList 
    216  
    217285 
    218286""" 
  • indico/MaKaC/search/cache.py

    r30b4d4 rf74371  
     1import datetime 
     2import os 
     3import time 
     4 
    15from MaKaC.common.cache import MultiLevelCache, MultiLevelCacheEntry 
    26 
     
    4953        return False 
    5054 
     55 
     56class MapOfRoomsCacheEntry(MultiLevelCacheEntry): 
     57    def __init__(self, query): 
     58        MultiLevelCacheEntry.__init__(self) 
     59        self._query = query 
     60 
     61    def getQuery(self): 
     62        return self._query 
     63 
     64    @classmethod 
     65    def create(cls, content, query): 
     66        entry = cls(query) 
     67        entry.setContent(content) 
     68        return entry 
     69 
     70class MapOfRoomsCache(MultiLevelCache): 
     71    """ 
     72    Cache that stores the appropriate record numbers for a specific search query, 
     73    in order to allow users to browse map of rooms 
     74    """ 
     75 
     76    _entryFactory = MapOfRoomsCacheEntry 
     77 
     78    def __init__(self): 
     79        super(MapOfRoomsCache, self).__init__('mapOfRooms') 
     80        self._ttl = 5 
     81 
     82    def _generateFileName(self, entry, version): 
     83        return '%s_%s' % (entry.getQuery(), version) 
     84 
     85    def _generatePath(self, entry): 
     86        return ['maps'] 
     87 
     88    def isDirty(self, path, object): 
     89        creationTime = datetime.datetime(*time.localtime(os.path.getmtime(path))[:6]) 
     90        return datetime.datetime.now() - creationTime > datetime.timedelta(minutes=self._ttl) 
  • indico/MaKaC/services/implementation/roomBooking.py

    r310d46 rf74371  
    215215 
    216216        return _( "Warning: holidays chosen" ) 
     217 
     218class RoomBookingMapBase( ServiceBase ): 
     219    def _param(self, parameterName): 
     220        try: 
     221            return self._params[parameterName] 
     222        except: 
     223            from MaKaC.services.interface.rpc.common import ServiceError 
     224            raise ServiceError("ERR-RB0", "Invalid %s." % parameterName) 
     225 
     226class RoomBookingMapCreateAspect(RoomBookingMapBase): 
     227 
     228    def _checkParams( self ): 
     229        self._location = Location.parse(self._param("location")) 
     230        self._aspect = self._param("aspect") 
     231 
     232    def _getAnswer( self ): 
     233        aspect = MapAspect() 
     234        aspect.updateFromDictionary(self._aspect) 
     235        self._location.addAspect(aspect) 
     236        return {} 
     237 
     238class RoomBookingMapUpdateAspect(RoomBookingMapBase): 
     239 
     240    def _checkParams( self ): 
     241        self._location = Location.parse(self._param("location")) 
     242        self._aspect = self._param("aspect") 
     243 
     244    def _getAnswer( self ): 
     245        aspect = self._location.getAspect(self._aspect['id']) 
     246        aspect.updateFromDictionary(self._aspect) 
     247        return {} 
     248 
     249class RoomBookingMapRemoveAspect(RoomBookingMapBase): 
     250 
     251    def _checkParams( self ): 
     252        self._location = Location.parse(self._param("location")) 
     253        self._aspectId = self._param("aspectId") 
     254 
     255    def _getAnswer( self ): 
     256        self._location.removeAspect(self._aspectId) 
     257        return {} 
     258 
     259class RoomBookingMapListAspects(RoomBookingMapBase): 
     260 
     261    def _checkParams( self ): 
     262        self._location = Location.parse(self._param("location")) 
     263 
     264    def _getAnswer( self ): 
     265        return [aspect.toDictionary() for aspect in self._location.getAspects()] 
     266 
  • indico/MaKaC/services/interface/rpc/handlers.py

    r064d40 rf74371  
    2121    "roomBooking.locationsAndRooms.list" :roomBooking.RoomBookingListLocationsAndRooms, 
    2222    "roomBooking.getDateWarning" :roomBooking.GetDateWarning, 
     23 
     24    "roomBooking.mapaspects.create": roomBooking.RoomBookingMapCreateAspect, 
     25    "roomBooking.mapaspects.update": roomBooking.RoomBookingMapUpdateAspect, 
     26    "roomBooking.mapaspects.remove": roomBooking.RoomBookingMapRemoveAspect, 
     27    "roomBooking.mapaspects.list": roomBooking.RoomBookingMapListAspects, 
    2328 
    2429    "resources.timezones.getAll": resources.GetTimezones, 
  • indico/MaKaC/webinterface/pages/roomBooking.py

    r975bdf rf74371  
    2323import MaKaC.webinterface.wcomponents as wcomponents 
    2424from MaKaC.webinterface.pages.main import WPMainBase 
     25from MaKaC.webinterface.pages.base import WPNotDecorated 
    2526from MaKaC.rb_location import CrossLocationDB 
    2627import MaKaC.common.info as info 
     
    223224class WPRoomBookingMapOfRooms(WPRoomBookingBase): 
    224225 
    225     def __init__(self, rh): 
    226         WPRoomBookingBase.__init__(self, rh) 
    227         self._rh = rh 
    228         self.addExtraCSSFile('mapofrooms.css') 
    229  
    230     def getJSFiles(self): 
    231         return WPRoomBookingBase.getJSFiles(self) + \ 
    232                self._includeJSPackage('RoomBooking') 
    233  
    234226    def _getTitle(self): 
    235227        return WPRoomBookingBase._getTitle(self) + " - " + _("Map of rooms") 
     
    239231 
    240232    def _getBody(self, params): 
    241         wc = wcomponents.WRoomBookingMapOfRooms(self._rh) 
     233        wc = wcomponents.WRoomBookingMapOfRooms() 
     234        return wc.getHTML(params) 
     235 
     236class WPRoomBookingMapOfRoomsWidget(WPNotDecorated): 
     237 
     238    def __init__(self, rh, aspects, buildings, defaultLocation, forVideoConference): 
     239        WPNotDecorated.__init__(self, rh) 
     240        self._aspects = aspects 
     241        self._buildings = buildings 
     242        self._defaultLocation = defaultLocation 
     243        self._forVideoConference = forVideoConference 
     244        self.addExtraCSSFile('mapofrooms.css') 
     245 
     246    def getJSFiles(self): 
     247        return WPNotDecorated.getJSFiles(self) + \ 
     248               self._includeJSPackage('RoomBooking') 
     249 
     250    def _getTitle(self): 
     251        return WPNotDecorated._getTitle(self) + " - " + _("Map of rooms") 
     252 
     253    def _setCurrentMenuItem(self): 
     254        self._roomMapOpt.setActive(True) 
     255 
     256    def _getBody(self, params): 
     257        wc = wcomponents.WRoomBookingMapOfRoomsWidget(self._aspects, self._buildings, self._defaultLocation, self._forVideoConference) 
    242258        return wc.getHTML(params) 
    243259 
  • indico/MaKaC/webinterface/rh/roomBooking.py

    r975bdf rf74371  
    4545from MaKaC import plugins 
    4646from MaKaC.plugins.RoomBooking.default.reservation import ResvHistoryEntry 
     47from MaKaC.search.cache import MapOfRoomsCache 
    4748 
    4849class CandidateDataFrom( object ): 
     
    669670class RHRoomBookingMapOfRooms(RHRoomBookingBase): 
    670671 
    671     def _businessLogic( self ): 
     672    def _process(self): 
     673        page = roomBooking_wp.WPRoomBookingMapOfRooms(self) 
     674        return page.display() 
     675 
     676class RHRoomBookingMapOfRoomsWidget(RHRoomBookingBase): 
     677 
     678    def __init__(self, *args, **kwargs): 
     679        super(RHRoomBookingMapOfRoomsWidget, self).__init__(*args, **kwargs) 
     680        self._cache = MapOfRoomsCache() 
     681 
     682    def _checkParams(self, params): 
     683        RHRoomBookingBase._checkParams(self, params) 
     684 
     685    def _businessLogic(self): 
    672686        # get all rooms 
    673         rooms = RoomBase.getRooms(allFast=True) 
     687        defaultLocation = Location.getDefaultLocation() 
     688        rooms = RoomBase.getRooms(location=defaultLocation.friendlyName) 
     689        aspects = [aspect.toDictionary() for aspect in defaultLocation.getAspects()] 
     690 
     691        # specialization for a video conference, CERN-specific 
     692        possibleEquipment = defaultLocation.factory.getEquipmentManager().getPossibleEquipment() 
     693        possibleVideoConference = 'Video conference' in possibleEquipment 
     694        self._forVideoConference = possibleVideoConference and self._getRequestParams().get("avc") == 'y' 
    674695 
    675696        # break-down the rooms by buildings 
     
    692713 
    693714                # add the room to its building 
    694                 building['rooms'].append(room.fossilize()) 
    695  
    696         # filter the buildings with coordinates and return them 
    697         buildings_with_coords = [b for b in buildings.values() if b['has_coordinates']] 
     715                if not self._forVideoConference or room.needsAVCSetup: 
     716                    building['rooms'].append(room.fossilize()) 
     717 
     718        # filter the buildings with rooms and coordinates and return them 
     719        buildings_with_coords = [b for b in buildings.values() if b['rooms'] and b['has_coordinates']] 
     720        self._defaultLocation = defaultLocation.friendlyName 
     721        self._aspects = aspects 
    698722        self._buildings = buildings_with_coords 
    699723 
    700724    def _process(self): 
    701725        self._businessLogic() 
    702         p = roomBooking_wp.WPRoomBookingMapOfRooms(self) 
    703         return p.display() 
     726        page = roomBooking_wp.WPRoomBookingMapOfRoomsWidget(self, self._aspects, self._buildings, self._defaultLocation, self._forVideoConference) 
     727 
     728        params = self._getRequestParams() 
     729        entry = self._cache.loadObject('', params) 
     730        if entry: 
     731            html = entry.getContent() 
     732        else: 
     733            html = page.display() 
     734            self._cache.cacheObject('', html, params) 
     735        return html 
    704736 
    705737# 2. List of ... 
  • indico/MaKaC/webinterface/tpls/RoomBookingAdminLocation.tpl

    r63511f rf74371  
    139139      </td> 
    140140    </tr> 
     141 
     142    <tr><td>&nbsp;</td></tr> 
     143    <tr> 
     144      <td class="titleUpCellTD" style="width: 160px;"><span class="titleCellFormat">Room map attributes</span></td> 
     145      <td bgcolor="white" valign="top" class="blacktext" style="padding-left: 12px;"> 
     146        <div id="AspectsListHolder"></div> 
     147      </td> 
     148    </tr> 
     149 
    141150    </table> 
    142151    <br> 
    143152 
     153<script type="text/javascript"> 
     154 
     155var newAspectsHandler = function(newAspect, setResult) { 
     156    indicoRequest( 
     157        'roomBooking.mapaspects.create', 
     158        { 
     159            location: '<%=location.friendlyName%>', 
     160            aspect: newAspect 
     161        }, 
     162        function(result,error) { 
     163            if (!error) { 
     164                setResult(true); 
     165            } else { 
     166                IndicoUtil.errorReport(error); 
     167                setResult(false); 
     168            } 
     169        } 
     170    ); 
     171} 
     172 
     173var editAspectHandler = function(oldAspect, setResult, newAspect) { 
     174    indicoRequest( 
     175        'roomBooking.mapaspects.update', 
     176        { 
     177            location: '<%=location.friendlyName%>', 
     178            aspect: newAspect 
     179        }, 
     180        function(result,error) { 
     181            if (!error) { 
     182                setResult(true); 
     183            } else { 
     184                IndicoUtil.errorReport(error); 
     185                setResult(false); 
     186            } 
     187        } 
     188    ); 
     189} 
     190 
     191var removeAspectHandler = function(aspect, setResult) { 
     192    indicoRequest( 
     193        'roomBooking.mapaspects.remove', 
     194        { 
     195            location: '<%=location.friendlyName%>', 
     196            aspectId: aspect.get('id') 
     197        }, 
     198        function(result,error) { 
     199            if (!error) { 
     200                setResult(true); 
     201            } else { 
     202                IndicoUtil.errorReport(error); 
     203                setResult(false); 
     204            } 
     205        } 
     206    ); 
     207} 
     208    indicoRequest( 
     209        'roomBooking.mapaspects.list', 
     210        { 
     211            location: '<%= location.friendlyName %>' 
     212        }, 
     213        function(result,error) { 
     214            if (!error) { 
     215                var aspectsListField = new MapAspectListField('AspectsListDiv', 'PeopleList', result, 
     216                        newAspectsHandler, editAspectHandler, removeAspectHandler); 
     217                $E('AspectsListHolder').set(aspectsListField.draw()); 
     218            } else { 
     219                IndicoUtil.errorReport(error); 
     220            } 
     221        } 
     222    ); 
     223 
     224</script> 
    144225 
    145226<!-- ============== Key Performance Indicators ================= --> 
     
    238319</tr> 
    239320</table> 
     321 
  • indico/MaKaC/webinterface/tpls/RoomBookingMapOfRooms.tpl

    r975bdf rf74371  
    1 <table id="map_table"> 
    2     <tr> 
    3         <td id="map_table_left"> 
    4             <div id="positions_canvas"></div> 
    5             <div id="map_canvas"></div> 
    6         </td> 
    7         <td id="map_table_right"> 
    8             <div id="filter_canvas"></div> 
    9         </td> 
    10     </tr> 
    11 </table> 
    12  
    13 <script type="text/javascript"> 
    14  
    15 var positions = [ 
    16     {'lat':46.23456689405093, 'lon':6.046686172485352, 'zoom':15, 'name':'Meyrin', 'isDefault':true}, 
    17     {'lat':46.259051447415175, 'lon':6.057773351931246, 'zoom':15, 'name':'PREVESSIN'}, 
    18     {'lat':46.23573201283012, 'lon':6.054509639707248, 'zoom':17, 'name':'POINT 1'}, 
    19     {'lat':46.25115822762375, 'lon':6.020456314054172, 'zoom':17, 'name':'POINT 2'}, 
    20     {'lat':46.30958858268458, 'lon':6.077267646724067, 'zoom':17, 'name':'POINT 5'}, 
    21     {'lat':46.29345231426436, 'lon':6.1115119456917455, 'zoom':17, 'name':'POINT 6'}, 
    22     {'lat':46.24158691675184, 'lon':6.097038745847385, 'zoom':17, 'name':'POINT 8'} 
    23 ]; 
    24 var buildings = <%= jsonEncode(buildings) %>; 
    25  
    26 IndicoUI.executeOnLoad(function(){ 
    27     var roomMap = new RoomMap($E('map_canvas').dom, $E('positions_canvas').dom, positions, buildings); 
    28 }); 
    29  
    30 </script> 
     1<iframe src="<%= mapOfRoomsWidgetURL %>" 
     2    frameborder="0" 
     3    width="100%%" 
     4    height="600px" 
     5    style="margin-top: -10px;"> 
     6</iframe> 
  • indico/MaKaC/webinterface/urlHandlers.py

    r975bdf rf74371  
    302302class UHRoomBookingMapOfRooms( URLHandler ): 
    303303    _relativeURL = "roomBooking.py/mapOfRooms" 
     304 
     305class UHRoomBookingMapOfRoomsWidget( URLHandler ): 
     306    _relativeURL = "roomBooking.py/mapOfRoomsWidget" 
    304307 
    305308class UHRoomBookingWelcome( URLHandler ): 
  • indico/MaKaC/webinterface/wcomponents.py

    r975bdf rf74371  
    61206120class WRoomBookingMapOfRooms(WTemplated): 
    61216121 
    6122     def __init__(self, rh): 
    6123         self._rh = rh 
     6122    def getVars(self): 
     6123 
     6124        vars = WTemplated.getVars(self) 
     6125        vars["mapOfRoomsWidgetURL"] = urlHandlers.UHRoomBookingMapOfRoomsWidget.getURL( None ) 
     6126 
     6127        return vars 
     6128 
     6129class WRoomBookingMapOfRoomsWidget(WTemplated): 
     6130 
     6131    def __init__(self, aspects, buildings, defaultLocation, forVideoConference): 
     6132        self._aspects = aspects 
     6133        self._buildings = buildings 
     6134        self._defaultLocation = defaultLocation 
     6135        self._forVideoConference = forVideoConference 
    61246136 
    61256137    def getVars(self): 
    61266138        vars = WTemplated.getVars(self) 
    61276139 
    6128         vars["buildings"] = self._rh._buildings 
     6140        vars["aspects"] = self._aspects 
     6141        vars["buildings"] = self._buildings 
     6142        vars["defaultLocation"] = self._defaultLocation 
     6143        vars["forVideoConference"] = self._forVideoConference 
    61296144 
    61306145        return vars 
  • indico/htdocs/css/Default.css

    r8cd77c rf74371  
    57205720} 
    57215721 
     5722div.AspectsListDiv { 
     5723    margin-bottom: 10px; 
     5724    height: 100px; 
     5725    width: 350px; 
     5726    border: 1px solid #CCCCCC; 
     5727    overflow: auto; 
     5728} 
     5729 
    57225730div.PeopleListDiv { 
    57235731    margin-bottom: 10px; 
  • indico/htdocs/css/mapofrooms.css

    r975bdf rf74371  
     1.tip p { 
     2    margin-top: 8px; 
     3    margin-bottom: 8px; 
     4} 
     5 
     6div.caption { 
     7    padding: 3px; 
     8    background-color: #F0F0F0 !important; 
     9    border: 1px solid #888888; 
     10    border-bottom: none; 
     11} 
     12 
    113/* Marker info */ 
     14 
     15.mapBuildingInfo { 
     16    max-height: 300px; 
     17    overflow-y: auto; 
     18} 
     19 
     20.mapRoomInfoLink { 
     21    border-left: 1px solid #AAAAAA; 
     22    padding-left: 5px; 
     23} 
     24 
     25.mapBookRoomLink { 
     26    padding-right: 5px; 
     27} 
    228 
    329#filter_canvas { 
    430    width: 200px; 
     31    padding-left: 10px; 
     32} 
     33 
     34.mapFilterTitle { 
     35    color: #777777; 
     36    font-size: 15px; 
     37    margin: 0; 
     38    padding: 0; 
     39    font-weight: bold; 
     40} 
     41 
     42.mapFilterGroup { 
     43    padding-left: 20px; 
     44} 
     45 
     46.mapFilterTextbox { 
     47    width: 70px; 
     48} 
     49 
     50.mapFilterLabel { 
     51    color:#444444; 
     52    font-size:13px; 
     53    padding-right: 5px; 
     54    padding-left: 2px; 
     55    display: inline-block; 
     56    min-width: 70px; 
     57} 
     58 
     59.mapRoomTooltipTitle { 
     60    font-weight: bold; 
     61    padding-left: 3px; 
     62} 
     63 
     64.mapRoomTooltipImage { 
     65    padding-left: 8px; 
     66    padding-right: 8px; 
     67} 
     68 
     69.mapRoomTooltipDescription { 
     70    padding-left: 8px; 
     71    padding-right: 8px; 
     72} 
     73 
     74.mapResultsInfo { 
     75    font-weight: bold; 
     76    font-size: 12px; 
     77} 
     78 
     79#mapFilterBox { 
     80    float: none; 
     81    margin-top: -27px; 
     82    margin-left: 10px; 
     83    width: 210px; 
     84} 
     85 
     86#mapFilterContentBox { 
     87    padding-right: 10px; 
     88    padding-left: 5px; 
     89} 
     90 
     91#filter_canvas { 
     92    margin-top: -3px; 
     93} 
     94 
     95#filter_canvas p { 
     96    margin-top: 10px; 
     97    margin-bottom: 10px; 
     98} 
     99.mapBuildingTitle { 
     100    font-weight: bold; 
     101} 
     102 
     103.mapButton { 
     104    margin-right: 5px; 
     105} 
     106 
     107/* Map table */ 
     108 
     109html, body { 
     110    margin: 0 auto; 
     111    height: 100%; 
     112} 
     113 
     114#map_table { 
     115    width: 99%; 
     116    height: 90%; 
     117} 
     118 
     119#map_cell { 
     120    width: 100%; 
     121    height:100%; 
     122} 
     123 
     124#map_canvas { 
     125    width: 100%; 
     126    height:100%; 
    5127} 
    6128 
     
    11133#map_table_left { 
    12134    width: 100%; 
    13 } 
    14  
    15 #map_canvas { 
    16     width: 100%; 
    17     height: 600px; 
    18 } 
    19  
    20 #map_table { 
    21     width: 100%; 
    22 } 
    23  
    24 .mapBuildingTitle { 
    25     font-weight: bold; 
    26135} 
    27136 
     
    37146 
    38147ul.mapAspectsList li { 
     148    position: relative; 
     149    padding-right: 5px; 
     150    padding-left: 5px; 
     151    border-right: 1px solid #AAAAAA; 
     152} 
     153 
     154ul.mapAspectsList li.browserIE7 { 
    39155    display: inline; 
    40     position: relative; 
    41     padding-right: 15px; 
    42     padding-left: 15px; 
    43     border-right: 1px solid #AAAAAA; 
     156} 
     157 
     158ul.mapAspectsList li.browserDefault { 
     159    display: inline-table; 
    44160} 
    45161 
    46162ul.mapAspectsList a { 
    47163    font-family: Verdana; 
    48     font-size: 16px; 
     164    font-size: 13px; 
    49165    font-weight: bold; 
    50166    cursor: pointer; 
     
    53169ul.mapAspectsList a:hover { 
    54170    color: #dbb03a !important; 
     171} 
     172 
     173.mapAspectsItem { 
     174    white-space: nowrap; 
    55175} 
    56176 
  • indico/htdocs/js/indico/Management/Loader.js

    r9033fd rf74371  
    44include(ScriptRoot + "indico/Management/eventCreation.js"); 
    55include(ScriptRoot + "indico/Management/Timetable.js"); 
     6include(ScriptRoot + "indico/Management/RoomBookingMapOfRooms.js"); 
  • indico/htdocs/js/indico/RoomBooking/MapOfRooms.js

    r975bdf rf74371  
    1 // include the Google Maps API 
    2 mapsKey = "ABQIAAAAma3utMfjLLhSmsMwYFxuOxQw5QZGSL2Qh09zAqNyDcYBVXi0OBTBnNqS2Hl5cVWGNuhE9BATvtEqyA"; 
    3 include("http://maps.google.com/maps?file=api&amp;v=2&amp;key=" + mapsKey); 
     1include("http://maps.google.com/maps/api/js?sensor=false"); 
    42 
    53/** 
     
    86type ("RoomMap", ["IWidget"], 
    97    { 
    10         initialize: function(mapCanvas, aspectsCanvas, aspects, buildings) { 
    11             if (GBrowserIsCompatible()) { 
    12                 this.createMap(mapCanvas); 
    13                 this.createAspectChangeLinks(aspectsCanvas, aspects); 
    14                 this.createMarkers(buildings); 
    15             } else { 
    16                 this.showNotCompatible(mapCanvas); 
     8        initialize: function(mapCanvas, aspectsCanvas, filterCanvas, aspects, buildings, filters) { 
     9            this.initData(aspects, buildings, filters); 
     10            this.createMap(mapCanvas); 
     11            this.initializeBounds(); 
     12            this.createAspectChangeLinks(aspectsCanvas); 
     13            this.createBuildingMarkers(); 
     14            this.createFilters(filterCanvas); 
     15            this.setDefaultFilterValues(); 
     16            this.updateFiltersState(); 
     17            this.filterMarkers(); 
     18        }, 
     19 
     20        initData: function(aspects, buildings, filters) { 
     21            this.aspects = aspects; 
     22            this.buildings = buildings; 
     23            this.filters = filters; 
     24 
     25            // save a reference to the building number filter 
     26            for (var i = 0; i < filters.length; i++) { 
     27                var filter = filters[i]; 
     28                if (filter.filterType == 'building' && filter.property == 'number') { 
     29                    this.buildingNumberFilter = filter; 
     30                } 
    1731            } 
    1832        }, 
    1933 
    2034        createMap: function(mapCanvas) { 
     35            this.markers = []; 
     36 
     37            // map options 
     38            var options = { 
     39              zoom: 17, 
     40              mapTypeId: google.maps.MapTypeId.HYBRID 
     41            }; 
     42 
    2143            // Google Maps API setup 
    22             this.map = new GMap2(mapCanvas); 
    23             this.map.setMapType(G_HYBRID_MAP); 
    24             this.map.setUIToDefault(); 
    25             this.map.enableDoubleClickZoom(); 
    26         }, 
    27  
    28         createInfoMarker: function(point, info) { 
    29             var marker = new GMarker(point); 
    30  
    31             // when the marker is clicked, open the info window 
    32             GEvent.addListener(marker, "click", function() { 
    33                 marker.openInfoWindowHtml(info); 
    34             }); 
    35  
    36             return marker; 
     44            this.map = new google.maps.Map(mapCanvas, options); 
     45            google.maps.event.addListener(this.map, "click", this.createMapClickHandler()); 
     46        }, 
     47 
     48        createMapClickHandler: function() { 
     49            var self = this; 
     50            return function() { 
     51                self.closeTooltips(); 
     52                self.closeInfoBaloon(); 
     53            } 
     54        }, 
     55 
     56        createBuildingMarkers: function() { 
     57            for (var i in this.buildings) { 
     58                var building = this.buildings[i]; 
     59                building.point = new google.maps.LatLng(building.latitude, building.longitude); 
     60 
     61                // create marker for the building 
     62                var marker = new google.maps.Marker({ 
     63                    position: building.point, 
     64                    map: this.map, 
     65                    visible: false 
     66                }); 
     67 
     68                // the building and marker reference each-other 
     69                marker.building = building; 
     70                building.marker = marker; 
     71 
     72                // when the marker is clicked, open the info window 
     73                marker.onClick = this.createMarkerClickHandler(marker); 
     74                google.maps.event.addListener(marker, "click", marker.onClick); 
     75 
     76                this.markers.push(marker); 
     77            } 
     78        }, 
     79 
     80        createMarkerClickHandler: function(marker) { 
     81            var self = this; 
     82            return function() { 
     83                // it the info baloon is shown for a first time, create it 
     84                if (!exists(marker.infoWindow)) { 
     85                    var info = self.createBuildingInfo(marker.building); 
     86                    marker.infoWindow = new google.maps.InfoWindow({content: info}); 
     87                } 
     88 
     89                // closed the tooltips and info baloon 
     90                self.closeInfoBaloon(); 
     91                self.closeTooltips(); 
     92 
     93                // show the info baloon 
     94                self.activeInfoWindow = marker.infoWindow; 
     95                marker.infoWindow.open(self.map, marker); 
     96            } 
     97        }, 
     98 
     99        closeInfoBaloon: function() { 
     100            // if a info baloon is shown, close it 
     101            if (exists(this.activeInfoWindow)) { 
     102                this.activeInfoWindow.close(); 
     103                this.activeInfoWindow = null; 
     104            } 
     105        }, 
     106 
     107        closeTooltips: function() { 
     108            domTT_closeAll(); 
    37109        }, 
    38110 
     
    40112            // unselect the previous selected link (if any) 
    41113            if (this.selectedLink) { 
    42                 this.selectedLink.dom.className = 'mapAspectUnselected'; 
     114                this.selectedLink.dom.className = this.constructAspectCss(false); 
    43115            } 
    44116 
    45117            // select the clicked link 
    46118            this.selectedLink = link; 
    47             link.dom.className = 'mapAspectSelected'; 
     119            link.dom.className = this.constructAspectCss(true); 
    48120        }, 
    49121 
    50122        createAspectChangeFunction: function(aspect, link) { 
    51             setSelectedAspectStyle = this.setSelectedAspectStyle; 
    52             map = this.map; 
     123            var self = this; 
     124            // execute this every time the user clicks on some aspect and changes the visible map area 
    53125            return function() { 
    54                 map.setCenter(new GLatLng(aspect.lat, aspect.lon), aspect.zoom); 
    55                 setSelectedAspectStyle(link); 
    56             } 
    57         }, 
    58  
    59         createAspectChangeLinks: function(aspectsCanvas, aspects) { 
     126                self.closeTooltips(); 
     127                self.map.setCenter(new google.maps.LatLng(aspect.centerLatitude, aspect.centerLongitude)); 
     128                self.map.setZoom(parseInt(aspect.zoomLevel)); 
     129                self.setSelectedAspectStyle(link); 
     130            } 
     131        }, 
     132 
     133        constructBrowserSpecificCss: function() { 
     134            return this.isBrowserIE7() ? 'browserIE7' : 'browserDefault'; 
     135        }, 
     136 
     137        constructAspectCss: function (selected) { 
     138            var selectionClass = selected ? 'mapAspectSelected' : 'mapAspectUnselected'; 
     139            return 'mapAspectsItem ' + selectionClass; 
     140        }, 
     141 
     142        createAspectChangeLinks: function(aspectsCanvas) { 
    60143            var links = Html.ul({className:'mapAspectsList'}).dom; 
    61             for (var i = 0; i < aspects.length; i++) { 
    62                 aspect = aspects[i]; 
     144            for (var i = 0; i < this.aspects.length; i++) { 
     145                var aspect = this.aspects[i]; 
    63146 
    64147                // construct a link that changes the map aspect if clicked 
    65                 var link = Html.a({'href':'#', className:'mapAspectUnselected'}, aspect.name); 
    66                 var itemClassName = i == 0 ? 'first' : i == aspects.length - 1 ? 'last' : ''; 
    67                 var item = Html.li({className:itemClassName}, link.dom); 
     148                var link = Html.a({'href': '#', className: this.constructAspectCss(false)}, aspect.name); 
     149                var itemClassName = i == 0 ? 'first ' : i == this.aspects.length - 1 ? 'last ' : ''; 
     150                var item = Html.li({className:itemClassName + this.constructBrowserSpecificCss()}, link.dom); 
    68151                var aspectChangeFunction = this.createAspectChangeFunction(aspect, link); 
    69152 
     153                // store the link for the aspect 
     154                aspect.link = link; 
     155 
    70156                // if the aspect is a default one, apply it on start 
    71                 if (aspect.isDefault) { 
     157                if (aspect.defaultOnStartup) { 
    72158                    aspectChangeFunction(); 
    73159                } 
     
    82168 
    83169        createRoomInfo: function(building, room) { 
    84             // info format: [link with room address] - [description of the room] 
     170            var self = this; 
    85171            var address = building.number + '/' + room.floor + '-' + room.roomNr; 
    86             var link = Html.a({href:room.url, className:'mapRoomLink'}, address).dom; 
    87             var desc = Html.span({className:'mapRoomDescription'}, room.markerDescription).dom; 
    88  
    89             roomInfo = Html.p({className:'mapRoomInfo'}, link, ' - ', desc); 
     172            var caption = "Room " + address; 
     173 
     174            // room address 
     175            var addr = Html.span({className:'mapRoomAddress'}, address).dom; 
     176 
     177            // "Book" link 
     178            var book = Html.a({href:room.url, className:'mapBookRoomLink'}, "Book"); 
     179 
     180            // "More" link - for room details 
     181            var more = Html.a({href:"#", className:'mapRoomInfoLink'}, "More..."); 
     182 
     183            // room details elements 
     184            var title = Html.div({className: 'mapRoomTooltipTitle'}, caption); 
     185            var img = Html.img({src: room.tipPhotoURL, width: 212, height: 140, className: 'mapRoomTooltipImage'}); 
     186            var desc = Html.div({className: 'mapRoomTooltipDescription'}, room.markerDescription); 
     187            var all = Widget.lines([img, desc]); 
     188            var help = Html.div({className: 'tip'}, all.dom); 
     189 
     190            // when the "More" link is clicked, show a tooltip with room details 
     191            more.observeClick(function(event) { 
     192                self.closeTooltips(); 
     193                domTT_activate(more.dom, event, 'content', help.dom, 'maxWidth', 223, 'type', 'sticky', 'caption', title.dom, 'closeLink', ' x  '); 
     194            }); 
     195 
     196            var roomInfo = Html.p({className:'mapRoomInfo'}, addr, ' - ', book.dom, more.dom); 
    90197            return roomInfo.dom; 
    91198        }, 
     
    101208            for (var j = 0; j < building.rooms.length; j++) { 
    102209                var room = building.rooms[j]; 
    103                 var roomInfo = this.createRoomInfo(building, room); 
    104                 roomsInfo.appendChild(roomInfo); 
    105             } 
    106  
     210                if (room.showOnMap) { 
     211                    var roomInfo = this.createRoomInfo(building, room); 
     212                    roomsInfo.appendChild(roomInfo); 
     213                } 
     214            } 
     215 
     216            // building info box 
    107217            var buildingInfo = Html.div({className:'mapBuildingInfo'}, title, roomsInfo); 
    108218            return buildingInfo.dom; 
    109219        }, 
    110220 
    111         createMarkers: function(buildings) { 
    112             for (var i = 0; i < buildings.length; i++) { 
    113                 var building = buildings[i]; 
    114  
    115                 // initialize the marker aspect and info 
    116                 var pos = new GLatLng(building.latitude, building.longitude); 
    117                 var info = this.createBuildingInfo(building); 
    118  
    119                 // construct the marker and add it to the map overlay 
    120                 var marker = this.createInfoMarker(pos, info); 
    121                 this.map.addOverlay(marker); 
    122             } 
    123         }, 
    124  
    125         showNotCompatible: function(mapCanvas) { 
    126             var info = Html.div({}, "Your browser is not compatible with Google Maps!").dom; 
    127             mapCanvas.appendChild(info); 
    128         }, 
     221        showMarkers: function() { 
     222            var bounds = this.map.getBounds(); 
     223            var inBoundsCount = 0; 
     224 
     225            // 'alone' building - a bulding that is displayed alone on the screen 
     226            var aloneBuilding = null; 
     227 
     228            // 'exact' building - a building whose number was entered in the building filter 
     229            var exactBuilding = null; 
     230 
     231            // if a building number filter exists, get its value 
     232            var exactBuildingNumber = null; 
     233            if (this.buildingNumberFilter) { 
     234                exactBuildingNumber = this.getFilterValue(this.buildingNumberFilter); 
     235            } 
     236 
     237            for (var i = 0; i < this.buildings.length; i++) { 
     238                var building = this.buildings[i]; 
     239                this.boundCounters[i] = 0; 
     240 
     241                // if the building is filtered as visible on map 
     242                if (building.showOnMap) { 
     243                    // if only 1 building is visible - that's the 'alone' building 
     244                    if (this.visibleBuildingsCount == 1) { 
     245                        aloneBuilding = building; 
     246                    } 
     247 
     248                    // if the building number is entered in the building filter, that's the 'exact' building 
     249                    if (exactBuildingNumber != null && building.number == exactBuildingNumber) { 
     250                        exactBuilding = building; 
     251                    } 
     252 
     253                    // initialize the marker aspect and info 
     254                    var pos = new google.maps.LatLng(building.latitude, building.longitude); 
     255 
     256                    // count the number of rooms in each of the aspect areas 
     257                    for (var j = 0; j < this.bounds.length; j++) { 
     258                        if (this.bounds[j].contains(pos)) { 
     259                            this.boundCounters[j] += building.visibleRoomsSize; 
     260                        } 
     261                    } 
     262                } 
     263 
     264                // show only the filtered buildings 
     265                building.marker.setVisible(building.showOnMap); 
     266            } 
     267 
     268            // if an 'exact' building if found, show it in the center of the map 
     269            if (exactBuilding != null) { 
     270                var center = new google.maps.LatLng(exactBuilding.latitude, exactBuilding.longitude); 
     271                this.map.setCenter(center); 
     272            } 
     273 
     274            // if an 'alone' building if found, show its info baloon 
     275            if (aloneBuilding != null) { 
     276                aloneBuilding.marker.onClick(); 
     277            } 
     278 
     279        }, 
     280 
     281        getFilterPropertyOptions: function(filter) { 
     282            var options = []; 
     283            function addOption(option) { 
     284                if (options.indexOf(option) < 0) options.push(option); 
     285            } 
     286 
     287            for (var i = 0; i < this.buildings.length; i++) { 
     288                var building = this.buildings[i]; 
     289                if (filter.filterType == 'building') { 
     290                    addOption(building[filter.property]); 
     291                } else { 
     292                    for (var j = 0; j < building.rooms.length; j++) { 
     293                        var room = building.rooms[j]; 
     294                        addOption(room[filter.property]); 
     295                    } 
     296                } 
     297            } 
     298 
     299            return options; 
     300        }, 
     301 
     302        updateFiltersState: function() { 
     303            for (var i = 0; i < this.filters.length; i++) { 
     304                var filter = this.filters[i]; 
     305 
     306                // the corresponding function calculates if the filter input is enabled (default: yes) 
     307                filter.enabled = !exists(filter.enabledIf) || filter.enabledIf(this); 
     308                filter.input.dom.disabled = !filter.enabled; 
     309 
     310                // the corresponding function calculates if the filter is active (default: yes) 
     311                filter.active = !exists(filter.activeIf) || filter.activeIf(this); 
     312            } 
     313        }, 
     314 
     315        createFilterWidget: function(filter) { 
     316            // value type 
     317            var input; 
     318            if (filter.inputType == 'text' || filter.inputType == 'subtext') { 
     319                // text input for text and sub-text filters 
     320                input = Html.input('text', {className: 'mapFilterTextbox'}); 
     321            } else if (filter.inputType == 'boolean') { 
     322                // checkbox input for boolean filters 
     323                input = Html.checkbox({className: 'mapFilterCheckbox'}); 
     324            } else if (filter.inputType == 'list_contains') { 
     325                // checkbox input for 'list containts' filters 
     326                input = Html.checkbox({className: 'mapFilterCheckbox'}); 
     327            } else if (filter.inputType == 'hidden') { 
     328                // no input for hidden filters 
     329                input = null; 
     330            } else if (filter.inputType == 'combo') { 
     331                // drop-down box input for combo filters 
     332                var options = []; 
     333                var optionValues = this.getFilterPropertyOptions(filter); 
     334                for (var i = 0; i < optionValues.length; i++) { 
     335                    var optionValue = optionValues[i]; 
     336                    var option = Html.option({value: optionValue}, optionValue); 
     337                    options.push(option); 
     338                } 
     339                input = Html.select({className: 'mapFilterCombo'}, options); 
     340            } 
     341 
     342            if (input) { 
     343                filter.input = input; 
     344 
     345                // observe change of the filter inputs 
     346                var self = this; 
     347                input.observeKeyPress(function(key) { 
     348                    self.onFiltersInputChanged(); 
     349                }); 
     350                input.observeChange(function(key) { 
     351                    self.onFiltersInputChanged(); 
     352                }); 
     353 
     354                // title 
     355                var label = Html.span({className: 'mapFilterLabel'}, filter.label); 
     356 
     357                // layout order 
     358                var order; 
     359                if (filter.inputType == 'text' || filter.inputType == 'subtext' || filter.inputType == 'combo') { 
     360                    order = [label, input]; 
     361                } else { 
     362                    order = [input, label]; 
     363                } 
     364                return Widget.inline(order); 
     365            } else { 
     366                return null; 
     367            } 
     368        }, 
     369 
     370        onFiltersInputChanged: function() { 
     371            this.updateFiltersState(); 
     372        }, 
     373 
     374        setDefaultFilterValue: function(filter) { 
     375            if (filter.group !== undefined) { 
     376                if (filter.defaultValue !== undefined) { 
     377                    filter.mainCheckbox.dom.checked = filter.defaultValue; 
     378                    filter.mainCheckbox.dispatchEvent("change"); 
     379                } 
     380                for (var i = 0; i < filter.group.length; i++) { 
     381                    this.setDefaultFilterValue(filter.group[i]); 
     382                } 
     383            } else { 
     384                if (filter.defaultValue !== undefined && filter.input) { 
     385                    if (filter.inputType == 'boolean' || filter.inputType == 'list_contains') { 
     386                        filter.input.dom.checked = filter.defaultValue; 
     387                    } else { 
     388                        filter.input.dom.value = filter.defaultValue; 
     389                    } 
     390                    filter.input.dispatchEvent("change"); 
     391                } 
     392            } 
     393        }, 
     394 
     395        setDefaultFilterValues: function() { 
     396            for (var i = 0; i < this.filters.length; i++) { 
     397                this.setDefaultFilterValue(this.filters[i]); 
     398            } 
     399        }, 
     400 
     401        createGroupWidget: function(filter) { 
     402            var widgets = Html.div({className: "mapFilterGroup"}, this.createFilterWidgets(filter.group)); 
     403 
     404            var mainCheckbox = Html.checkbox({className: 'mapFilterCheckbox'}); 
     405 
     406            function onMainCheckboxClick() { 
     407                if (mainCheckbox.dom.checked) { 
     408                    IndicoUI.Effect.appear(widgets); 
     409                } else { 
     410                    IndicoUI.Effect.disappear(widgets); 
     411                } 
     412            } 
     413 
     414            mainCheckbox.observeChange(onMainCheckboxClick); 
     415            onMainCheckboxClick(); 
     416 
     417            var label = Html.span({className: 'mapFilterLabel'}, filter.label); 
     418 
     419            var top = Html.div({}, mainCheckbox, label); 
     420 
     421            filter.widgets = widgets; 
     422            filter.mainCheckbox = mainCheckbox 
     423            return Html.div({}, top, widgets); 
     424        }, 
     425 
     426        createFilterWidgets: function(filters) { 
     427            var widgets = []; 
     428            for (var i = 0; i < filters.length; i++) { 
     429                var filter = filters[i]; 
     430                var widget; 
     431                if (exists(filter.group)) { 
     432                    widget = this.createGroupWidget(filter); 
     433                } else { 
     434                    widget = this.createFilterWidget(filter); 
     435                } 
     436                widgets.push(widget); 
     437            } 
     438            return Widget.lines(widgets); 
     439        }, 
     440 
     441        createFilters: function(filterCanvas) { 
     442            var lines = []; 
     443 
     444            var title = Html.div({className: 'mapFilterTitle'}, $T("Search criteria")+":"); 
     445            lines.push(title); 
     446 
     447            var widgets = this.createFilterWidgets(this.filters); 
     448            lines.push(widgets); 
     449 
     450            var self = this; 
     451 
     452            var filterButton = Html.button('mapButton', $T("Filter")); 
     453            filterButton.observeClick(function() { 
     454                self.filterMarkers(); 
     455            }); 
     456 
     457            var resetButton = Html.button('mapButton', $T("Reset")); 
     458            resetButton.observeClick(function() { 
     459                self.setDefaultFilterValues(); 
     460                self.filterMarkers(); 
     461            }); 
     462 
     463            this.buttons = Html.div({}, filterButton, resetButton); 
     464            lines.push(this.buttons); 
     465 
     466            this.progress = Html.span({}, progressIndicator(true, true)).dom; 
     467 
     468            this.resultsInfo = Html.span('mapResultsInfo', ""); 
     469            lines.push(this.resultsInfo); 
     470 
     471            filterCanvas.appendChild(Widget.lines(lines).dom); 
     472        }, 
     473 
     474        matchesCriteria: function(x, criteria) { 
     475            for (var i = 0; i < criteria.length; i++) { 
     476                var criterium = criteria[i]; 
     477                if (!criterium(x)) { 
     478                    return false; 
     479                } 
     480            } 
     481            return true; 
     482        }, 
     483 
     484        filterByCriteria: function(items, criteria) { 
     485            var count = 0; 
     486            for (var i = 0; i < items.length; i++) { 
     487                var item = items[i]; 
     488                item.showOnMap = this.matchesCriteria(item, criteria); 
     489                if (item.showOnMap) { 
     490                    count++; 
     491                } 
     492            } 
     493            return count; 
     494        }, 
     495 
     496        filterBuildingsByCriteria: function(buildingCriteria, roomCriteria) { 
     497            this.filterByCriteria(this.buildings, buildingCriteria); 
     498            for (var i = 0; i < this.buildings.length; i++) { 
     499                var building = this.buildings[i]; 
     500                if (building.showOnMap) { 
     501                    building.visibleRoomsSize = this.filterByCriteria(building.rooms, roomCriteria); 
     502                    this.visibleRoomsCount += building.visibleRoomsSize; 
     503                    if (building.visibleRoomsSize > 0) { 
     504                        this.visibleBuildingsCount++; 
     505                    } else { 
     506                        building.showOnMap = false; 
     507                    } 
     508                } else { 
     509                    building.visibleRoomsSize = 0; 
     510                } 
     511            } 
     512        }, 
     513 
     514        building: function(number) { 
     515            for (var i = 0; i < this.buildings.length; i++) { 
     516                var building = this.buildings[i]; 
     517                if (building.number == number) { 
     518                    return building; 
     519                } 
     520            } 
     521            return null; 
     522        }, 
     523 
     524        filterInput: function(index) { 
     525            return this.getFilterValue(this.filters[index]); 
     526        }, 
     527 
     528        createPropertyFilter: function(inputType, propertyName, expectedValue) { 
     529            if (inputType == 'list_contains') { 
     530                return function(obj) { 
     531                    // search for element in the list 
     532                    return obj[propertyName].indexOf(expectedValue) > -1; 
     533                } 
     534            } else if (inputType == 'subtext') { 
     535                return function(obj) { 
     536                    // search for substring in the string 
     537                    return obj[propertyName].indexOf(expectedValue) > -1; 
     538                } 
     539            } else { 
     540                return function(obj) { 
     541                    return obj[propertyName] == expectedValue; 
     542                } 
     543            } 
     544        }, 
     545 
     546        getFilterValue: function(filter) { 
     547            var value; // the filter value 
     548            if (filter.inputType == 'boolean') { 
     549                // boolean value that tells if checkbox is checked 
     550                value = filter.input.dom.checked; 
     551                if (value && filter.checkedValue !== undefined) { 
     552                    // if the checkbox is checked, the boolean value can be replaced with arbitrary one 
     553                    value = filter.checkedValue; 
     554                } else if (value && filter.filterFunction !== undefined) { 
     555                    // for more complex filtering logic, a custom filter function can be specified 
     556                    value = filter.filterFunction; 
     557                } 
     558            } else if (filter.inputType == 'list_contains') { 
     559                // if the checkbox is checked, the filter value is the specified one 
     560                value = filter.input.dom.checked ? filter.value : ''; 
     561            } else if (filter.inputType == 'hidden') { 
     562                value = filter.defaultValue; 
     563            } else { 
     564                value = filter.input.dom.value; 
     565            } 
     566            return value; 
     567        }, 
     568 
     569        addFilterFunctionToCriteria: function(filter, func, buildingCriteria, roomCriteria) { 
     570            if (filter.filterType == 'building') { 
     571                buildingCriteria.push(func); 
     572            } else { 
     573                roomCriteria.push(func); 
     574            } 
     575        }, 
     576 
     577        addFiltersToCriteria: function(filters, buildingCriteria, roomCriteria) { 
     578            for (var i = 0; i < filters.length; i++) { 
     579                filter = filters[i]; 
     580                // check if the filter is a group of filters 
     581                if (filter.group !== undefined) { 
     582                    var value = filter.mainCheckbox.dom.checked; 
     583                    // the group filter shoud be enabled 
     584                    if (!filter.optional || value) { 
     585                        if (filter.property) { 
     586                            // a filter function that checks the specified property for the specified value 
     587                            var func = this.createPropertyFilter('boolean', filter.property, value); 
     588                            this.addFilterFunctionToCriteria(filter, func, buildingCriteria, roomCriteria); 
     589                        } 
     590                        this.addFiltersToCriteria(filter.group, buildingCriteria, roomCriteria); 
     591                    } 
     592                } else { 
     593                    var value = this.getFilterValue(filter); 
     594                    if ((!filter.optional || value) && filter.active && filter.enabled) { 
     595                        var func; 
     596                        if (filter.filterFunction) { 
     597                            // the first argument of the custom filter function is the calling instance 
     598                            // specify the calling instance (this) and derivate a proper predicate function 
     599                            func = curry(filter.filterFunction, this); 
     600                        } else { 
     601                            // a filter function that checks the specified property for the specified value 
     602                            func = this.createPropertyFilter(filter.inputType, filter.property, value); 
     603                        } 
     604                        this.addFilterFunctionToCriteria(filter, func, buildingCriteria, roomCriteria); 
     605                    } 
     606                } 
     607            } 
     608        }, 
     609 
     610        resetFilteringCycle: function() { 
     611            this.visibleBuildingsCount = 0; 
     612            this.visibleRoomsCount = 0; 
     613 
     614            // the info balloons whould be re-created after each filtering 
     615            for (var i = 0; i < this.buildings.length; i++) { 
     616                var building = this.buildings[i]; 
     617                building.marker.infoWindow = null; 
     618            } 
     619 
     620            // reset the counters for the aspects (areas) 
     621            for (var i = 0; i < this.boundCounters.length; i++) { 
     622                this.boundCounters[i] = 0; 
     623            } 
     624        }, 
     625 
     626        filterMarkers: function() { 
     627            this.buttons.dom.appendChild(this.progress); 
     628            var mapView = this; 
     629            setTimeout(function() { 
     630                var buildingCriteria = []; 
     631                var roomCriteria = []; 
     632                mapView.resetFilteringCycle(); 
     633                mapView.closeTooltips(); 
     634                mapView.closeInfoBaloon(); 
     635                mapView.addFiltersToCriteria(mapView.filters, buildingCriteria, roomCriteria); 
     636                mapView.filterBuildingsByCriteria(buildingCriteria, roomCriteria); 
     637                mapView.showMarkers(); 
     638                mapView.showResultsInfo(); 
     639                mapView.updateAspectsInfo(); 
     640                mapView.buttons.dom.removeChild(mapView.progress); 
     641            }, 0); 
     642        }, 
     643 
     644        showResultsInfo: function() { 
     645            var info = $T('Total') + ' ' 
     646                        + this.visibleRoomsCount + ' ' + $T('room(s)') 
     647                        + ' / ' + this.visibleBuildingsCount + ' ' + $T('building(s)'); 
     648            this.resultsInfo.dom.innerHTML = info; 
     649        }, 
     650 
     651        initializeBounds: function() { 
     652            this.bounds = []; 
     653            this.boundCounters = []; 
     654            for (var i = 0; i < this.aspects.length; i++) { 
     655                var aspect = this.aspects[i]; 
     656 
     657                // initialize the bounds of the area descripbed in the aspect 
     658                var sw = new google.maps.LatLng(aspect.topLeftLatitude, aspect.topLeftLongitude); 
     659                var ne = new google.maps.LatLng(aspect.bottomRightLatitude, aspect.bottomRightLongitude); 
     660                this.bounds.push(new google.maps.LatLngBounds(sw, ne)); 
     661 
     662                // initialize the array of counters for the aspect areas 
     663                this.boundCounters.push(0); 
     664            } 
     665        }, 
     666 
     667        updateAspectsInfo: function() { 
     668            for (var i = 0; i < this.aspects.length; i++) { 
     669                var aspect = this.aspects[i]; 
     670                var counter = this.boundCounters[i]; 
     671                aspect.link.dom.innerHTML = aspect.name + " (" + counter + ")"; 
     672            } 
     673        }, 
     674 
     675        isBrowserIE7: function() { 
     676            var isIE = window.ActiveXObject ? true : false; 
     677            var agent = navigator.userAgent.toLowerCase(); 
     678            return isIE && /msie 7/.test(agent); 
     679        } 
    129680 
    130681    }, 
     
    133684     * Constructor of the RoomMap 
    134685     */ 
    135     function(mapCanvas, aspectsCanvas, aspects, buildings) { 
    136         this.initialize(mapCanvas, aspectsCanvas, aspects, buildings); 
     686 
     687    function(mapCanvas, aspectsCanvas, filterCanvas, aspects, buildings, filters) { 
     688        this.initialize(mapCanvas, aspectsCanvas, filterCanvas, aspects, buildings, filters); 
    137689        this.values = {}; 
    138690        this.extraComponents = []; 
  • indico/htdocs/roomBooking.py

    r975bdf rf74371  
    3434def mapOfRooms(req, **params): 
    3535    return roomBooking.RHRoomBookingMapOfRooms(req).process(params) 
     36def mapOfRoomsWidget(req, **params): 
     37    return roomBooking.RHRoomBookingMapOfRoomsWidget(req).process(params) 
    3638 
    3739# 2. List of... 
Note: See TracChangeset for help on using the changeset viewer.