update cities example
This commit is contained in:
parent
83776fec5d
commit
27ea70784c
1 changed files with 160 additions and 28 deletions
|
@ -1,21 +1,24 @@
|
|||
/*
|
||||
...
|
||||
In this example, we will build a list of cities that interacts with a map.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Since we will be doing some mapping, we have to load the Geo module.
|
||||
We load the `UI` and `Geo` modules. The latter provides a number of methods to
|
||||
retrieve geographic and political information about countries.
|
||||
*/
|
||||
Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
||||
|
||||
/*
|
||||
We load the list of cities.
|
||||
*/
|
||||
Ox.getJSON('json/cities.json', function(cities) {
|
||||
|
||||
/*
|
||||
The JSON data originally comes from
|
||||
<a href="http://download.geonames.org/export/dump/cities1000.zip">geonames.org</a>.
|
||||
It's an array of 10,000 city objects, each of which has the following
|
||||
properties:
|
||||
<a href="http://download.geonames.org/export/dump"
|
||||
target="_blank">geonames.org</a>. It's an array of 10,000 city objects,
|
||||
each of which has the following properties:
|
||||
<pre>
|
||||
{
|
||||
"country_code": "CN",
|
||||
|
@ -92,6 +95,11 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
};
|
||||
});
|
||||
|
||||
/*
|
||||
The preview button opens or closes the preview dialog (which we will
|
||||
create later). It does so by calling the `openPreview` or `closePreview`
|
||||
method of the list (which we will also create in a moment).
|
||||
*/
|
||||
var $preview = Ox.Button({
|
||||
disabled: true,
|
||||
selectable: true,
|
||||
|
@ -114,6 +122,10 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
.bindEvent({
|
||||
submit: function(data) {
|
||||
$list.options({
|
||||
/*
|
||||
This query will find matches in either `name`,
|
||||
`region` or `continent`.
|
||||
*/
|
||||
query: {
|
||||
conditions: data.value ? [
|
||||
{
|
||||
|
@ -137,20 +149,30 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
});
|
||||
}
|
||||
}),
|
||||
/*
|
||||
The toolbar holds the preview button and the find element.
|
||||
*/
|
||||
$toolbar = Ox.Bar({size: 24})
|
||||
.attr({id: 'toolbar'})
|
||||
.append($preview)
|
||||
.append($find),
|
||||
/*
|
||||
This is our list.
|
||||
*/
|
||||
$list = Ox.TableList({
|
||||
/*
|
||||
First of all, we define the columns.
|
||||
*/
|
||||
columns: [
|
||||
{
|
||||
/*
|
||||
We don't want to display the id, so we omit the
|
||||
visible attribute, which defaults to false. We still
|
||||
have to include the id here, since is the unique key
|
||||
of our table. In consequence, whenever the list
|
||||
fires a select event, it will reference this value
|
||||
as the item's id.
|
||||
`visible` attribute, which defaults to `false`. We
|
||||
still have to include the id in the list of columns,
|
||||
since it's the unique key of our table. In
|
||||
consequence, whenever the list fires a `select`
|
||||
event, it will reference this value as the item's
|
||||
id.
|
||||
*/
|
||||
id: 'id',
|
||||
operator: '+',
|
||||
|
@ -160,9 +182,9 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
},
|
||||
{
|
||||
/*
|
||||
We use the format function to display the region as
|
||||
a colored icon with a tooltip. The region class is
|
||||
added to apply our own custom CSS, and
|
||||
We use the `format` function to display the region
|
||||
as a colored icon with a tooltip. The region class
|
||||
is added to apply our own custom CSS, and
|
||||
Ox.getGeoColor returns a color for the region. Note
|
||||
that the actual value is 'Continent, Region,
|
||||
Country', which results in a nicer sort order than
|
||||
|
@ -188,7 +210,7 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
operator: '+',
|
||||
/*
|
||||
We want the column title to be a symbol, so we pass
|
||||
the 'icon' symbol at a titleImage. We can pick
|
||||
the 'icon' symbol as the `titleImage`. We can pick
|
||||
anything from the collection of symbols that comes
|
||||
with Ox.UI. The column still needs a textual title,
|
||||
to be displayed in the menu that allows to show or
|
||||
|
@ -223,6 +245,9 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
width: 16
|
||||
},
|
||||
{
|
||||
/*
|
||||
If `capital` is `true`, we display a star.
|
||||
*/
|
||||
format: function(value) {
|
||||
return value
|
||||
? Ox.Element({
|
||||
|
@ -248,7 +273,7 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
contains the values of all columns. This allows us
|
||||
to format a value dependent on other values. In this
|
||||
case, we want to display the name in bold if the
|
||||
value for capital is true.
|
||||
value for capital is `true`.
|
||||
*/
|
||||
format: function(value, data) {
|
||||
return data.capital
|
||||
|
@ -283,10 +308,10 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
width: 80
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return Ox.formatDegrees(value, 'lat');
|
||||
},
|
||||
align: 'right',
|
||||
id: 'lat',
|
||||
operator: '-',
|
||||
title: 'Latitude',
|
||||
|
@ -294,10 +319,10 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
width: 80
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return Ox.formatDegrees(value, 'lng');
|
||||
},
|
||||
align: 'right',
|
||||
id: 'lng',
|
||||
operator: '+',
|
||||
title: 'Longitude',
|
||||
|
@ -305,10 +330,10 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
width: 80
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return Ox.formatNumber(value) + ' m';
|
||||
},
|
||||
align: 'right',
|
||||
id: 'elevation',
|
||||
operator: '-',
|
||||
title: 'Elevation',
|
||||
|
@ -327,25 +352,47 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
This makes sure the column titles get displayed
|
||||
*/
|
||||
columnsVisible: true,
|
||||
/*
|
||||
We pass our array of cities of `items`.
|
||||
*/
|
||||
items: cities,
|
||||
/*
|
||||
FIXME: Actually, 'keys' doesn't make so much sense when the
|
||||
API is local, since the list has been passed all data anyway
|
||||
*/
|
||||
keys: ['capital'],
|
||||
/*
|
||||
We don't want to allow simulaneous selection of multiple
|
||||
items, so we set `max` to `1`.
|
||||
*/
|
||||
max: 1,
|
||||
scrollbarVisible: true,
|
||||
/*
|
||||
We have to specify the default sort order.
|
||||
*/
|
||||
sort: ['-population', '+name'],
|
||||
/*
|
||||
When the list retrieves items, it fires an `init` event. By
|
||||
default, this event has an `items` property, which is the
|
||||
number of items. Via `sums`, we can add more properties.
|
||||
In this case, the `init` event will have a `population`
|
||||
property that is the sum of the population of all items.
|
||||
*/
|
||||
sums: ['population']
|
||||
})
|
||||
.bindEvent({
|
||||
/*
|
||||
The `closepreview` event fires when the user presses `space`
|
||||
while preview is active. See `openpreview`, below.
|
||||
*/
|
||||
closepreview: function() {
|
||||
$preview.options({value: false});
|
||||
$dialog.close();
|
||||
},
|
||||
/*
|
||||
On `init`, we display the number of cities and the total
|
||||
population.
|
||||
*/
|
||||
init: function(data) {
|
||||
$status.html(
|
||||
(data.items ? Ox.formatNumber(data.items) : 'No')
|
||||
|
@ -357,21 +404,32 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
)
|
||||
);
|
||||
},
|
||||
/*
|
||||
The `open` event fires when the user doubleclicks an item,
|
||||
or presses `enter` while an item is selected. In this case,
|
||||
we want the map to zoom to the selected place.
|
||||
*/
|
||||
open: function(data) {
|
||||
$map.zoomToPlace();
|
||||
},
|
||||
/*
|
||||
The `openpreview` event fires when an item is selected and
|
||||
the user presses `space`. It can be used to implement
|
||||
functionality similar to the "QuickView" feature in the Mac
|
||||
OS X Finder. In this case, we open a dialog that shows a
|
||||
flag and a map.
|
||||
*/
|
||||
openpreview: function(data) {
|
||||
var item = Ox.getObjectById(cities, data.ids[0]);
|
||||
$flagImage = $('<img>')
|
||||
.attr({
|
||||
src: Ox.getFlagByGeoname(item.country, 256)
|
||||
});
|
||||
$flagImage = $('<img>').attr({
|
||||
src: Ox.getFlagByGeoname(item.country, 256)
|
||||
});
|
||||
$mapImage = Ox.MapImage({
|
||||
height: 256,
|
||||
markers: [item],
|
||||
place: Ox.getCountryByGeoname(item.country),
|
||||
width: 256
|
||||
});
|
||||
height: 256,
|
||||
markers: [item],
|
||||
place: Ox.getCountryByGeoname(item.country),
|
||||
width: 256
|
||||
});
|
||||
setImageSizes();
|
||||
$preview.options({value: true});
|
||||
$dialog.options({
|
||||
|
@ -382,6 +440,14 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
title: [item.name, item.country].join(', ')
|
||||
}).open();
|
||||
},
|
||||
/*
|
||||
The `select` event passes an array of selected ids —
|
||||
either one, as defined above, or none. We enable or disable
|
||||
the preview button accordingly. Then we set the `selected`
|
||||
option of the map to the selected id (or to `undefined`,
|
||||
which will cause a deselect), and pan to that place (which
|
||||
will do nothing if no place is selected).
|
||||
*/
|
||||
select: function(data) {
|
||||
$preview.options({disabled: data.ids.length == 0});
|
||||
$map.options({selected: data.ids[0]}).panToPlace();
|
||||
|
@ -390,6 +456,11 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
$flagImage,
|
||||
$mapImage,
|
||||
$content = Ox.Element(),
|
||||
/*
|
||||
This is the preview dialog. By setting `focus` to `false`, we make
|
||||
it non-modal, i.e. the user can still interact with the rest of the
|
||||
application while the dialog is open.
|
||||
*/
|
||||
$dialog = Ox.Dialog({
|
||||
closeButton: true,
|
||||
content: $content,
|
||||
|
@ -412,15 +483,32 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
setImageSizes();
|
||||
}
|
||||
}),
|
||||
/*
|
||||
The status bar displays the list's totals.
|
||||
*/
|
||||
$status = $('<div>').css({
|
||||
margin: '3px',
|
||||
fontSize: '9px',
|
||||
textAlign: 'center'
|
||||
}),
|
||||
$statusbar = Ox.Bar({size: 16}).append($status),
|
||||
/*
|
||||
Now we create the map.
|
||||
*/
|
||||
$map = Ox.Map({
|
||||
/*
|
||||
When `clickable` is `true`, clicking on the map will perform
|
||||
a reverse geo lookup and select the matching geographic
|
||||
entity.
|
||||
*/
|
||||
clickable: true,
|
||||
keys: ['population'],
|
||||
/*
|
||||
Here, we add custom marker colors and sizes, depending on
|
||||
population. Note that we have to handle `void 0` too, since
|
||||
by clicking on the map, or using the map's find element, the
|
||||
user may select a place that is not one of our cities.
|
||||
*/
|
||||
markerColor: function(place) {
|
||||
return place.population === void 0 ? [128, 128, 128]
|
||||
: place.population >= 10000000 ? [255, 0, 0]
|
||||
|
@ -445,18 +533,35 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
: place.population >= 50000 ? 10
|
||||
: 8;
|
||||
},
|
||||
/*
|
||||
We pass our array of cities of `places`.
|
||||
*/
|
||||
places: cities,
|
||||
/*
|
||||
Finally, we enable a number of interface elements.
|
||||
*/
|
||||
showControls: true,
|
||||
showToolbar: true,
|
||||
showZoombar: true
|
||||
})
|
||||
.bindEvent({
|
||||
/*
|
||||
The `select` event fires when a place is selected or
|
||||
deselected. We set the `selected` option of the list to the
|
||||
selected id, wrapped in an array. (Note that if the selected
|
||||
place is not one of our cities, it will have a temporary id
|
||||
that doesn't exist in our list. Selecting a non-existent id
|
||||
will cause a deselect, which is what we want here.)
|
||||
*/
|
||||
select: function(data) {
|
||||
$list.options({
|
||||
selected: data.place ? [data.place.id] : []
|
||||
});
|
||||
}
|
||||
}),
|
||||
/*
|
||||
The list panel holds the toolbar, the list, and the statusbar.
|
||||
*/
|
||||
$listPanel = Ox.SplitPanel({
|
||||
elements: [
|
||||
{element: $toolbar, size: 24},
|
||||
|
@ -465,9 +570,17 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
],
|
||||
orientation: 'vertical'
|
||||
}),
|
||||
/*
|
||||
The main panel holds the list panel and the map.
|
||||
*/
|
||||
$mainPanel = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
/*
|
||||
Elements of a split panel trigger `resize` and
|
||||
`resizeend` events when they are resized. Here, we
|
||||
make sure that the find element shrinks accordingly.
|
||||
*/
|
||||
element: $listPanel.bindEvent({
|
||||
resize: function(data) {
|
||||
$find.options({
|
||||
|
@ -478,12 +591,23 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
}
|
||||
}),
|
||||
resizable: true,
|
||||
/*
|
||||
The `resize` option is usually `[min, max]`, but by
|
||||
specifying additional values, we make the panel
|
||||
"snappy" at these points. Here, the points are the
|
||||
positions of our list columns.
|
||||
*/
|
||||
resize: [176, 256, 336, 416, 496].map(function(size) {
|
||||
return size + Ox.UI.SCROLLBAR_SIZE;
|
||||
}),
|
||||
size: 416 + Ox.UI.SCROLLBAR_SIZE
|
||||
},
|
||||
{
|
||||
/*
|
||||
The map uses the Google Maps API, which requires a
|
||||
notification when the map size changes. The map's
|
||||
`resizeMap` method takes care of that.
|
||||
*/
|
||||
element: $map.bindEvent({
|
||||
resizeend: $map.resizeMap
|
||||
})
|
||||
|
@ -493,6 +617,10 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
})
|
||||
.appendTo(Ox.$body);
|
||||
|
||||
/*
|
||||
Helper function that sets the flag and map image sizes when the preview
|
||||
dialog is initialized, or resized.
|
||||
*/
|
||||
function setImageSizes() {
|
||||
var size = Math.floor(($dialog.options('width') - 64) / 2);
|
||||
[$flagImage, $mapImage].forEach(function($image) {
|
||||
|
@ -500,6 +628,10 @@ Ox.load({UI: {showScreen: true}, Geo: {}}, function() {
|
|||
});
|
||||
}
|
||||
|
||||
/*
|
||||
When the window size changes, the map size changes too, so we have to
|
||||
notify the map.
|
||||
*/
|
||||
Ox.$window.bind({resize: $map.resizeMap});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue