| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| var $ = require('../util/jquery'); |
| var defineClass = require('../util/define-class'); |
| var strings = require('../strings').currentLocale; |
| |
| function markerIcon(opts) { |
| if (opts.icon) { |
| return 'http://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=' + |
| opts.icon + '|' + opts.color; |
| } else { |
| return 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=' + |
| (opts.label || '•') + '|' + opts.color; |
| } |
| } |
| |
| var DestinationMarker = defineClass({ |
| statics: { |
| color: { |
| RED: 'FC6355', |
| ORANGE: 'FF8000', //TODO(rosswang): tune |
| YELLOW: 'FFFF00', //TODO(rosswang): tune |
| GREEN: '00E73D', |
| LIGHT_BLUE: '8080FF', //TODO(rosswang): tune |
| BLUE: '7090FC', // originally '5781FC', |
| PURPLE: '8000FF', //TODO(rosswang): tune |
| PINK: 'FF8080' //TODO(rosswang): tune |
| }, |
| |
| icon: { |
| ORIGIN: 'home', |
| DESTINATION: 'flag' |
| } |
| }, |
| |
| publics: { |
| clear: function() { |
| this.marker.setMap(null); |
| this.onClear(); |
| }, |
| |
| pushClient: function(client, color, update) { |
| this.clients.push($.extend({}, this.topClient(), { |
| client: client, |
| color: color, |
| listeners: [] |
| })); |
| if (update !== false) { |
| this.updateIcon(); |
| this.updateTitle(); |
| } |
| }, |
| |
| /** |
| * Flip the top two clients, to deprioritize a low-priority client that was |
| * just pushed. |
| */ |
| deprioritizeClient: function() { |
| if (this.clients.length > 1) { |
| var demoted = this.topClient(); |
| this.clients.splice(--this.clients.length - 1, 0, demoted); |
| } |
| |
| this.updateIcon(); |
| this.updateTitle(); |
| }, |
| |
| removeClient: function(client) { |
| var onClick = this.onClick; |
| this.clients = this.clients.filter(function(entry) { |
| var match = entry.client === client; |
| if (match) { |
| $.each(entry.listeners, function() { |
| onClick.remove(this); |
| }); |
| } |
| return !match; |
| }); |
| |
| this.refreshClickability(); |
| |
| if (!this.clients.length) { |
| this.clear(); |
| } else { |
| this.updateIcon(); |
| this.updateTitle(); |
| } |
| }, |
| |
| getClient: function() { |
| return this.topClient().client; |
| }, |
| |
| hasClient: function(client) { |
| for (var i = 0; i < this.clients.length; i++) { |
| if (client === this.clients[0].client) { |
| return true; |
| } |
| } |
| return false; |
| }, |
| |
| setColor: function(color) { |
| this.topClient().color = color; |
| this.updateIcon(); |
| }, |
| |
| setDestinationLabel: function(destinationLabel) { |
| this.topClient().destinationLabel = destinationLabel; |
| this.updateTitle(); |
| }, |
| |
| setLabel: function(label) { |
| var client = this.topClient(); |
| client.label = label; |
| client.icon = null; |
| this.updateIcon(); |
| }, |
| |
| restrictListenerToClient: function(callback, client) { |
| var self = this; |
| client = client || this.getClient(); |
| return function() { |
| if (self.getClient() === client) { |
| return callback.apply(this, arguments); |
| } |
| }; |
| }, |
| |
| setIcon: function(icon) { |
| var client = this.topClient(); |
| client.icon = icon; |
| client.label = null; |
| this.updateIcon(); |
| } |
| }, |
| |
| privates: { |
| refreshClickability: function() { |
| this.marker.setClickable(this.onClick.has()); |
| }, |
| |
| topClient: function() { |
| return this.clients[this.clients.length - 1]; |
| }, |
| |
| getIcon: function() { |
| return markerIcon(this.topClient()); |
| }, |
| |
| updateIcon: function() { |
| this.marker.setIcon(this.getIcon()); |
| }, |
| |
| updateTitle: function() { |
| var destLabel = this.topClient().destinationLabel; |
| this.marker.setTitle(destLabel? |
| strings.label(this.topClient().destinationLabel, this.title) : |
| this.title); |
| } |
| }, |
| |
| events: [ 'onClick', 'onClear' ], |
| constants: [ 'marker', 'place' ], |
| |
| /** |
| * A note on clients: destination markers can be shared between multiple use |
| * cases, ex. search and multiple actual destination associations. A marker |
| * is removed when all of its clients have been removed. The latest client |
| * determines the color of the marker. Click event handlers are added per |
| * client, unless they're added as global (a second argument to |
| * `Callbacks.add`); all remain active while their client is registered, but |
| * when the client is removed the corresponding click handlers are removed as |
| * well. |
| */ |
| init: function(maps, map, place, client, color) { |
| var self = this; |
| |
| this.map = map; |
| this.place = place; |
| this.clients = [{ client: client, color: color, listeners: [] }]; |
| |
| this.icon = null; |
| this.label = ''; |
| |
| this.title = place.getName(); |
| |
| this.marker = new maps.Marker({ |
| icon: this.getIcon(), |
| map: map, |
| place: place.getPlaceObject(), |
| title: this.title, |
| clickable: false |
| }); |
| |
| /* Override onClick.add to keep a record of which listeners are bound to |
| * which clients to remove listeners on client removal. This does not |
| * however implicitly restrict such listeners; that must be left to the |
| * caller so as to allow the caller to later pre-emptively remove the |
| * listener if desired. */ |
| defineClass.decorate(this.onClick, 'add', function(listener, global) { |
| if (!global) { |
| /* Per jQuery, listener can also be an array; however, there seems to |
| * be a bug in jQuery at this time where remove will not remove arrays, |
| * only individual functions, so let's flatten here. */ |
| var listeners = self.topClient().listeners; |
| if ($.isArray(listener)) { |
| $.each(listener, function() { |
| listeners.push(this); |
| }); |
| } else { |
| listeners.push(listener); |
| } |
| } |
| }, function() { |
| self.refreshClickability(); |
| }); |
| |
| maps.event.addListener(this.marker, 'click', this.onClick); |
| } |
| }); |
| |
| module.exports = DestinationMarker; |