Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hatnote/monumental
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 17.04.01
Choose a base ref
...
head repository: hatnote/monumental
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on May 11, 2017

  1. Copy the full SHA
    53be451 View commit details
  2. Copy the full SHA
    9872416 View commit details

Commits on May 12, 2017

  1. feat(list): map optimization

    Paweł Marynowski committed May 12, 2017
    Copy the full SHA
    b018010 View commit details

Commits on May 14, 2017

  1. Copy the full SHA
    1c06717 View commit details

Commits on May 16, 2017

  1. Copy the full SHA
    acdd635 View commit details

Commits on May 17, 2017

  1. style(monument): code lint

    yarl committed May 17, 2017
    Copy the full SHA
    b739a5d View commit details

Commits on May 25, 2017

  1. Copy the full SHA
    fcbf638 View commit details
  2. fix(list): fix popup behaviour

    yarl committed May 25, 2017
    Copy the full SHA
    fd37b73 View commit details
  3. Copy the full SHA
    b404b31 View commit details
  4. feat(main): add quick filter

    yarl committed May 25, 2017
    Copy the full SHA
    96389d7 View commit details
  5. Copy the full SHA
    7b2224d View commit details
  6. feat(list): styling quick filter

    yarl committed May 25, 2017
    Copy the full SHA
    62a25dc View commit details
  7. Copy the full SHA
    1189c29 View commit details

Commits on May 29, 2017

  1. Copy the full SHA
    a0590b3 View commit details
  2. fix(toolbar): change monument/list detection

    because of Berlin
    yarl committed May 29, 2017
    Copy the full SHA
    2e837a0 View commit details
  3. fix(dashboard): add credit line

    yarl committed May 29, 2017
    Copy the full SHA
    5f02767 View commit details
  4. feat(list): adjustments

    yarl committed May 29, 2017
    Copy the full SHA
    06363aa View commit details
  5. Copy the full SHA
    5658f3c View commit details
  6. fix(app): add missing params

    yarl committed May 29, 2017
    Copy the full SHA
    05c2b6f View commit details
  7. Copy the full SHA
    4c258ea View commit details
  8. version 17.05.01

    yarl committed May 29, 2017
    Copy the full SHA
    f900ccc View commit details
  9. Merge pull request #15 from hatnote/dev

    version 17.05.01
    yarl authored May 29, 2017
    Copy the full SHA
    85e4979 View commit details
  10. Copy the full SHA
    680f614 View commit details
  11. Copy the full SHA
    a880edd View commit details
  12. Copy the full SHA
    9143af4 View commit details
  13. Copy the full SHA
    288bf89 View commit details
  14. version 17.05.02

    yarl committed May 29, 2017
    Copy the full SHA
    4f7d652 View commit details
  15. Merge pull request #16 from hatnote/dev

    version 17.05.02
    yarl authored May 29, 2017
    Copy the full SHA
    2c0b384 View commit details

Commits on Jul 13, 2017

  1. Copy the full SHA
    fa11ccb View commit details

Commits on Jul 16, 2017

  1. Copy the full SHA
    5c3789a View commit details

Commits on Jul 17, 2017

  1. Copy the full SHA
    60f409d View commit details

Commits on Jul 22, 2017

  1. Copy the full SHA
    5567da3 View commit details
  2. feat(services): add text service

    yarl committed Jul 22, 2017
    Copy the full SHA
    0f014b2 View commit details
  3. Copy the full SHA
    6d65b4d View commit details

Commits on Jul 27, 2017

  1. Copy the full SHA
    4615e8d View commit details
  2. Copy the full SHA
    2676610 View commit details
  3. Copy the full SHA
    d8ea9ae View commit details
  4. Copy the full SHA
    ec445f7 View commit details
  5. Copy the full SHA
    1258a5e View commit details
  6. feat(app): add library as type

    yarl committed Jul 27, 2017
    Copy the full SHA
    89ef7c8 View commit details
  7. Copy the full SHA
    013819d View commit details
  8. Copy the full SHA
    bab3921 View commit details
  9. Copy the full SHA
    b470ba2 View commit details
  10. Copy the full SHA
    64a4d5e View commit details
  11. feat(services): add China

    yarl committed Jul 27, 2017
    Copy the full SHA
    6813aec View commit details
  12. feat(monument): add editing

    yarl committed Jul 27, 2017
    Copy the full SHA
    1b637a2 View commit details
  13. Copy the full SHA
    ac07558 View commit details
  14. fix(list): fixes

    yarl committed Jul 27, 2017
    Copy the full SHA
    a2d2b7c View commit details
  15. version 17.07

    yarl committed Jul 27, 2017
    Copy the full SHA
    f923d6e View commit details
  16. Merge pull request #17 from hatnote/dev

    version 17.07
    yarl authored Jul 27, 2017
    Copy the full SHA
    0476338 View commit details
Showing with 2,358 additions and 649 deletions.
  1. +2 −0 .gitignore
  2. +0 −5 config.default.yaml
  3. +0 −27 monumental/README.md
  4. +0 −200 monumental/server.py
  5. +3 −3 package.json
  6. +0 −11 requirements.txt
  7. +6 −0 src/components/index.js
  8. +92 −53 src/components/main/dashboard/dashboard.html
  9. +57 −1 src/components/main/dashboard/dashboard.js
  10. +5 −1 src/components/main/dashboard/dashboard.scss
  11. +89 −52 src/components/main/list/list.html
  12. +286 −70 src/components/main/list/list.js
  13. +82 −11 src/components/main/list/list.scss
  14. +9 −0 src/components/main/list/popup.tpl.html
  15. +83 −7 src/components/main/map/map.html
  16. +133 −36 src/components/main/map/map.js
  17. +95 −3 src/components/main/map/map.scss
  18. +27 −5 src/components/main/monument/components/location.js
  19. +7 −7 src/components/main/monument/components/native-name.js
  20. +50 −0 src/components/main/monument/components/property-coord.js
  21. +222 −0 src/components/main/monument/components/property-item.js
  22. +163 −0 src/components/main/monument/components/property-quantity.js
  23. +10 −4 src/components/main/monument/components/url.js
  24. +241 −36 src/components/main/monument/monument.html
  25. +235 −16 src/components/main/monument/monument.js
  26. +103 −3 src/components/main/monument/monument.scss
  27. +8 −5 src/components/toolbar/toolbar.js
  28. BIN src/images/marker-green.png
  29. BIN src/images/marker-red.png
  30. +8 −5 src/index.js
  31. +3 −1 src/index_dev.ejs
  32. +3 −1 src/index_local.ejs
  33. +3 −0 src/services/index.js
  34. +2 −1 src/services/lang.service.js
  35. +32 −7 src/services/map.service.js
  36. +34 −0 src/services/text.service.js
  37. +156 −3 src/services/wiki.service.js
  38. +21 −72 src/services/wikidata.service.js
  39. +4 −0 src/styles/_general.scss
  40. +8 −0 src/styles/_map.scss
  41. +42 −0 src/styles/_spinner.scss
  42. +31 −0 src/styles/_upload.scss
  43. +3 −3 src/styles/style.scss
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
node_modules
app
static
logs

.vscode
jsconfig.json

config.hatnote.yaml
config.local.yaml
config.labs.yaml
*.*~
5 changes: 0 additions & 5 deletions config.default.yaml

This file was deleted.

27 changes: 0 additions & 27 deletions monumental/README.md

This file was deleted.

200 changes: 0 additions & 200 deletions monumental/server.py

This file was deleted.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "monumental",
"version": "17.04.01",
"version": "17.09.01",
"private": true,
"description": "Monumental app",
"main": "src/index.js",
@@ -59,14 +59,14 @@
"angular": "1.5.9",
"angular-animate": "1.5.9",
"angular-aria": "1.5.9",
"angular-file-upload": "^2.3.4",
"angular-file-upload": "^2.5.0",
"angular-leaflet-directive": "^0.10.0",
"angular-local-storage": "^0.5.0",
"angular-material": "^1.0.0",
"angular-messages": "1.5.9",
"angular-sanitize": "1.5.9",
"angular-swipe": "^0.4.0",
"angular-ui-router": "*",
"angular-ui-router": "0.4.2",
"babel-polyfill": "^6.16.0",
"babel-runtime": "^6.23.0",
"leaflet": "^1.0.2",
11 changes: 0 additions & 11 deletions requirements.txt

This file was deleted.

6 changes: 6 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,9 @@ import map from './main/map/map';
import monument from './main/monument/monument';
import location from './main/monument/components/location';
import nativeName from './main/monument/components/native-name';
import propertyCoord from './main/monument/components/property-coord';
import propertyItem from './main/monument/components/property-item';
import propertyQuantity from './main/monument/components/property-quantity';
import url from './main/monument/components/url';

import toolbar from './toolbar/toolbar';
@@ -22,6 +25,9 @@ export default () => {
monument();
location();
nativeName();
propertyCoord();
propertyItem();
propertyQuantity();
url();

toolbar();
145 changes: 92 additions & 53 deletions src/components/main/dashboard/dashboard.html
Original file line number Diff line number Diff line change
@@ -3,13 +3,48 @@
<div class="dashboard" layout="column" layout-align="start stretch">
<div layout="column" layout-align="start stretch">
<div class="dashboard__box" flex>
<div class="big">Monumental is a web application which displays information about cultural heritage monuments based on data gathered on <a href="//wikidata.org">Wikidata</a>, <a href="//wikipedia.org">Wikipedia</a> and <a href="//commons.wikimedia.org">Wikimedia Commons</a>. Tool is created as a part of <a href="//meta.wikimedia.org/wiki/Connected_Open_Heritage">Connected Open Heritage</a> project by <a href="//se.wikimedia.org">Wikimedia Sweden</a> and <a href="//www.wikilovesmonuments.org/">Wiki Loves Monuments</a> initiative.</div>
<div class="big">
Welcome to Monumental, the tool that allows you to explore built heritage and learn more about monuments.
</div>
<p>
Enter the name of a monument in the search box above (for example, 'Empire State Building') to learn more or explore heritage
by entering a geographic region (for example, 'Berlin'). You can also click an example monument or city below.
</p>
<p>
The tool displays information and multimedia about cultural heritage monuments gathered through <a href="//wikidata.org">Wikidata</a>,
<a href="//wikipedia.org">Wikipedia</a> and <a href="//commons.wikimedia.org">Wikimedia Commons</a>. It is created as a part of
the <a href="//meta.wikimedia.org/wiki/Connected_Open_Heritage">Connected Open Heritage</a> project by
<a href="//se.wikimedia.org">Wikimedia Sweden</a> and <a href="//www.wikilovesmonuments.org/">Wiki Loves Monuments</a> initiative.
</p>
</div>

<div class="dashboard__box" flex>
<h4 class="md-title">Monuments nearby</h4>
<p ng-if="!$ctrl.nearby">Enable localization to see monuments nearby</p>
<p ng-if="$ctrl.nearby && !$ctrl.nearby">No monuments nearby</p>
<p ng-if="$ctrl.nearby === 'error'">Error getting monuments nearby</p>
<md-card ng-repeat="example in $ctrl.nearby" ui-sref="main.object({id: example.id})">
<div class="md-card-image"
layout="row" layout-align="center center"
ng-init="$ctrl.getImage(example)">
<img ng-if="example.image"
load-src="{{ ::example.image.thumburl }}"
title="Uploaded by: {{ ::example.image.user }}">
</div>
<md-card-title>
<md-card-title-text>
<a>{{ ::example.name }}</a>
<small class="muted">{{ ::example.distance | number }} km away</small>
</md-card-title-text>
</md-card-title>
</md-card>
</div>

<div class="dashboard__box" flex>
<h4 class="md-title">Example monuments</h4>
<md-card ng-repeat="example in $ctrl.examples" ui-sref="main.object({id: example.id})">
<div class="md-card-image" layout="row" layout-align="center center">
<img load-src="{{ ::example.img }}">
<img load-src="{{ ::example.img }}" title="Credit: {{ ::example.credit }}">
</div>
<md-card-title>
<md-card-title-text>
@@ -19,65 +54,69 @@ <h4 class="md-title">Example monuments</h4>
</md-card>
</div>
<div class="dashboard__box" flex>
<h4 class="md-title">Example list of monuments</h4>
<h4 class="md-title">Explore a city</h4>
<div>
<a ui-sref="main.list({id: '1492'})">Barcelona</a>&nbsp;·
<a ui-sref="main.list({id: '64'})">Berlin</a>&nbsp;·
<a ui-sref="main.list({id: '2256'})">Birmingham</a>&nbsp;·
<a ui-sref="main.list({id: '1780'})">Bratislava</a>&nbsp;·
<a ui-sref="main.list({id: '24879'})">Bremen</a>&nbsp;·
<a ui-sref="main.list({id: '239'})">Brussels</a>&nbsp;·
<a ui-sref="main.list({id: '19660'})">Bucharest</a>&nbsp;·
<a ui-sref="main.list({id: '1781'})">Budapest</a>&nbsp;·
<a ui-sref="main.list({id: '365'})">Cologne</a>&nbsp;·
<a ui-sref="main.list({id: '1295'})">Dortmund</a>&nbsp;·
<a ui-sref="main.list({id: '1731'})">Dresden</a>&nbsp;·
<a ui-sref="main.list({id: '1718'})">Düsseldorf</a>&nbsp;·
<a ui-sref="main.list({id: '2066'})">Essen</a>&nbsp;·
<a ui-sref="main.list({id: '1794'})">Frankfurt</a>&nbsp;·
<a ui-sref="main.list({id: '52502'})">Gothenburg</a>&nbsp;·
<a ui-sref="main.list({id: '9920'})">Haarlem</a>&nbsp;·
<a ui-sref="main.list({id: '1055'})">Hamburg</a>&nbsp;·
<a ui-sref="main.list({id: '1715'})">Hanover</a>&nbsp;·
<a ui-sref="main.list({id: '1757'})">Helsinki</a>&nbsp;·
<a ui-sref="main.list({id: '31487'})">Kraków</a>&nbsp;·
<a ui-sref="main.list({id: '1899'})">Kyiv</a>&nbsp;·
<a ui-sref="main.list({id: '39121'})">Leeds</a>&nbsp;·
<a ui-sref="main.list({id: '2079'})">Leipzig</a>&nbsp;·
<a ui-sref="main.list({id: '597'})">Lisbon</a>&nbsp;·
<a ui-sref="main.list({id: '580'})">Łódź</a>&nbsp;·
<a ui-sref="main.list({id: '2807'})">Madrid</a>&nbsp;·
<a ui-sref="main.list({id: '23482'})">Marseille</a>&nbsp;·
<a ui-sref="main.list({id: '490'})">Milan</a>&nbsp;·
<a ui-sref="main.list({id: '1726'})">Munich</a>&nbsp;·
<a ui-sref="main.list({id: '8851'})">Málaga</a> &nbsp;·
<a ui-sref="main.list({id: '2090'})">Nuremberg</a>&nbsp;·
<a ui-sref="main.list({id: '90'})">Paris</a>&nbsp;·
<a ui-sref="main.list({id: '268'})">Poznań</a>&nbsp;·
<a ui-sref="main.list({id: '1085'})">Prague</a>&nbsp;·
<a ui-sref="main.list({id: '1773'})">Riga</a>&nbsp;·
<a ui-sref="main.list({id: '220'})">Rome</a>&nbsp;·
<a ui-sref="main.list({id: '2680952'})">Rotterdam</a>&nbsp;·
<a ui-sref="main.list({id: '8717'})">Seville</a>&nbsp;·
<a ui-sref="main.list({id: '42448'})">Sheffield</a>&nbsp;·
<a ui-sref="main.list({id: '506250'})">Stockholm</a>&nbsp;·
<a ui-sref="main.list({id: '1022'})">Stuttgart</a>&nbsp;·
<a ui-sref="main.list({id: '495'})">Turin</a>&nbsp;·
<a ui-sref="main.list({id: '8818'})">Valencia</a>&nbsp;·
<a ui-sref="main.list({id: '1741'})">Vienna</a>&nbsp;·
<a ui-sref="main.list({id: '216'})">Vilnius</a>&nbsp;·
<a ui-sref="main.list({id: '270'})">Warsaw</a>&nbsp;·
<a ui-sref="main.list({id: '1799'})">Wrocław</a>&nbsp;·
<a ui-sref="main.list({id: '10305'})">Zaragoza</a>
<a ui-sref="main.list({id: '1492', heritage: '1'})">Barcelona</a>&nbsp;·
<a ui-sref="main.list({id: '64', heritage: '1'})">Berlin</a>&nbsp;·
<a ui-sref="main.list({id: '2256', heritage: '1'})">Birmingham</a>&nbsp;·
<a ui-sref="main.list({id: '1780', heritage: '1'})">Bratislava</a>&nbsp;·
<a ui-sref="main.list({id: '24879', heritage: '1'})">Bremen</a>&nbsp;·
<a ui-sref="main.list({id: '239', heritage: '1'})">Brussels</a>&nbsp;·
<a ui-sref="main.list({id: '19660', heritage: '1'})">Bucharest</a>&nbsp;·
<a ui-sref="main.list({id: '1781', heritage: '1'})">Budapest</a>&nbsp;·
<a ui-sref="main.list({id: '365', heritage: '1'})">Cologne</a>&nbsp;·
<a ui-sref="main.list({id: '1295', heritage: '1'})">Dortmund</a>&nbsp;·
<a ui-sref="main.list({id: '1731', heritage: '1'})">Dresden</a>&nbsp;·
<a ui-sref="main.list({id: '1718', heritage: '1'})">Düsseldorf</a>&nbsp;·
<a ui-sref="main.list({id: '2066', heritage: '1'})">Essen</a>&nbsp;·
<a ui-sref="main.list({id: '1794', heritage: '1'})">Frankfurt</a>&nbsp;·
<a ui-sref="main.list({id: '52502', heritage: '1'})">Gothenburg</a>&nbsp;·
<a ui-sref="main.list({id: '9920', heritage: '1'})">Haarlem</a>&nbsp;·
<a ui-sref="main.list({id: '1055', heritage: '1'})">Hamburg</a>&nbsp;·
<a ui-sref="main.list({id: '1715', heritage: '1'})">Hanover</a>&nbsp;·
<a ui-sref="main.list({id: '1757', heritage: '1'})">Helsinki</a>&nbsp;·
<a ui-sref="main.list({id: '31487', heritage: '1'})">Kraków</a>&nbsp;·
<a ui-sref="main.list({id: '1899', heritage: '1'})">Kyiv</a>&nbsp;·
<a ui-sref="main.list({id: '39121', heritage: '1'})">Leeds</a>&nbsp;·
<a ui-sref="main.list({id: '2079', heritage: '1'})">Leipzig</a>&nbsp;·
<a ui-sref="main.list({id: '597', heritage: '1'})">Lisbon</a>&nbsp;·
<a ui-sref="main.list({id: '580', heritage: '1'})">Łódź</a>&nbsp;·
<a ui-sref="main.list({id: '2807', heritage: '1'})">Madrid</a>&nbsp;·
<a ui-sref="main.list({id: '23482', heritage: '1'})">Marseille</a>&nbsp;·
<a ui-sref="main.list({id: '490', heritage: '1'})">Milan</a>&nbsp;·
<a ui-sref="main.list({id: '1726', heritage: '1'})">Munich</a>&nbsp;·
<a ui-sref="main.list({id: '8851', heritage: '1'})">Málaga</a> &nbsp;·
<a ui-sref="main.list({id: '2090', heritage: '1'})">Nuremberg</a>&nbsp;·
<a ui-sref="main.list({id: '90', heritage: '1'})">Paris</a>&nbsp;·
<a ui-sref="main.list({id: '268', heritage: '1'})">Poznań</a>&nbsp;·
<a ui-sref="main.list({id: '1085', heritage: '1'})">Prague</a>&nbsp;·
<a ui-sref="main.list({id: '1773', heritage: '1'})">Riga</a>&nbsp;·
<a ui-sref="main.list({id: '220', heritage: '1'})">Rome</a>&nbsp;·
<a ui-sref="main.list({id: '2680952', heritage: '1'})">Rotterdam</a>&nbsp;·
<a ui-sref="main.list({id: '8717', heritage: '1'})">Seville</a>&nbsp;·
<a ui-sref="main.list({id: '42448', heritage: '1'})">Sheffield</a>&nbsp;·
<a ui-sref="main.list({id: '506250', heritage: '1'})">Stockholm</a>&nbsp;·
<a ui-sref="main.list({id: '1022', heritage: '1'})">Stuttgart</a>&nbsp;·
<a ui-sref="main.list({id: '495', heritage: '1'})">Turin</a>&nbsp;·
<a ui-sref="main.list({id: '8818', heritage: '1'})">Valencia</a>&nbsp;·
<a ui-sref="main.list({id: '1741', heritage: '1'})">Vienna</a>&nbsp;·
<a ui-sref="main.list({id: '216', heritage: '1'})">Vilnius</a>&nbsp;·
<a ui-sref="main.list({id: '270', heritage: '1'})">Warsaw</a>&nbsp;·
<a ui-sref="main.list({id: '1799', heritage: '1'})">Wrocław</a>&nbsp;·
<a ui-sref="main.list({id: '10305', heritage: '1'})">Zaragoza</a>
</div>
</div>
<div class="dashboard__box" flex>
<h4 class="md-title">More</h4>
<div layout="column" layout-align="start stretch">
<p layout="column" layout-align="start stretch">
<a ui-sref="main.list({id: '15', heritage: '1'})">all monuments in Africa</a>
<a ui-sref="main.list({id: '46', c: '50.7920:14.8975:4', heritage: '9259'})">World Heritage Sites in Europe</a>
</p>
<p layout="column" layout-align="start stretch">
<strong>Map</strong>
<a href="#/map?c=51.5075:-0.0748:16">monuments nearby Tower of London</a>
<a href="#/map?c=48.8583:2.2944:16">monuments nearby the Eiffel Tower</a>
<a ui-sref="main.map({c: '51.5075:-0.0748:16', heritage: '1'})">monuments nearby Tower of London</a>
<a ui-sref="main.map({c: '48.8583:2.2944:16', heritage: '1'})">monuments nearby the Eiffel Tower</a>
</p>
<p layout="column" layout-align="start stretch">
<strong>Games</strong>
58 changes: 57 additions & 1 deletion src/components/main/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -5,8 +5,11 @@ import pack from '../../../../package.json';

const DashboardComponent = { controller, template };

function controller($filter, $mdToast, $state, $window, WikiService, langService) {
function controller($filter, $mdToast, $state, $window, WikiService, langService, wikidata) {
const vm = this;
const langs = langService.getUserLanguages();

vm.getImage = getImage;
vm.lang = {};
vm.languagesList = langService.getLanguagesList();
vm.languages = langService.getUserLanguages();
@@ -25,60 +28,113 @@ function controller($filter, $mdToast, $state, $window, WikiService, langService
package: pack,
};

getNearestMonuments();

vm.examples = [
{
name: 'Empire State Building',
id: 9188,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Empire_State_Building_from_the_Top_of_the_Rock.jpg/640px-Empire_State_Building_from_the_Top_of_the_Rock.jpg',
credit: 'Jiuguang Wang / CC BY-SA 2.0',
},
{
name: 'Katedra Wawelska',
id: 638519,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/292_Krakow_Katedra_na_Wawelu_20070805.jpg/640px-292_Krakow_Katedra_na_Wawelu_20070805.jpg',
credit: 'Jakub Hałun / CC BY-SA 3.0',
},
{
name: 'Church of the Holy Sepulchre',
id: 187702,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/01/The_Church_of_the_Holy_Sepulchre-Jerusalem.JPG/640px-The_Church_of_the_Holy_Sepulchre-Jerusalem.JPG',
credit: '@jlascar/flickr / CC BY 2.0',
},
{
name: 'Buckingham Palace',
id: 42182,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Buckingham_Palace%2C_London_-_April_2009.jpg/640px-Buckingham_Palace%2C_London_-_April_2009.jpg',
credit: 'Diliff / CC BY-SA 3.0',
},
{
name: 'Sydney Opera House',
id: 45178,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Sydney_Opera_House_Sails.jpg/640px-Sydney_Opera_House_Sails.jpg',
credit: 'Enochlau / CC BY-SA 3.0',
},
{
name: 'Lincoln Memorial',
id: 213559,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/7/78/Aerial_view_of_Lincoln_Memorial_-_east_side_EDIT.jpeg/640px-Aerial_view_of_Lincoln_Memorial_-_east_side_EDIT.jpeg',
credit: 'Carol M. Highsmith / upstateNYer / PD',
},
{
name: 'Brandenburger Tor',
id: 82425,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/Brandenburg_Gate_%288331820462%29.jpg/640px-Brandenburg_Gate_%288331820462%29.jpg',
credit: 'Steve Collis / CC BY 2.0',
},
{
name: 'Pałac Kultury i Nauki',
id: 167566,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/PKiN_widziany_z_WFC.jpg/640px-PKiN_widziany_z_WFC.jpg',
credit: 'Nnb / GFDL',
},
{
name: 'Tour Eiffel',
id: 243,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/640px-Tour_Eiffel_Wikimedia_Commons.jpg',
credit: 'Benh LIEU SONG / CC BY-SA 3.0',
},
{
name: 'Duomo di Milano',
id: 18068,
img: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/876MilanoDuomo.JPG/640px-876MilanoDuomo.JPG',
credit: 'MarkusMark / CC BY-SA 3.0',
},
];
}

function geolocSuccess(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
const request = wikidata.getSPARQL(`SELECT ?place ?placeLabel ?dist (SAMPLE(?image) AS ?image)
WHERE
{
SERVICE wikibase:around {
?place wdt:P625 ?location .
bd:serviceParam wikibase:center "Point(${longitude} ${latitude})"^^geo:wktLiteral .
bd:serviceParam wikibase:radius "10" .
}
?place wdt:P1435 ?monument .
OPTIONAL { ?place wdt:P18 ?image . }
SERVICE wikibase:label { bd:serviceParam wikibase:language "${langs.map(lang => lang.code).join(',')}" }
BIND(geof:distance("Point(${longitude} ${latitude})"^^geo:wktLiteral, ?location) as ?dist)
}
GROUP BY ?place ?placeLabel ?dist
ORDER BY ?dist`);
request.then((data) => {
vm.nearby = data.slice(0, 15).map(item => ({
id: item.place.value.substring(32),
name: item.placeLabel.value,
imageName: item.image ? item.image.value.substring(51) : undefined,
distance: item.dist.value,
}));
});
}

function getImage(item) {
if (!item.imageName) { return; }
WikiService.getImage(decodeURIComponent(item.imageName), { iiurlwidth: 640 }).then((response) => {
item.image = response.imageinfo;
});
}

function getNearestMonuments() {
navigator.geolocation.getCurrentPosition(geolocSuccess, () => {
vm.nearby = 'error';
});
}

function saveUserLanguages() {
langService.setUserLanguages(vm.languages)
.then(() => {
6 changes: 5 additions & 1 deletion src/components/main/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -14,6 +14,10 @@ mo-dashboard {
border-radius: 2px;
padding: 20px;

.big {
margin-bottom: 10px;
}

.md-title {
margin: 0;
margin-bottom: 15px;
@@ -54,7 +58,7 @@ mo-dashboard {

md-card-title {
padding: 10px 15px;
height: 42px;
min-height: 42px;
}
}
}
141 changes: 89 additions & 52 deletions src/components/main/list/list.html
Original file line number Diff line number Diff line change
@@ -1,47 +1,35 @@
<div class="container" layout="column" layout-align="start stretch" flex>
<div layout="column" layout-align="start stretch" flex="none">
<div class="list__header" layout="row" layout-align="center start">
<div flex="none" hide-xs>
<div class="header__image" ng-if="$ctrl.image.length">
<a class="header__image-link" layout="row" layout-align="center center"
ng-repeat="image in ::$ctrl.image"
ng-if="$first"
ng-href="{{ ::image.descriptionurl }}">
<img ng-src="{{ ::image.thumburl }}">
</a>
</div>
<div class="header__image" ng-if="!$ctrl.image.length">
<div class="header__image-link" layout="row" layout-align="center center">
<md-icon>close</md-icon>
</div>
</div>
</div>
<div layout="column" layout-align="start stretch" flex
ng-if="$ctrl.place"
ng-show="$ctrl.mobile.fullHeader">
<div class="header__primary" layout="row" layout-align="center start">
<div flex>
<div layout="row" layout-align="start end" flex>
<h2 class="header__title md-display-1"
ng-if="!$ctrl.place.labels[$ctrl.lang.code]">
<em>missing label in {{ ::$ctrl.lang.name }}</em>
</h2>
<h2 class="header__title md-display-1"
ng-if="$ctrl.place.labels[$ctrl.lang.code]">
{{ ::$ctrl.place.labels[$ctrl.lang.code] }}
{{ ::$ctrl.place.labels[$ctrl.lang.code].value }}
</h2>
<h4 class="header__subtitle md-headline muted"><mo-native-name monument="$ctrl.place"></mo-native-name></h4>
</div>
<div flex="none">
<md-button class="md-icon-button" aria-label="Switch to map view"
ng-disabled="$ctrl.map.center.zoom < 9"
ui-sref="main.map($ctrl.filter)">
<md-icon>map</md-icon>
<md-tooltip>Switch to map view</md-tooltip>
</md-button>
<md-button class="md-icon-button md-primary" aria-label="Open in Wikidata"
ng-href="//wikidata.org/wiki/{{ ::$ctrl.place.id }}" target="_blank">
<md-icon md-svg-icon="assets/images/barcode.svg"></md-icon>
<md-tooltip>Show Wikidata entry</md-tooltip>
</md-button>
</div>
</div>
<div class="header__secondary">
<mo-location class="header__place" monument="::$ctrl.place"></mo-location>
</div>
</div>
<div layout="row" layout-align="start center" flex
ng-if="$ctrl.place"
@@ -63,17 +51,60 @@ <h4 class="header__subtitle md-headline">{{ ::$ctrl.place.labels[$ctrl.lang] ||
<div class="list__subheader"
layout="row" layout-align="start center"
ng-if="$ctrl.mobile.fullHeader">
<!--<md-input-container md-no-float>
<input type="text" ng-model="$ctrl.filters.name" placeholder="Name">
</md-input-container>-->
<span ng-if="$ctrl.showMap">{{$ctrl.list.length}} results</span>
<span ng-if="!$ctrl.showMap">Loading...</span>
<div class="list__quick-filter" layout="row" layout-align="start center">
<md-input-container>
<label>Quick filter</label>
<input ng-model="$ctrl.searchText">
</md-input-container>
<span ng-if="$ctrl.loading !== 'data'">{{ $ctrl.list.length }}{{ $ctrl.total > 2000 ? "+" : "" }} results</span>
</div>
<div flex></div>
<!--
<md-button class="md-icon-button md-primary" aria-label="Open in Wikidata">
<md-icon>filter_list</md-icon>
</md-button>
-->
<div class="list__filter" layout="row" layout-align="center center">

<md-input-container>
<label>Heritage status</label>
<md-select ng-model="$ctrl.filter.heritage" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="0">No</md-option>
<md-option ng-value="1">Yes</md-option>
<md-option ng-value="9259">World Heritage Site</md-option>
</md-select>
</md-input-container>

<md-input-container>
<label>Type</label>
<md-select ng-model="$ctrl.filter.type" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="type.value"
ng-repeat="type in $ctrl.dict.types">
{{ type.label }}
</md-option>
</md-select>
</md-input-container>

<md-input-container>
<label>Image</label>
<md-select ng-model="$ctrl.filter.image" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="1">Yes</md-option>
<md-option ng-value="0">No</md-option>
</md-select>
</md-input-container>

<md-input-container>
<label>Wikipedia article</label>
<md-select ng-model="$ctrl.filter.wikipedia" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="1">Yes</md-option>
<md-option ng-value="0">No</md-option>
</md-select>
</md-input-container>

<md-button class="md-button md-primary" aria-label="Open in Wikidata" ng-if="false">
<md-icon>filter_list</md-icon>
Filter
</md-button>
</div>
<md-button class="md-icon-button" aria-label="Hide header" hide-gt-xs
ng-click="$ctrl.mobile.fullHeader = false">
<md-icon>keyboard_arrow_up</md-icon>
@@ -88,10 +119,13 @@ <h4 class="header__subtitle md-headline">{{ ::$ctrl.place.labels[$ctrl.lang] ||
ng-swipe-left="$ctrl.showMyMap()">
<div class="list__container" flex
layout="column" layout-align="start stretch">
<md-virtual-repeat-container flex>
<md-virtual-repeat-container md-top-index="$ctrl.topIndex" flex>
<md-list-item class="md-2-line"
md-virtual-repeat="item in $ctrl.list | orderBy: 'name.value'"
ui-sref="main.object({id: item.name.value_id.substring(1)})">
md-virtual-repeat="item in $ctrl.list | filter:$ctrl.searchText"
ng-class = "{ 'md-2-line--active' : (item.hovered || $ctrl.highlighted === item.name.value_id) }"
ng-mouseenter="item.hovered = true"
ng-mouseleave="item.hovered = false"
ng-click="$ctrl.zoomToID(item.name.value_id)">
<div class="list__image" layout="row" layout-align="center center">
<img ng-src="{{ item.image }}" alt="{{ item.name.value }}" ng-if="item.image">
<md-icon ng-if="!item.image">clear</md-icon>
@@ -100,24 +134,16 @@ <h4 class="header__subtitle md-headline">{{ ::$ctrl.place.labels[$ctrl.lang] ||
<p>{{ item.name.value }}</p>
<p class="muted">{{ item.admin.value }}</p>
</div>
<md-button class="md-button list__action" aria-label="Open in new tab"
ui-sref="main.object({id: item.name.value_id.substring(1)})"
target="_blank">
<md-icon>open_in_new</md-icon>
<span>Open</span>
</md-button>
</md-list-item>
<md-list-item class="md-2-line" ng-if="!$ctrl.showMap">
<div class="list__image" layout="row" layout-align="center center"></div>
<div class="md-list-item-text" layout="column"></div>
</md-list-item>
<md-list-item class="md-2-line" ng-if="!$ctrl.showMap">
<div class="list__image" layout="row" layout-align="center center"></div>
<div class="md-list-item-text" layout="column"></div>
</md-list-item>
<md-list-item class="md-2-line" ng-if="!$ctrl.showMap">
<div class="list__image" layout="row" layout-align="center center"></div>
<div class="md-list-item-text" layout="column"></div>
</md-list-item>
<md-list-item class="md-2-line" ng-if="!$ctrl.showMap">
<div class="list__image" layout="row" layout-align="center center"></div>
<div class="md-list-item-text" layout="column"></div>
</md-list-item>
<md-list-item class="md-2-line" ng-if="!$ctrl.showMap">
<md-list-item class="md-2-line"
ng-repeat="i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
ng-if="$ctrl.loading === 'data'">
<div class="list__image" layout="row" layout-align="center center"></div>
<div class="md-list-item-text" layout="column"></div>
</md-list-item>
@@ -126,10 +152,21 @@ <h4 class="header__subtitle md-headline">{{ ::$ctrl.place.labels[$ctrl.lang] ||
<div class="list__swiper" ng-click="$ctrl.showMyMap()"></div>
</div>
<div class="list__map" flex flex-xs="50">
<leaflet ng-if="$ctrl.showMap" flex
<div class="list__map--loading"
layout="column" layout-align="center center"
ng-if="$ctrl.loading">
<div class="cssload-container">
<div class="cssload-speeding-wheel"></div>
</div>
<p>Loading data</p>
</div>
<leaflet ng-if="$ctrl.map" flex
markers="$ctrl.map.markers"
markers-watch-options="$ctrl.map.markersWatchOptions"
layers="::$ctrl.map.layers"
center="::$ctrl.map.center"></leaflet>
center="$ctrl.map.center"
url-hash-center="yes"
event-broadcast="$ctrl.map.events"></leaflet>
<div class="list__swiper"
ng-click="$ctrl.showMyList()"
ng-swipe-right="$ctrl.showMyList()"></div>
356 changes: 286 additions & 70 deletions src/components/main/list/list.js

Large diffs are not rendered by default.

93 changes: 82 additions & 11 deletions src/components/main/list/list.scss
Original file line number Diff line number Diff line change
@@ -57,17 +57,17 @@ mo-list {
}

.list__header {
padding: 20px 25px;
padding: 20px 25px 10px;
background: white;
border-bottom: 1px solid $softGrey;

@include x-small { padding: 15px 20px; }
@include x-small { padding: 15px 20px 10px; }

.header__primary {
margin-bottom: 10px;
border-bottom: 1px solid $softGrey;
// margin-bottom: 10px;
// border-bottom: 1px solid $softGrey;

@include x-small { margin-bottom: 5px; }
// @include x-small { margin-bottom: 5px; }

&.header__primary--compact {
margin-bottom: 0;
@@ -118,6 +118,7 @@ mo-list {
.header__title {
margin-top: 0;
margin-bottom: 0;
margin-right: 10px;
font-size: 32px;
line-height: 32px;

@@ -126,7 +127,8 @@ mo-list {

.header__subtitle {
margin-top: 0;
margin-bottom: 10px;
margin-bottom: 0;
line-height: 24px;

@include x-small { font-size: 20px; margin-bottom: 5px; }
}
@@ -143,6 +145,10 @@ mo-list {

@include x-small { padding: 10px 20px; }

.md-button {
margin: 0 5px;
}

.md-icon-button {
@include x-small {
width: 30px;
@@ -153,15 +159,39 @@ mo-list {
}

md-input-container {
margin-bottom: 0;
margin-top: 0;
margin: 0 5px;
padding: 0;

.md-errors-spacer {
display: none;
}

md-select .md-select-value {
min-width: 130px;
}

&.md-input-has-value label:not(.md-no-float) {
-webkit-transform: translate3d(-1px, 14px, 0) scale(0.75);
transform: translate3d(-1px, 14px, 0) scale(0.75);
}
}

.list__quick-filter {
md-input-container {
width: 410px;
}
& > span {
margin-left: 25px;
font-weight: 500;
font-size: 90%;
text-transform: uppercase;
}
}
}

md-checkbox, md-checkbox:last-of-type {
margin: 0 5px;
}

.list__container {
margin-right: 20px;
@@ -177,6 +207,18 @@ mo-list {
.md-button {
@include x-small { padding: 0 10px; }
}

.list__action {
display: none;
}

&.md-2-line--active {
background-color: #e5e5e5;

.list__action {
display: block;
}
}
}
}

@@ -209,24 +251,53 @@ mo-list {

@include x-small { margin-left: 10px; }

.list__map--loading {
position: absolute;
width: 100%;
height: 100%;
z-index: 1010;
background: rgba(229, 229, 229, 0.5);
}

.angular-leaflet-map {
width: 100%;
height: 100%;
}

.leaflet-popup-close-button {
z-index: 1;
padding: 5px;
display: none;
}

.leaflet-popup-content-wrapper {
padding: 0;
border-radius: 2px;
}

.leaflet-popup-content {
min-width: 300px;
min-width: 250px;
margin: 0;
font: 16px Roboto, Arial, sans-serif;

.md-button {
padding: 0 !important;
}

.md-list-item-text {
margin: 10px;
height: 60px;

p:first-child {
// max-height: 36px;
line-height: 1.4em;
}
}

.list__image {
margin: 0;
width: 80px;
min-width: 80px;
height: 80px;
}
}
}

9 changes: 9 additions & 0 deletions src/components/main/list/popup.tpl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<md-list-item class="md-2-line" ui-sref="main.object({id: ${mid.substring(1)}})">
<div class="list__image" layout="row" layout-align="center center">
<img ng-src="{{'${element.image}'}}" ng-if="${!!element.image}">
</div>
<div class="md-list-item-text" layout="column">
<p>${element.name.value}</p>
<p class="muted">${element.admin.value}</p>
</div>
</md-list-item>
90 changes: 83 additions & 7 deletions src/components/main/map/map.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,66 @@
<div class="container" layout="column" layout-align="start stretch" flex>
<div layout="column" layout-align="start stretch" flex="none">
<div class="list__subheader"
layout="row" layout-align="start center">
<div class="list__quick-filter" layout="row" layout-align="start center">
<md-input-container>
<label>Quick filter</label>
<input ng-model="$ctrl.searchText">
</md-input-container>
<span ng-if="$ctrl.loading !== 'data'">{{ $ctrl.list.length }}{{ $ctrl.total > 2000 ? "+" : "" }} results</span>
</div>
<div flex></div>
<div class="list__filter" layout="row" layout-align="center center">
<md-input-container>
<label>Heritage status</label>
<md-select ng-model="$ctrl.filter.heritage" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="0">No</md-option>
<md-option ng-value="1">Yes</md-option>
<md-option ng-value="9259">World Heritage Site</md-option>
</md-select>
</md-input-container>

<md-input-container>
<label>Type</label>
<md-select ng-model="$ctrl.filter.type" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="type.value"
ng-repeat="type in $ctrl.dict.types">
{{ type.label }}
</md-option>
</md-select>
</md-input-container>

<md-input-container>
<label>Image</label>
<md-select ng-model="$ctrl.filter.image" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="1">Yes</md-option>
<md-option ng-value="0">No</md-option>
</md-select>
</md-input-container>

<md-input-container>
<label>Wikipedia article</label>
<md-select ng-model="$ctrl.filter.wikipedia" ng-change="$ctrl.filterMap()">
<md-option ng-value=""><span class="muted">Any</span></md-option>
<md-option ng-value="1">Yes</md-option>
<md-option ng-value="0">No</md-option>
</md-select>
</md-input-container>

<md-button class="md-button md-primary" aria-label="Open in Wikidata" ng-if="false">
<md-icon>filter_list</md-icon>
Filter
</md-button>
</div>
<md-button class="md-icon-button" aria-label="Hide header" hide-gt-xs
ng-click="$ctrl.mobile.fullHeader = false">
<md-icon>keyboard_arrow_up</md-icon>
</md-button>
</div>
</div>
<md-content class="content" flex
layout="row" layout-align="start stretch"
ng-class="$ctrl.contentScrolled ? 'content--scrolled' : 'content--unscrolled'">
@@ -7,23 +69,29 @@
ng-swipe-left="$ctrl.showMyMap()">
<div class="list__loading"
layout="column" layout-align="center center"
ng-if="$ctrl.map.center.zoom <= 12">
ng-if="$ctrl.map.center.zoom <= 10">
<h4 class="md-title">Zoom in</h4>
</div>
<div class="list__loading" layout="column" layout-align="center center"
ng-if="$ctrl.loading">
<md-progress-linear md-mode="indeterminate"></md-progress-linear>
<div class="cssload-container">
<div class="cssload-speeding-wheel"></div>
</div>
<p>Loading data</p>
</div>
<div class="list__loading" layout="column" layout-align="center center"
ng-if="$ctrl.map.center.zoom > 12 && !$ctrl.loading && !$ctrl.list.length">
ng-if="$ctrl.map.center.zoom > 10 && !$ctrl.loading && !$ctrl.list.length">
<h4 class="md-title">No results</h4>
</div>
<div class="list__container" flex
layout="column" layout-align="start stretch">
<md-virtual-repeat-container flex>
<md-virtual-repeat-container md-top-index="$ctrl.topIndex" flex>
<md-list-item class="md-2-line"
md-virtual-repeat="item in $ctrl.list | orderBy: 'name.value'"
ui-sref="main.object({id: item.name.value_id.substring(1)})">
md-virtual-repeat="item in $ctrl.list | filter:$ctrl.searchText"
ng-class = "{ 'md-2-line--active' : (item.hovered || $ctrl.highlighted === item.name.value_id) }"
ng-mouseenter="item.hovered = true"
ng-mouseleave="item.hovered = false"
ng-click="$ctrl.zoomToID(item.name.value_id)">
<div class="list__image" layout="row" layout-align="center center">
<img ng-src="{{item.image}}" alt="{{item.name.value}}" ng-if="item.image">
<md-icon ng-if="!item.image">clear</md-icon>
@@ -32,6 +100,12 @@ <h4 class="md-title">No results</h4>
<p>{{ item.name.value }}</p>
<p class="muted">{{ item.admin.value }}</p>
</div>
<md-button class="md-button list__action" aria-label="Open in new tab"
ui-sref="main.object({id: item.name.value_id.substring(1)})"
target="_blank">
<md-icon>open_in_new</md-icon>
<span>Open</span>
</md-button>
</md-list-item>
</md-virtual-repeat-container>
</div>
@@ -40,9 +114,11 @@ <h4 class="md-title">No results</h4>
<div class="list__map" flex flex-xs="50">
<leaflet ng-if="!$ctrl.loadingMap" flex
markers="$ctrl.map.markers"
markers-watch-options="$ctrl.map.markersWatchOptions"
layers="$ctrl.map.layers"
center="$ctrl.map.center"
url-hash-center="yes"
center="$ctrl.map.center"></leaflet>
event-broadcast="$ctrl.map.events"></leaflet>
<div class="list__swiper"
ng-click="$ctrl.showMyList()"
ng-swipe-right="$ctrl.showMyList()"></div>
169 changes: 133 additions & 36 deletions src/components/main/map/map.js
Original file line number Diff line number Diff line change
@@ -1,83 +1,145 @@
import L from 'leaflet';

import './map.scss';
import template from './map.html';

const MapComponent = { controller, template };

function controller($location, $q, $scope, $state, $stateParams, $timeout, $window, langService, leafletData, localStorageService, mapService, wikidata) {
const vm = this;
const icon = mapService.getMapIcon();
const langs = langService.getUserLanguages();

let canceler = $q.defer();
let request = null;

// bindings

vm.map = mapService.getMapInstance({ center: { lat: 49.4967, lng: 12.4805, zoom: 4 } });
vm.dict = {
types: [
{ label: 'Art', value: '838948' },
{ label: 'Building', value: '41176' },
{ label: 'Castle', value: '23413' },
{ label: 'Cemetery', value: '39614' },
{ label: 'Library', value: '7075' },
{ label: 'Manor house', value: '879050' },
{ label: 'Park', value: '22698' },
{ label: 'Place of worship', value: '1370598' },
{ label: 'Residential building', value: '11755880' },
],
};
vm.filter = angular.extend({ heritage: 1 }, $stateParams);
vm.list = [];
vm.listParams = {};
vm.loading = false;
vm.loadingMap = true;

vm.map = mapService.getMapInstance({ center: { lat: 49.4967, lng: 12.4805, zoom: 4 } });

vm.filterMap = filterMap;
vm.showMyMap = () => { vm.contentScrolled = true; };
vm.showMyList = () => { vm.contentScrolled = false; };
vm.zoomToID = zoomToID;

// activate
// init

$scope.$on('centerUrlHash', (event, centerHash) => {
$location.search({ c: centerHash });
});
init();

$timeout(() => {
vm.loadingMap = false;
// functions

function filterMap() {
$state.transitionTo('main.map', vm.filter, { notify: false });
leafletData.getMap().then((map) => {
if (map.getZoom() > 12) {
if (map.getZoom() > 10) {
getDataBB(map.getBounds());
}
map.on('dragend zoomend', () => {
if (map.getZoom() > 12) {
});
}

function init() {
$scope.$on('centerUrlHash', (event, centerHash) => {
vm.filter.c = centerHash;
$state.transitionTo('main.map', vm.filter, { notify: false });
});

$timeout(() => {
vm.loadingMap = false;
leafletData.getMap().then((map) => {
if (map.getZoom() > 10) {
getDataBB(map.getBounds());
}
map.on('dragend zoomend', () => {
if (map.getZoom() > 10) {
getDataBB(map.getBounds());
}
});
map.on('dragstart zoomstart', () => {
canceler.resolve();
});
});
map.on('dragstart zoomstart', () => {
canceler.resolve();
});
});
}, 100);
}, 100);

$window.document.title = 'Monumental';

// functions
$window.document.title = 'Monumental';
}

function getDataBB(bounds) {
vm.loading = true;
canceler.resolve();
canceler = $q.defer();

const imageOptions = ['MINUS { ?item wdt:P18 ?image . }', '?item wdt:P18 ?image .'];
const image = imageOptions[vm.filter.image] || 'OPTIONAL { ?item wdt:P18 ?image . }';

const wikipediaOptions = ['FILTER NOT EXISTS { ?article schema:about ?item } .', 'FILTER EXISTS { ?article schema:about ?item } .'];
const wikipedia = wikipediaOptions[vm.filter.wikipedia] || '';

request = wikidata.getSPARQL(`SELECT ?item ?itemLabel ?admin ?adminLabel ?image ?coord ?heritage WHERE {
SERVICE wikibase:box {
?item wdt:P625 ?coord .
bd:serviceParam wikibase:cornerWest "Point(${bounds.getSouthWest().lng} ${bounds.getSouthWest().lat})"^^geo:wktLiteral .
bd:serviceParam wikibase:cornerEast "Point(${bounds.getNorthEast().lng} ${bounds.getNorthEast().lat})"^^geo:wktLiteral .
}
OPTIONAL { ?item wdt:P131 ?admin . }
OPTIONAL { ?item wdt:P18 ?image . }
?item wdt:P1435 ?heritage .
${image}
${getHeritageFilter()}
${vm.filter.type ? `?item wdt:P31 ?type . ?type wdt:P279* wd:Q${vm.filter.type} .` : 'OPTIONAL { ?item wdt:P31 ?type }'}
${wikipedia}
SERVICE wikibase:label { bd:serviceParam wikibase:language "${langs.map(lang => lang.code).join(',')}" }
}`, { timeout: canceler.promise });

request.then((data) => {
vm.map.markers = {};
// http://stackoverflow.com/a/36744732/1418878
vm.list = data
const list = data
.map(element => setListElement(element))
.filter((element, index, array) => array.findIndex(t => t.name.value_id === element.name.value_id) === index);
vm.total = list.length;
vm.list = list
.slice(0, 2000)
.sort((a, b) => (a.name.value > b.name.value) ? 1 : ((b.name.value > a.name.value) ? -1 : 0));
vm.list.forEach((element) => {
vm.map.markers[element.name.value_id] = setMarker(element);
});

let timeout = null;
$scope.$on('leafletDirectiveMarker.mouseover', (event, marker) => { timeout = $timeout(() => { showPopup(event, marker); }, 250); });
$scope.$on('leafletDirectiveMarker.mouseout', () => { $timeout.cancel(timeout); });
$scope.$on('leafletDirectiveMarker.click', showPopup);
vm.loading = false;
}).catch(() => {
vm.loading = false;
});
}

function getHeritageFilter() {
const query = '?item p:P1435 ?monument .';
const value = vm.filter.heritage;
if (angular.isUndefined(value)) { return `OPTIONAL { ${query} }`; }
if (parseInt(value, 10) === 0) { return `MINUS { ${query} }`; }
if (parseInt(value, 10) === 1) { return `${query}`; }
if (parseInt(value, 10) > 1) { return `?item wdt:P1435 wd:Q${value} .`; }
return '';
}

/**
* Gets link to image
* @param {String} image
@@ -90,18 +152,6 @@ function controller($location, $q, $scope, $state, $stateParams, $timeout, $wind
return false;
}

function getMessage(element) {
return `<md-list-item class="md-2-line" ui-sref="main.object({id: ${element.name.value_id.substring(1)}})">
<div class="list__image" layout="row" layout-align="center center">
<img ng-src="{{ '${element.image}' }}" alt="${element.name.value}" ng-if="${!!element.image}">
</div>
<div class="md-list-item-text" layout="column">
<p>${element.name.value}</p>
<p class="muted">${element.admin ? element.admin.value : ''}</p>
</div>
</md-list-item>`;
}

function setListElement(element) {
const id = element.item.value;
const obj = {
@@ -141,13 +191,60 @@ function controller($location, $q, $scope, $state, $stateParams, $timeout, $wind

function setMarker(element) {
return {
data: element,
lat: +element.coord[1],
lng: +element.coord[0],
message: getMessage(element),
layer: 'monuments',
icon,
icon: mapService.getMapIcon(element),
};
}

function showPopup(event, marker) {
if (marker.leafletEvent.type === 'click') {
const item = vm.list.filter(element => element.name.value_id === marker.model.data.name.value_id)[0];
vm.highlighted = item.name.value_id;
vm.topIndex = vm.list.indexOf(item);
}

if (marker.leafletObject.getPopup() && marker.leafletObject.isPopupOpen()) {
return;
}
if (marker.leafletObject.getPopup() && !marker.leafletObject.isPopupOpen()) {
marker.leafletObject.openPopup();
return;
}

const data = marker.model.data;
const text = `<md-list-item class="md-2-line"
ui-sref="main.object({id: ${data.name.value_id.substring(1)}})">
<div class="list__image" layout="row" layout-align="center center" ng-if="${!!data.image}">
<img ng-src="{{::'${data.image}'}}">
</div>
<div class="md-list-item-text" layout="column">
<p>${data.name.value}</p>
<p class="muted">${data.admin ? data.admin.value : ''}</p>
</div>
</md-list-item>`;

const popup = L.popup({ autoPan: false }).setContent(text);
marker.leafletObject.bindPopup(popup);
marker.leafletObject.openPopup();
}

function zoomToID(pinId) {
const marker = vm.map.markers[pinId];
$q.all({
map: leafletData.getMap(),
markers: leafletData.getMarkers(),
}).then((data) => {
data.map.setView([marker.lat, marker.lng], 17);
showPopup(null, {
leafletObject: data.markers[pinId],
leafletEvent: {},
model: marker,
});
});
}
}

export default () => {
98 changes: 95 additions & 3 deletions src/components/main/map/map.scss
Original file line number Diff line number Diff line change
@@ -51,6 +51,57 @@ mo-map {
}
}

.list__subheader {
padding: 15px 25px;
background: #fafafa;
border-bottom: 1px solid $softGrey;

@include x-small { padding: 10px 20px; }

.md-button {
margin: 0 5px;
}

.md-icon-button {
@include x-small {
width: 30px;
min-height: 30px;
height: 30px;
padding: 0px;
}
}

md-input-container {
margin: 0 5px;
padding: 0;

.md-errors-spacer {
display: none;
}

md-select .md-select-value {
min-width: 130px;
}

&.md-input-has-value label:not(.md-no-float) {
-webkit-transform: translate3d(-1px, 14px, 0) scale(0.75);
transform: translate3d(-1px, 14px, 0) scale(0.75);
}
}

.list__quick-filter {
md-input-container {
width: 410px;
}
span {
margin-left: 25px;
font-weight: 500;
font-size: 90%;
text-transform: uppercase;
}
}
}

.list {
position: relative;
width: 450px;
@@ -70,6 +121,18 @@ mo-map {
.md-button {
@include x-small { padding: 0 10px; }
}

.list__action {
display: none;
}

&.md-2-line--active {
background-color: #e5e5e5;

.list__action {
display: block;
}
}
}
}

@@ -130,24 +193,53 @@ mo-map {

@include x-small { margin-left: 10px; }

.list__map--loading {
position: absolute;
width: 100%;
height: 100%;
z-index: 1010;
background: rgba(229, 229, 229, 0.5);
}

.angular-leaflet-map {
width: 100%;
height: 100%;
}

.leaflet-popup-close-button {
z-index: 1;
padding: 5px;
display: none;
}

.leaflet-popup-content-wrapper {
padding: 0;
border-radius: 2px;
}

.leaflet-popup-content {
min-width: 300px;
min-width: 250px;
margin: 0;
font: 16px Roboto, Arial, sans-serif;

.md-button {
padding: 0 !important;
}

.md-list-item-text {
margin: 10px;
height: 60px;

p:first-child {
// max-height: 36px;
line-height: 1.4em;
}
}

.list__image {
margin: 0;
width: 80px;
min-width: 80px;
height: 80px;
}
}
}

32 changes: 27 additions & 5 deletions src/components/main/monument/components/location.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
const LocationComponent = {
bindings: { monument: '=' },
bindings: {
monument: '=',
params: '=',
},
controller,
template: `<div layout="row" layout-align="start center">
<span><md-icon>location_city</md-icon></span>
<span>
<span ng-repeat="place in $ctrl.location">
<a ui-sref="main.list({id: place.value_id.substring(1)})">{{ place.value }}</a><span ng-if="!$last"> · </span>
<a href ng-click="$ctrl.go(place)">{{ place.value }}</a><span ng-if="!$last"> · </span>
</span>
<span class="muted" ng-if="!$ctrl.location">No location provided</span>
</span>
</div>`,
};

function controller(wikidata) {
function controller($state, wikidata) {
const vm = this;
vm.go = go;

init();

function init() {
@@ -26,8 +31,10 @@ function controller(wikidata) {
id = vm.monument.id;
} else if (claims.P131) {
prop = 'wdt:P131';
const preferred = claims.P131.values.filter(value => value.rank === 'preferred');
id = preferred.length ? preferred[0].value_id : claims.P131.values[0].value_id;
const preferred = claims.P131.filter(value => value.rank === 'preferred');
id = preferred.length
? preferred[0].mainsnak.datavalue.value.id
: claims.P131[0].mainsnak.datavalue.value.id;
} else {
return;
}
@@ -36,6 +43,21 @@ function controller(wikidata) {
vm.location = response;
});
}

function go(place) {
const params = {};
if (vm.params) {
angular.extend(params, vm.params, {
id: place.value_id.substring(1),
});
} else {
angular.extend(params, {
id: place.value_id.substring(1),
heritage: 1,
});
}
$state.go('main.list', params);
}
}

export default () => {
14 changes: 7 additions & 7 deletions src/components/main/monument/components/native-name.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
const NativeNameComponent = {
bindings: { monument: '=' },
controller,
template: '<span ng-repeat="name in ::$ctrl.getNativeLabel() track by $index">{{ ::name }}<span ng-if="!$last"> · </span></span>',
template: '<span ng-repeat="name in ::$ctrl.nativeLabel track by $index">{{ ::name.value }}<span ng-if="!$last"> · </span></span>',
};

function controller(langService) {
const vm = this;
vm.getNativeLabel = getNativeLabel;
vm.nativeLabel = getNativeLabel();

function getNativeLabel() {
const country = getPropertyValue('P17');
const languages = langService.getNativeLanguages(country.value_id);
const languages = langService.getNativeLanguages(country.id) || ['en'];
return languages.map(lang => vm.monument.labels[lang]).filter(name => name);
}

function getPropertyValue(prop) {
if (vm.monument.claims[prop] && vm.monument.claims[prop].values.length) {
return vm.monument.claims[prop].values[0];
}
return false;
if (!vm.monument.claims[prop]) return false;
const value = vm.monument.claims[prop][0];

return value.mainsnak.datavalue.value;
}
}

50 changes: 50 additions & 0 deletions src/components/main/monument/components/property-coord.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const PropertyCoordComponent = {
bindings: {
claims: '=',
edit: '=',
labels: '=',
lang: '=',
property: '=',
text: '=',
},
controller,
template: `
<span class="muted" flex="35">{{ ::($ctrl.text[$ctrl.property][$ctrl.lang.code] || $ctrl.text[$ctrl.property].en) }}</span>
<span flex="65"
layout="column" layout-align="start stretch">
<span class="monument__details-value"
layout="column" layout-align="start"
ng-repeat="value in ::$ctrl.claims[$ctrl.property]">
<a ui-sref="main.map({c: '{{value.mainsnak.datavalue.value.latitude}}:{{value.mainsnak.datavalue.value.longitude}}:16', heritage: '1'})">
{{ ::value.mainsnak.datavalue.value.latitude | toDMS:'NS' }} &nbsp; {{ ::value.mainsnak.datavalue.value.longitude | toDMS:'EW' }}
</a>
<small class="muted">
{{ ::value.mainsnak.datavalue.value.latitude.toFixed(6) }} {{ ::value.mainsnak.datavalue.value.longitude.toFixed(6) }}
</small>
</span>
<span class="monument__details-value"
ng-if="!$ctrl.claims[$ctrl.property].length">
<span class="muted">not provided</span>
</span>
</span>`,
};

function controller(wikidata) {
const vm = this;
vm.querySearch = text => wikidata.getSearch(text);
}

export default () => {
angular
.module('monumental')
.component('moPropertyCoord', PropertyCoordComponent)
.filter('toDMS', () => (input, direction) => {
const dir = input < 0 ? direction[1] : direction[0];
const absolute = Math.abs(input);
const deg = parseInt(absolute, 10);
const t1 = (absolute - deg) * 60;
const min = parseInt(t1, 10);
const sec = ((t1 - min) * 60).toFixed(2);
return `${deg}° ${min}${sec}${dir}`;
});
};
222 changes: 222 additions & 0 deletions src/components/main/monument/components/property-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
const PropertyItemComponent = {
bindings: {
claims: '=',
edit: '=',
hidden: '=',
labels: '=',
lang: '=',
link: '=',
property: '=',
text: '=',
},
controller,
template: `
<span class="muted" flex="35"
ng-if="$ctrl.edit.all || (!$ctrl.edit.all && !$ctrl.hidden)">
{{ ::($ctrl.text[$ctrl.property][$ctrl.lang.code] || $ctrl.text[$ctrl.property].en) }}
</span>
<span flex="65"
layout="column" layout-align="start stretch">
<span class="monument__details-value"
ng-repeat="value in ::$ctrl.propertyClaims">
<div class="monument__details-edit" ng-if="!$ctrl.edit.all && !$ctrl.hidden">
<span ng-if="!$ctrl.link">
{{ ::($ctrl.labels[value.mainsnak.datavalue.value.id][$ctrl.lang.code] || $ctrl.labels[value.mainsnak.datavalue.value.id].en || value.mainsnak.datavalue.value.id) }}
<md-button class="md-icon-button md-primary" aria-label="Open in Wikidata"
ng-if="false"
ng-href="//www.wikidata.org/wiki/{{ ::value.mainsnak.datavalue.value.id }}" target="_blank">
<md-tooltip>Show Wikidata entry</md-tooltip>
<md-icon md-svg-icon="assets/images/barcode.svg"></md-icon>
</md-button>
</span>
<a ui-sref="main.object({id: value.mainsnak.datavalue.value.id.substring(1)})"
ng-if="value.mainsnak.datavalue.value.id && $ctrl.link === true">
{{ ::($ctrl.labels[value.mainsnak.datavalue.value.id][$ctrl.lang.code] || $ctrl.labels[value.mainsnak.datavalue.value.id].en || value.mainsnak.datavalue.value.id) }}
</a>
<a ui-sref="main.list({id: value.mainsnak.datavalue.value.id.substring(1), heritage: 1})"
ng-if="value.mainsnak.datavalue.value.id && $ctrl.link === 'list'">
{{ ::($ctrl.labels[value.mainsnak.datavalue.value.id][$ctrl.lang.code] || $ctrl.labels[value.mainsnak.datavalue.value.id].en || value.mainsnak.datavalue.value.id) }}
</a>
<div layout="row" layout-align="start center"
class="monument__details-qualifier"
ng-repeat="qualifier in ::value.qualifiers">
<span class="muted" flex="30">{{ ::($ctrl.text[qualifier[0].property][$ctrl.lang.code] || $ctrl.text[qualifier[0].property].en || qualifier[0].property) }}</span>
<div layout="column" layout-align="start start">
<span ng-repeat="qualifiervalue in qualifier"
ng-init="$ctrl.getFormattedTime(qualifiervalue.datavalue)">
{{ ::(qualifiervalue.datavalue.value.label[$ctrl.lang.code] || qualifiervalue.datavalue.value.label.en || qualifiervalue.datavalue.value.label) }}
</span>
</div>
</div>
<span class="muted"
ng-if="!value.mainsnak.datavalue.value.id && $first">
not provided
</span>
</div>
<div class="property__edit"
layout="row" layout-align="start start"
ng-if="$ctrl.edit.all"
ng-class="{'property__edit--deleted': value.action.type === 'remove'}">
<md-autocomplete flex
ng-init="value.search = $ctrl.labels[value.mainsnak.datavalue.value.id][$ctrl.lang.code] || $ctrl.labels[value.mainsnak.datavalue.value.id].en || value.mainsnak.datavalue.value.id || ''"
md-input-name="autocompleteField"
md-selected-item="value.searchSelected"
md-selected-item-change="$ctrl.queueEdit(value)"
md-search-text="value.search"
md-search-text-change="$ctrl.searchTextChange(value, $last)"
md-items="item in $ctrl.querySearch(value.search)"
md-item-text="item.label"
md-min-length="1"
md-floating-label="label"
md-escape-options="blur"
md-dropdown-position="bottom"
ng-disabled="value.action.type === 'remove'"
ng-model-options="{ debounce: 500 }">
<md-item-template>
<strong>{{item.label}}</strong>
<span>{{item.description}}</span> <span class="muted">{{ item.id }}</span>
</md-item-template>
</md-autocomplete>
<md-button class="md-primary" aria-label="Category added"
ng-href="//wikidata.org/w/index.php?action=historysubmit&type=revision&diff={{ value.save }}" target="_blank"
ng-if="value.save">
<md-tooltip md-direction="bottom">Click to see diff or revert edit</md-tooltip>
<md-icon>check_circle</md-icon>
</md-button>
<md-button ng-disabled="value.loading"
ng-click="$ctrl.queueRemove(value)"
ng-if="value.id || value.searchSelected">
<md-progress-circular md-mode="indeterminate" md-diameter="24px" ng-if="value.loading"></md-progress-circular>
<md-tooltip md-direction="bottom">Click to remove</md-tooltip>
<md-icon ng-if="!value.loading">delete</md-icon>
</md-button>
<md-button ng-disabled="true"
ng-if="!value.id && !value.searchSelected && !value.save"
aria-label="Empty">
</md-button>
</div>
</span>
</span>`,
};

function controller($q, $rootScope, $stateParams, $timeout, wikidata, WikiService) {
const vm = this;

vm.propertyClaims = vm.claims[vm.property];

vm.getFormattedTime = getFormattedTime;
vm.querySearch = text => wikidata.getSearch(text);
vm.queueEdit = queueEdit;
vm.queueRemove = queueRemove;
vm.searchTextChange = searchTextChange;

// init

if (!vm.propertyClaims) {
vm.claims[vm.property] = [{}];
} else {
vm.claims[vm.property].push({});
}
vm.propertyClaims = vm.claims[vm.property];

// functions

function addClaim(value) {
return WikiService.addClaimItem({
entity: `Q${$stateParams.id}`,
property: vm.property,
value: +value.searchSelected.title.substring(1),
});
}

function getFormattedTime(datavalue) {
if (!datavalue) { return; }

if (datavalue.type === 'time') {
WikiService.getFormattedTime(datavalue.value, vm.lang.code)
.then((response) => {
datavalue.value.label = response;
});
} else if (datavalue.type === 'wikibase-entityid') {
WikiService.getLabel(datavalue.value.id)
.then((response) => {
datavalue.value.label = response;
});
} else if (datavalue.type === 'string') {
datavalue.value = { label: datavalue.value };
}
}

/**
* Add edit to queue
* @param {Object} value
*/
function queueEdit(value) {
if (value.searchSelected) {
value.save = undefined;
}
value.action = {
type: 'save',
promise: save,
value,
};
$rootScope.$emit('recountQueue');
}

/**
* Add remove to queue
* @param {Object} value
*/
function queueRemove(value) {
if (value.id) {
value.action = {
type: 'remove',
promise: remove,
value,
};
} else {
const index = vm.propertyClaims.indexOf(value);
if (index !== -1) {
vm.propertyClaims.splice(index, 1);
}
}
$rootScope.$emit('recountQueue');
}

function remove(value) {
return WikiService.removeClaim({ id: value.id });
}

function save(value) {
value.loading = true;
const action = value.id ? setClaim : addClaim;

return action(value).then((response) => {
value.loading = false;
value.save = response.data.pageinfo.lastrevid;
value.id = response.data.claim.id;
value.mainsnak = response.data.claim.mainsnak;
});
}

function searchTextChange(value, isLast) {
if (value.search && isLast) {
vm.propertyClaims.push({});
}
}

function setClaim(value) {
return WikiService.setClaimItem({
id: value.id,
property: value.mainsnak.property,
value: +value.searchSelected.title.substring(1),
});
}
}

export default () => {
angular
.module('monumental')
.component('moPropertyItem', PropertyItemComponent);
};
163 changes: 163 additions & 0 deletions src/components/main/monument/components/property-quantity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
const PropertyQuantityComponent = {
bindings: {
claims: '=',
edit: '=',
hidden: '=',
labels: '=',
lang: '=',
property: '=',
text: '=',
},
controller,
template: `
<span class="muted" flex="35"
ng-if="$ctrl.edit.all || (!$ctrl.edit.all && !$ctrl.hidden)">
{{ ::($ctrl.text[$ctrl.property][$ctrl.lang.code] || $ctrl.text[$ctrl.property].en) }}
</span>
<span flex="65"
layout="column" layout-align="start stretch">
<span class="monument__details-value"
ng-repeat="value in ::$ctrl.propertyClaims">
<div class="monument__details-edit" ng-if="!$ctrl.edit.all && !$ctrl.hidden">
{{ ::value.mainsnak.datavalue.value.amount | number }}
<span class="muted">{{ ::($ctrl.text[value.mainsnak.datavalue.value.unit.substring(31)][$ctrl.lang.code] || $ctrl.text[value.mainsnak.datavalue.value.unit.substring(31)].en) }}</span>
<span class="muted"
ng-if="!value.mainsnak.datavalue.value.amount && $first">
not provided
</span>
</div>
<div class="property__edit"
layout="row" layout-align="start start"
ng-if="$ctrl.edit.all"
ng-class="{'property__edit--deleted': value.action.type === 'remove'}">
<md-input-container flex
ng-init="value.newValue = value.mainsnak.datavalue.value.amount">
<input type="text" aria-label="Value"
ng-model="value.newValue"
ng-change="$ctrl.queueEdit(value, $last)"
ng-disabled="value.action.type === 'remove'">
</md-input-container>
<div ng-init="value.unit = value.mainsnak.datavalue.value.unit.substring(31)">
{{ $ctrl.text[value.unit][$ctrl.lang.code] || $ctrl.text[value.unit].en }}
</div>
<md-button class="md-primary" aria-label="Category added"
ng-href="//wikidata.org/w/index.php?action=historysubmit&type=revision&diff={{ value.save }}" target="_blank"
ng-if="$ctrl.edit.all && value.save">
<md-tooltip md-direction="bottom">Click to see diff or revert edit</md-tooltip>
<md-icon>check_circle</md-icon>
</md-button>
<md-button ng-disabled="value.loading"
ng-click="$ctrl.queueRemove(value)"
ng-if="value.id || value.newValue">
<md-progress-circular md-mode="indeterminate" md-diameter="24px" ng-if="value.loading"></md-progress-circular>
<md-tooltip md-direction="bottom">Click to remove</md-tooltip>
<md-icon ng-if="!value.loading">delete</md-icon>
</md-button>
</div>
</span>
</span>`,
};

function controller($q, $rootScope, $stateParams, wikidata, WikiService) {
const vm = this;

vm.propertyClaims = vm.claims[vm.property];

vm.queueEdit = queueEdit;
vm.queueRemove = queueRemove;

// init

if (!vm.propertyClaims) {
vm.claims[vm.property] = [{}];
} else {
vm.claims[vm.property].push({});
}
vm.propertyClaims = vm.claims[vm.property];

// functions

function addClaim(value) {
return WikiService.addClaimString({
entity: `Q${$stateParams.id}`,
property: vm.property,
value: angular.toJson({
amount: value.newValue,
unit: value.unit ? `//www.wikidata.org/entity/${value.unit}` : undefined,
}),
});
}

/**
* Add edit to queue
* @param {Object} value
*/
function queueEdit(value, isLast) {
if (value.newValue && isLast) {
vm.propertyClaims.push({});
}

value.save = undefined;
value.action = {
type: 'save',
promise: save,
value,
};
$rootScope.$emit('recountQueue');
}

/**
* Add remove to queue
* @param {Object} value
*/
function queueRemove(value) {
if (value.id) {
value.action = {
type: 'remove',
promise: remove,
value,
};
} else {
const index = vm.propertyClaims.indexOf(value);
if (index !== -1) {
vm.propertyClaims.splice(index, 1);
}
}
$rootScope.$emit('recountQueue');
}

function remove(value) {
return WikiService.removeClaim({ id: value.id });
}

function save(value) {
value.loading = true;
const action = value.id ? setClaim : addClaim;

action(value).then((response) => {
value.loading = false;
if (response.error) {
value.error = response.error.info;
} else {
value.save = response.data.pageinfo.lastrevid;
value.id = response.data.claim.id;
value.mainsnak = response.data.claim.mainsnak;
}
});
}

function setClaim(value) {
return WikiService.setClaimQuantity({
id: value.id,
property: value.mainsnak.property,
value: value.newValue,
unit: value.mainsnak.datavalue.value.unit,
});
}
}

export default () => {
angular
.module('monumental')
.component('moPropertyQuantity', PropertyQuantityComponent);
};
14 changes: 10 additions & 4 deletions src/components/main/monument/components/url.js
Original file line number Diff line number Diff line change
@@ -13,12 +13,18 @@ function controller() {
init();

function init() {
if (vm.monument && vm.monument.claims && vm.monument.claims.P856) {
const values = vm.monument.claims.P856.values;
vm.value = values[0].value;
vm.label = values[0].value.replace(/^https?:\/\/(www\.)?/, '').replace(/^\/|\/$/g, '');
if (getPropertyValue('P856')) {
vm.value = getPropertyValue('P856');
vm.label = vm.value.replace(/^https?:\/\/(www\.)?/, '').replace(/^\/|\/$/g, '');
}
}

function getPropertyValue(prop) {
if (!vm.monument.claims[prop]) return false;
const value = vm.monument.claims[prop][0];

return value.mainsnak.datavalue.value;
}
}

export default () => {
277 changes: 241 additions & 36 deletions src/components/main/monument/monument.html

Large diffs are not rendered by default.

251 changes: 235 additions & 16 deletions src/components/main/monument/monument.js
Original file line number Diff line number Diff line change
@@ -7,19 +7,34 @@ import pack from '../../../../package.json';

const MonumentComponent = { controller, template };

function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $stateParams, $timeout, $window, localStorageService, WikiService, imageService, langService, leafletData, mapService, wikidata) {
function controller($httpParamSerializerJQLike, $anchorScroll, $http, $mdDialog, $mdMenu, $mdToast, $q, $rootScope, $sce, $scope, $state, $stateParams, $timeout, $window, FileUploader, localStorageService, WikiService, imageService, langService, leafletData, mapService, textService, wikidata) {
const vm = this;
const icon = mapService.getMapIcon();
const id = $stateParams.id.includes('Q') ? $stateParams.id : `Q${$stateParams.id}`;
const langs = langService.getUserLanguages();

vm.getCommonsLink = getCommonsLink;
vm.getWikipediaArticle = getWikipediaArticle;
vm.actions = {
claims: [],
other: [],
};
vm.busy = false;
vm.edit = { all: false };
vm.image = [];
vm.isLoggedIn = false;
vm.lang = langs[0];
vm.map = {};
vm.queue = [];
vm.stateParams = $stateParams;
vm.text = null;

vm.categoryChange = categoryChange;
vm.getCommonsLink = getCommonsLink;
vm.getWikipediaArticle = getWikipediaArticle;
vm.labelChange = labelChange;
vm.openArticleList = (menu, event) => menu.open(event);
vm.openImage = openImage;
vm.queryCommonsCategory = queryCommonsCategory;
vm.saveAll = saveAll;
vm.scrollTo = anchor => $anchorScroll(anchor);

// init
@@ -28,6 +43,69 @@ function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $statePa

// functions

var uploader = vm.uploader = new FileUploader({
url: `${$window.__env.baseUrl}/commons`,
// url: 'https://commons.wikimedia.org/w/api.php',
withCredentials: true,
});

// FILTERS

uploader.filters.push({
name: 'imageFilter',
fn: function (item /*{File|FileLikeObject}*/, options) {
var type = '|' + item.type.slice(item.type.lastIndexOf('/') + 1) + '|';
return '|jpg|png|jpeg|bmp|gif|'.indexOf(type) !== -1;
}
});

// CALLBACKS

uploader.onWhenAddingFileFailed = function (item /*{File|FileLikeObject}*/, filter, options) {
console.info('onWhenAddingFileFailed', item, filter, options);
};
uploader.onAfterAddingFile = function (fileItem) {
console.info('onAfterAddingFile', fileItem);
const data = [
{ action: 'upload' },
{ format: 'json' },
{ errorformat: 'html' },
{ filename: fileItem.file.name },
{ comment: '#monumental' },
{ text: 'Initial text' },
{ watchlist: 'watch' },
{ use_auth: true },
];
fileItem.formData.push(...data);
};
uploader.onAfterAddingAll = function (addedFileItems) {
console.info('onAfterAddingAll', addedFileItems);
};
uploader.onBeforeUploadItem = function (item) {
console.info('onBeforeUploadItem', item);
};
uploader.onProgressItem = function (fileItem, progress) {
console.info('onProgressItem', fileItem, progress);
};
uploader.onProgressAll = function (progress) {
console.info('onProgressAll', progress);
};
uploader.onSuccessItem = function (fileItem, response, status, headers) {
console.info('onSuccessItem', fileItem, response, status, headers);
};
uploader.onErrorItem = function (fileItem, response, status, headers) {
console.info('onErrorItem', fileItem, response, status, headers);
};
uploader.onCancelItem = function (fileItem, response, status, headers) {
console.info('onCancelItem', fileItem, response, status, headers);
};
uploader.onCompleteItem = function (fileItem, response, status, headers) {
console.info('onCompleteItem', fileItem, response, status, headers);
};
uploader.onCompleteAll = function () {
console.info('onCompleteAll');
};

function getCategoryInfo(category) {
WikiService.getCategoryInfo(category).then((response) => {
vm.category = response;
@@ -69,12 +147,12 @@ function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $statePa

function getInterwiki() {
const country = getPropertyValue('P17');
const countryLanguages = langService.getNativeLanguages(country.value_id);
const countryLanguages = langService.getNativeLanguages(country.id);

vm.interwiki = {};

vm.interwiki.all = Object.keys(vm.monument.interwiki)
.map(key => vm.monument.interwiki[key])
vm.interwiki.all = Object.keys(vm.monument.sitelinks)
.map(key => vm.monument.sitelinks[key])
.map(element => ({
code: element.site.replace('wiki', ''),
title: element.title,
@@ -98,10 +176,70 @@ function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $statePa
}

function getPropertyValue(prop) {
if (vm.monument.claims[prop] && vm.monument.claims[prop].values.length) {
return vm.monument.claims[prop].values[0];
if (!vm.monument.claims[prop]) return false;
const value = vm.monument.claims[prop][0];

return value.mainsnak.datavalue.value;
}

function labelChange(lang) {
vm.actions.other[0] = {
promise: setLabel,
value: lang,
};
}

function categoryChange(value) {
if (value.searchSelected) {
value.save = undefined;
}
return false;
vm.actions.other[1] = {
type: 'save',
promise: value.id ? setClaimString : addClaimString,
value,
};
}

function queryCommonsCategory(text) {
return WikiService.getCategorySearch(text);
}

function saveSingle(actions, index) {
const promise = actions[index];
return promise.promise(promise.value)
.then((response) => {
if (actions[index + 1]) {
saveSingle(actions, index + 1);
} else {
$state.go($state.current, { id }, { reload: true });
}
})
.catch((err) => {
$mdToast.show($mdToast.simple().textContent(`Error: ${err}`).hideDelay(3000));
if (actions[index + 1]) {
saveSingle(actions, index + 1);
} else {
vm.busy = false;
}
});
}

function saveAll() {
vm.busy = true;
const actions = [...vm.actions.claims, ...vm.actions.other];
saveSingle(actions, 0);
}

function recountQueue() {
const claims = _.values(vm.monument.claims);
vm.actions.claims = [];
claims.forEach((claim) => {
claim.forEach((value) => {
if (value.action) {
vm.actions.claims.push(value.action);
}
});
});
}

function init() {
@@ -110,24 +248,41 @@ function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $statePa
package: pack,
};

WikiService.getUserInfo().then((response) => {
vm.isLoggedIn = response;
});

const queueListener = $rootScope.$on('recountQueue', () => recountQueue());
$scope.$on('$destroy', () => queueListener());

vm.loading = true;

textService.getText().then((data) => {
vm.text = data;
});

wikidata.getById(id).then((data) => {
vm.monument = _.sample(data);
vm.monument = data;

// image
if (getPropertyValue('P18')) {
const image = getPropertyValue('P18').value;
const image = getPropertyValue('P18');
getImage(image);
}
// commons category
if (getPropertyValue('P373')) {
const category = getPropertyValue('P373').value;
const category = getPropertyValue('P373');
getCategoryInfo(category);
getCategoryMembers(category);
} else {
vm.monument.claims.P373 = [{}];
}

getInterwiki();

// coordinates
if (getPropertyValue('P625')) {
const value = getPropertyValue('P625').value;
const value = getPropertyValue('P625');
vm.map = mapService.getMapInstance({
center: {
lat: value.latitude,
@@ -149,8 +304,8 @@ function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $statePa
}
vm.loading = false;

const title = vm.monument.labels[vm.lang.code] || vm.monument.labels.en || vm.monument.id;
$window.document.title = `${title} – Monumental`;
const title = vm.monument.labels[vm.lang.code] || vm.monument.labels.en;
$window.document.title = `${title ? title.value : vm.monument.id} – Monumental`;
});
}

@@ -161,6 +316,26 @@ function controller($anchorScroll, $http, $mdDialog, $mdMenu, $q, $sce, $statePa
list: vm.images,
});
}

function setLabel(lang) {
return WikiService.setLabel(id, lang, vm.monument.labels[lang].newValue);
}

function setClaimString(value) {
return WikiService.setClaimString({
id: value.id,
property: value.mainsnak.property,
value: value.searchSelected.title,
});
}

function addClaimString(value) {
return WikiService.addClaimString({
entity: id,
property: 'P373',
value: value.searchSelected.title,
});
}
}

export default () => {
@@ -181,5 +356,49 @@ export default () => {
});
loadImage();
},
}));
}))
.directive('ngThumb', ['$window', function ($window) {
var helper = {
support: !!($window.FileReader && $window.CanvasRenderingContext2D),
isFile: function (item) {
return angular.isObject(item) && item instanceof $window.File;
},
isImage: function (file) {
var type = '|' + file.type.slice(file.type.lastIndexOf('/') + 1) + '|';
return '|jpg|png|jpeg|bmp|gif|'.indexOf(type) !== -1;
}
};

return {
restrict: 'A',
template: '<canvas/>',
link: function (scope, element, attributes) {
if (!helper.support) return;

var params = scope.$eval(attributes.ngThumb);

if (!helper.isFile(params.file)) return;
if (!helper.isImage(params.file)) return;

var canvas = element.find('canvas');
var reader = new FileReader();

reader.onload = onLoadFile;
reader.readAsDataURL(params.file);

function onLoadFile(event) {
var img = new Image();
img.onload = onLoadImage;
img.src = event.target.result;
}

function onLoadImage() {
var width = params.width || this.width / this.height * params.height;
var height = params.height || this.height / this.width * params.width;
canvas.attr({ width: width, height: height });
canvas[0].getContext('2d').drawImage(this, 0, 0, width, height);
}
}
};
}]);
};
106 changes: 103 additions & 3 deletions src/components/main/monument/monument.scss
Original file line number Diff line number Diff line change
@@ -2,6 +2,15 @@
@import '../../../styles/responsive';

mo-monument {
.busy {
position: fixed;
z-index: 10;
width: 100%;
height: 100%;
top: 0;
background: rgba(255, 255, 255, .75);
}

.monument {
width: 980px;
max-width: 100%;
@@ -52,7 +61,7 @@ mo-monument {
margin-right: 15px;
}

img {
.image {
display: inline-block;
margin-right: 5px;
padding: 5px;
@@ -80,6 +89,13 @@ mo-monument {
max-width: 100%;
max-height: 100%;
}

.material-icons {
font-size: 400%;
opacity: .75;
width: 65px;
height: 65px;
}
}

.monument__data {
@@ -109,6 +125,29 @@ mo-monument {
font-size: 25px;
}

.md-icon-button {
height: 30px;
min-height: 30px;
width: 30px;
margin: -4px 0 0 2px;
padding: 0;
}

md-input-container {
margin-bottom: 0;
margin-top: -5px;
padding: 0;

.md-input {
padding: 0;
padding-bottom: 5px;
}

.md-errors-spacer {
display: none;
}
}

.loading-line {
height: 30px;
}
@@ -182,15 +221,76 @@ mo-monument {

.monument__details {
.monument__details-item {
margin-bottom: 15px;
& > .muted {
margin-bottom: 20px;
}

strong {
margin-right: 20px;
}
}

.monument__details-edit {
margin-bottom: 5px;
}

.monument__details-qualifier {
font-size: 90%;
margin-left: 10px;
}

.monument__details-value {
line-height: 1.5;
line-height: 1.8;

.property__edit {
&.property__edit--deleted {
md-input-container .md-input {
text-decoration: line-through;
}
}

.md-button {
min-width: 36px;
min-height: 36px;
margin: 0 10px 0 10px;

@include x-small { margin: 0 0 0 10px; }
}

md-autocomplete md-autocomplete-wrap {
margin-top: -20px;

md-progress-linear.md-inline {
bottom: 15px;
right: 0;
left: 0;
}

md-input-container {
// margin-bottom: 0;
// margin-top: 0;
padding: 0;

label {
display: none;
}
.md-input {
padding: 0;
padding-bottom: 0;
}

.md-errors-spacer {
display: none;
}
}
}

& > md-input-container,
& > md-select {
margin: 0;
padding: 0;
}
}
}
}

13 changes: 8 additions & 5 deletions src/components/toolbar/toolbar.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ const ToolbarComponent = { bindings: { wide: '=' }, controller, template };

function controller($document, $mdSidenav, $mdToast, $state, $timeout, $window, WikiService, wikidata) {
const vm = this;
const baseUrl = $window.__env.baseUrl;

vm.isLoggedIn = false;
vm.loading = true;
vm.mobile = {};
@@ -38,10 +40,10 @@ function controller($document, $mdSidenav, $mdToast, $state, $timeout, $window,
if (!item) { return; }
wikidata.getRecursive(item.id, 'wdt:P31/wdt:P279').then((response) => {
const ids = response.map(prop => prop.value_id);
if (ids.includes('Q811979')) {
if (ids.includes('Q56061') || ids.includes('Q5107')) {
$state.go('main.list', { id: item.id.substring(1), heritage: 1, c: undefined });
} else if (ids.includes('Q811979')) {
$state.go('main.object', { id: item.id.substring(1) });
} else if (ids.includes('Q56061')) {
$state.go('main.list', { id: item.id.substring(1) });
} else {
$state.go('main.object', { id: item.id.substring(1) });
/*
@@ -64,11 +66,12 @@ function controller($document, $mdSidenav, $mdToast, $state, $timeout, $window,

function login() {
vm.loading = true;
$window.location.pathname = `${$window.__env.baseUrl}/login`;
const current = $window.location.href;
$window.location.href = `${baseUrl}/login?next=${encodeURIComponent(current)}`;
}

function logout() {
$window.location.pathname = `${$window.__env.baseUrl}/logout`;
$window.location.href = `${baseUrl}/logout`;
}

function openSearch() {
Binary file added src/images/marker-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/images/marker-red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 8 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import angular from 'angular';
import _ from 'lodash';

import 'angular-animate';
import 'angular-file-upload';
import 'angular-leaflet-directive';
import 'angular-local-storage';
import 'angular-material';
@@ -14,12 +15,12 @@ import 'leaflet.markercluster';
import 'ng-infinite-scroll';
import 'restangular';

import './styles/style.scss';
import 'angular-material/angular-material.css';
import 'leaflet/dist/leaflet.css';
import 'leaflet.markercluster/dist/MarkerCluster.css';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
import 'material-design-icons/iconfont/material-icons.css';
import './styles/style.scss';

import components from './components';
import services from './services';
@@ -28,6 +29,7 @@ window._ = _;

angular
.module('monumental', [
'angularFileUpload',
'ngAnimate',
'ngMaterial',
'ngMessages',
@@ -65,7 +67,7 @@ function localStorageConfig(localStorageServiceProvider) {
* @param {any} $stateProvider
* @param {any} $urlRouterProvider
*/
function stateConfig($stateProvider, $urlRouterProvider) {
function stateConfig($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('main', {
template: '<mo-main></mo-main>',
@@ -77,12 +79,12 @@ function stateConfig($stateProvider, $urlRouterProvider) {
resolve: {},
})
.state('main.list', {
url: '/list/:id',
url: '/list/:id?c&heritage&image&type&wikipedia',
template: '<mo-list></mo-list>',
resolve: {},
})
.state('main.map', {
url: '/map',
url: '/map?c&heritage&image&type&wikipedia',
template: '<mo-map></mo-map>',
resolve: {},
})
@@ -102,6 +104,7 @@ function stateConfig($stateProvider, $urlRouterProvider) {
resolve: {},
});
$urlRouterProvider.otherwise('/');
// $locationProvider.html5Mode(true);
}

/**
@@ -120,7 +123,7 @@ function themeConfig($mdThemingProvider, $provide) {
// tp.alwaysWatchTheme(true);
tp.theme('default')
.primaryPalette('belize')
.accentPalette('orange');
.accentPalette('grey');

$provide.value('themeProvider', tp);
}
4 changes: 3 additions & 1 deletion src/index_dev.ejs
Original file line number Diff line number Diff line change
@@ -8,14 +8,16 @@
<meta name="apple-mobile-web-app-status-bar-style" content="#2980b9">
<link rel="manifest" href="manifest.json">
<title>Monumental</title>
<link href="https://tools-static.wmflabs.org/fontcdn/css?family=Roboto:300,400,500,700&subset=latin,latin-ext" rel="stylesheet">
<link href="https://tools-static.wmflabs.org/fontcdn/css?family=Merriweather&subset=cyrillic,cyrillic-ext,latin-ext" rel="stylesheet">
</head>
<body ng-app="monumental" ng-cloak>
<ui-view></ui-view>
<script>
(function (window) {
window.__env = window.__env || {};
window.__env.name = 'dev';
window.__env.baseUrl = '/monumental';
window.__env.baseUrl = '//tools.wmflabs.org/monumental';
}(this));
</script>
</body>
4 changes: 3 additions & 1 deletion src/index_local.ejs
Original file line number Diff line number Diff line change
@@ -8,14 +8,16 @@
<meta name="apple-mobile-web-app-status-bar-style" content="#2980b9">
<link rel="manifest" href="manifest.json">
<title>Monumental (Localhost)</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&subset=latin,latin-ext" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Merriweather&subset=cyrillic,cyrillic-ext,latin-ext" rel="stylesheet">
</head>
<body ng-app="monumental" ng-cloak>
<ui-view></ui-view>
<script>
(function (window) {
window.__env = window.__env || {};
window.__env.name = 'local';
window.__env.baseUrl = '';
window.__env.baseUrl = '//localhost:5000';
}(this));
</script>
</body>
3 changes: 3 additions & 0 deletions src/services/index.js
Original file line number Diff line number Diff line change
@@ -3,11 +3,14 @@ import map from './map.service';
import theme from './theme.service';
import wiki from './wiki.service';
import wikidata from './wikidata.service';
import text from './text.service';


export default () => {
lang();
map();
theme();
wiki();
wikidata();
text();
};
3 changes: 2 additions & 1 deletion src/services/lang.service.js
Original file line number Diff line number Diff line change
@@ -198,6 +198,7 @@ const LangService = function ($filter, $q, localStorageService) {
Q805: ['ar'],
Q953: ['en'],
Q954: ['en'],
Q148: ['zh'],
};
const languageList = [
{ code: 'en', name: 'English', nativeName: 'English' },
@@ -521,7 +522,7 @@ const LangService = function ($filter, $q, localStorageService) {

const langs = ls || [def];
if (!langs.includes('en')) { langs.push('en'); }
userLanguages = langs.map(lang => $filter('filter')(languageList, lang)[0]);
userLanguages = langs.map(lang => languageList.filter(l => l.code === lang)[0]);
return angular.copy(userLanguages);
}

39 changes: 32 additions & 7 deletions src/services/map.service.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import L from 'leaflet';

import '../images/marker-green.png';
import '../images/marker-red.png';
import '../images/marker-shadow.png';

@@ -12,25 +15,38 @@ const MapService = () => {
//

function getMapIcon(options) {
return angular.extend({
iconUrl: 'assets/images/marker-red.png',
return {
iconUrl: `assets/images/marker-${options && options.image ? 'green' : 'red'}.png`,
shadowUrl: 'assets/images/marker-shadow.png',
iconSize: [29, 41],
shadowSize: [41, 41],
iconAnchor: [15, 41],
shadowAnchor: [12, 41],
popupAnchor: [0, -43],
}, options);
};
}

function getMapInstance(options) {
return angular.extend({
markersWatchOptions: {
doWatch: true,
isDeep: false,
individual: {
doWatch: false,
isDeep: false,
},
},
center: {
lat: 51.686,
lng: 19.545,
zoom: 7,
},
markers: {},
events: {
markers: {
enable: ['click', 'mouseover', 'mouseout'],
},
},
layers: {
baselayers: {
wiki: {
@@ -39,8 +55,10 @@ const MapService = () => {
url: '//maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png',
layerOptions: {
subdomains: ['a', 'b', 'c'],
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
attribution: '&copy; <a href="//www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
continuousWorld: true,
maxNativeZoom: 18,
maxZoom: 21,
},
},
osm: {
@@ -49,8 +67,10 @@ const MapService = () => {
url: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
layerOptions: {
subdomains: ['a', 'b', 'c'],
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
attribution: '&copy; <a href="//www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
continuousWorld: true,
maxNativeZoom: 19,
maxZoom: 21,
},
},
},
@@ -60,10 +80,15 @@ const MapService = () => {
type: 'markercluster',
visible: true,
layerOptions: {
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
disableClusteringAtZoom: 17,
maxClusterRadius: zoom => 130 - (zoom * 5),
animate: false,
iconCreateFunction: cluster => new L.DivIcon({
html: `<div><span>${cluster.getChildCount()}</span></div>`,
className: 'marker-cluster marker-cluster-small',
iconSize: new L.Point(40, 40),
}),
},
},
},
34 changes: 34 additions & 0 deletions src/services/text.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const TextService = ($q, wikidata) => {
const service = {
getText,
};
let texts = null;

return service;

function getPropertiesLabels() {
const ids = [
'P31', 'P366', 'P186', 'P149',
'P669', 'P276', 'P625',
'P2048', 'P2046', 'P1101', 'P1139', 'P1301',
'P84', 'P287', 'P631', 'P193', 'P1028', 'P127',
'P361', 'P527',
'P580', 'P582', 'P585', 'P805', 'P518', 'P670', 'P1706', // qualifiers
'Q11573', 'Q25343',
];
return wikidata.getLabels(ids).then((data) => {
texts = data;
return texts;
});
}

function getText() {
return texts ? $q.when(texts) : getPropertiesLabels();
}
};

export default () => {
angular
.module('monumental')
.factory('textService', TextService);
};
159 changes: 156 additions & 3 deletions src/services/wiki.service.js
Original file line number Diff line number Diff line change
@@ -3,13 +3,22 @@ import _ from 'lodash';
const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wikidata) {
const service = {
addCategory,
addClaimItem,
addClaimString,
getArticleHeader,
getFilesCategories,
getFormattedTime,
getCategoryInfo,
getCategoryMembers,
getCategorySearch,
getImage,
getLabel,
getUserInfo,
setClaim,
removeClaim,
setClaimItem,
setClaimQuantity,
setClaimString,
setLabel,
};

const appAPI = `${$window.__env.baseUrl}/api`;
@@ -47,7 +56,7 @@ const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wi
if (response) {
return $q.reject('Category is already added');
}
return setClaim({
return postWikidata({
action: 'wbcreateclaim',
format: 'json',
entity: `${id}`,
@@ -59,6 +68,17 @@ const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wi
});
}

function setLabel(id, lang, value) {
return postWikidata({
action: 'wbsetlabel',
format: 'json',
id: `${id}`,
language: `${lang}`,
summary: '#monumental',
value: `${value}`,
});
}

function getArticleHeader(lang, title) {
const params = angular.extend({}, defaultParams, {
prop: 'extracts',
@@ -99,6 +119,20 @@ const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wi
});
}

function getCategorySearch(name) {
const params = angular.extend({}, defaultParams, {
action: 'query',
list: 'search',
srsearch: name,
srnamespace: 14,
srlimit: 20,
srprop: 'timestamp',
});
return $http.jsonp('https://commons.wikimedia.org/w/api.php', {
params,
}).then(response => response.data.query.search);
}

function getFilesCategories(files) {
const params = angular.extend({}, defaultParams, {
prop: 'categories',
@@ -111,6 +145,17 @@ const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wi
}).then(response => response.data.query.pages);
}

function getFormattedTime(value, lang) {
return wikidata.get({
action: 'wbformatvalue',
props: undefined,
options: angular.toJson({ lang }),
generate: 'text/plain',
property: 'P585',
datavalue: angular.toJson({ value, type: 'time' }),
}).then(response => response.result);
}

function getImage(image, extraParams) {
const params = angular.extend({}, imageParams, { titles: `File:${image}` }, extraParams);
return $http.jsonp('https://commons.wikimedia.org/w/api.php', {
@@ -126,6 +171,11 @@ const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wi
});
}

function getLabel(id) {
return wikidata.getLabels([id])
.then(response => response[id]);
}

function getUserInfo() {
return $http.get(appAPI, {
params: {
@@ -141,10 +191,113 @@ const WikiService = function ($http, $httpParamSerializerJQLike, $q, $window, wi
});
}

function setClaim(params) {
function addClaimItem(value) {
return postWikidata({
action: 'wbcreateclaim',
format: 'json',
entity: value.entity,
property: value.property,
snaktype: 'value',
value: angular.toJson({
'entity-type': 'item',
'numeric-id': value.value,
}),
summary: '#monumental',
});
}

function addClaimString(value) {
return postWikidata({
action: 'wbcreateclaim',
format: 'json',
entity: value.entity,
property: value.property,
snaktype: 'value',
value: value.value,
summary: '#monumental',
});
}

function removeClaim(value) {
return postWikidata({
action: 'wbremoveclaims',
format: 'json',
claim: value.id,
summary: '#monumental',
});
}

function setClaimItem(value) {
return postWikidata({
action: 'wbsetclaim',
format: 'json',
claim: angular.toJson({
id: value.id,
type: 'claim',
mainsnak: {
snaktype: 'value',
property: value.property,
datavalue: {
type: 'wikibase-entityid',
value: {
'entity-type': 'item',
'numeric-id': value.value,
},
},
},
}),
summary: '#monumental',
});
}

function setClaimQuantity(value) {
return postWikidata({
action: 'wbsetclaim',
format: 'json',
claim: angular.toJson({
id: value.id,
type: 'claim',
mainsnak: {
snaktype: 'value',
property: value.property,
datavalue: {
type: 'quantity',
value: {
amount: value.value,
unit: value.unit,
},
},
},
}),
summary: '#monumental',
});
}

function setClaimString(value) {
return postWikidata({
action: 'wbsetclaim',
format: 'json',
claim: angular.toJson({
id: value.id,
type: 'claim',
mainsnak: {
snaktype: 'value',
property: value.property,
datavalue: {
type: 'string',
value: value.value,
},
},
}),
summary: '#monumental',
});
}

function postWikidata(params) {
return $http({
method: 'POST',
url: appAPI,
// data: angular.extend({ use_auth: true }, params),
data: $httpParamSerializerJQLike(angular.extend({ use_auth: true }, params)),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
}).then((response) => {
Loading