diff --git a/data/characterData.js b/data/characterData.js new file mode 100644 index 0000000..2bc043e --- /dev/null +++ b/data/characterData.js @@ -0,0 +1,67 @@ +const DATA_CHARACTERS = { + "characters": [ + { + "name": "Nori", + "series": "Rings of Power", + "seasons": [1, 2, 3], + "color": "#2f4f22", + "portrait": "img/portraits/harfoots.webp" + }, + { + "name": "Galadriel", + "series": "Rings of Power", + "seasons": [1, 2, 3], + "color": "#3c76b1", + "portrait": "img/portraits/galadriel.webp" + }, + { + "name": "Elrond", + "series": "Rings of Power", + "seasons": [1, 2, 3], + "color": "#907826", + "portrait": "img/portraits/" + }, + { + "name": "Arondir", + "series": "Rings of Power", + "seasons": [1, 2, 3], + "color": "#81639f", + "portrait": "img/portraits/" + }, + { + "name": "Elendil", + "series": "Rings of Power", + "seasons": [1, 2, 3], + "color": "#494949", + "portrait": "img/portraits/" + }, + { + "name": "Halbrand", + "series": "Rings of Power", + "seasons": [1, 2, 3], + "color": "#a9531f", + "portrait": "img/portraits/" + }, + { + "name": "Frodo and Sam", + "series": "The Lord of the Rings: The Fellowship of the Ring", + "seasons": [100], + "color": "#912222", + "portrait": "img/portraits/frodosam.webp" + }, + { + "name": "Elrond", + "series": "The Lord of the Rings: The Fellowship of the Ring", + "seasons": [100], + "color": "#907826", + "portrait": "img/portraits/" + }, + { + "name": "Bilbo and Thorin", + "series": "The Hobbit", + "seasons": [104], + "color": "#4e2291", + "portrait": "img/portraits/" + } + ] +}; \ No newline at end of file diff --git a/data/markers.js b/data/dataMarkerMarkers.js similarity index 86% rename from data/markers.js rename to data/dataMarkerMarkers.js index 3afbc70..1ee6df2 100644 --- a/data/markers.js +++ b/data/dataMarkerMarkers.js @@ -1,41 +1,4 @@ const DATA_MARKERS = { - "types": [ - { - "name": "area", - "icon": "area.webp", - "iconSize": [30, 30], - "iconAnchor": [15, 15], - "popupAnchor": [0, -5] - }, - { - "name": "point of interest", - "icon": "poi.webp", - "iconSize": [30, 30], - "iconAnchor": [15, 15], - "popupAnchor": [0, -5] - }, - { - "name": "village", - "icon": "village.webp", - "iconSize": [30, 30], - "iconAnchor": [15, 15], - "popupAnchor": [0, -5] - }, - { - "name": "city", - "icon": "city.webp", - "iconSize": [30, 30], - "iconAnchor": [15, 15], - "popupAnchor": [0, -5] - }, - { - "name": "event", - "icon": "event.webp", - "iconSize": [30, 30], - "iconAnchor": [15, 15], - "popupAnchor": [0, -5] - } - ], "markers": [ { "title": "Hobbiton", @@ -43,14 +6,15 @@ const DATA_MARKERS = { "type": "village", "image": "hobbiton.webp", "episodes": [ - {"episode": 1, "season": 100} + {"episode": 1, "season": 100}, + {"episode": 1, "season": 104} ], "decription": "Hobbiton is a Hobbit village in the central regions of the Shire, within the borders of the Westfarthing.", "readMoreUrl": "https://tolkiengateway.net/wiki/Hobbiton", "isConfirmed": true }, { - "title": "Passing of the Evles", + "title": "Passing of the Elves", "coordinates": [771, 631.8], "type": "event", "image": "passing-elves.webp", @@ -79,7 +43,8 @@ const DATA_MARKERS = { "type": "village", "image": "bree.webp", "episodes": [ - {"episode": 1, "season": 100} + {"episode": 1, "season": 100}, + {"episode": 1, "season": 104} ], "decription": "Bree is a village of Men and hobbits, located east of the Shire and south of Fornost in Eriador.", "readMoreUrl": "https://tolkiengateway.net/wiki/Bree", @@ -139,7 +104,8 @@ const DATA_MARKERS = { "type": "city", "image": "rivendell.webp", "episodes": [ - {"episode": 1, "season": 100} + {"episode": 1, "season": 100}, + {"episode": 1, "season": 104} ], "decription": "Rivendell, or Imladris, is an Elven outpost established by Elrond in the Misty Mountains on the eastern edge of Eriador.", "readMoreUrl": "https://tolkiengateway.net/wiki/Rivendell", @@ -156,6 +122,18 @@ const DATA_MARKERS = { "decription": "A blizzard summoned by Saruman forces the Fellowship of the Ring to turn back.", "readMoreUrl": "https://tolkiengateway.net/wiki/Misty_Mountains", "isConfirmed": true + }, + { + "title": "The Lonely Mountain", + "coordinates": [851.25, 920.75], + "type": "area", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "The Lonely Mountain. Erebor", + "readMoreUrl": "https://tolkiengateway.net/wiki/Lonely_Mountain", + "isConfirmed": true }, { "title": "Caras Galadhon", @@ -193,6 +171,30 @@ const DATA_MARKERS = { "readMoreUrl": "https://tolkiengateway.net/wiki/Rauros", "isConfirmed": true }, + { + "title": "Erech", + "coordinates": [564, 799.25], + "type": "point of interest", + "image": "", + "episodes": [ + {"episode": 1, "season": 100} + ], + "decription": "Paths of the Dead", + "readMoreUrl": "https://tolkiengateway.net/wiki/Erech", + "isConfirmed": true + }, + { + "title": "Amon Hen", + "coordinates": [597.25, 859.875], + "type": "point of interest", + "image": "", + "episodes": [ + {"episode": 1, "season": 100} + ], + "decription": "An ancient watchtower hill with ruins, located by the wooded slopes of Parth Galen. The location Boromir is killed by Lurtz", + "readMoreUrl": "https://tolkiengateway.net/wiki/Amon_Hen", + "isConfirmed": true + }, { "title": "Emyn Arnen", "coordinates": [603.5, 881], @@ -277,6 +279,78 @@ const DATA_MARKERS = { "readMoreUrl": "", "isConfirmed": true }, + { + "title": "ElvenKing's Halls", + "coordinates": [834.5, 900.31], + "type": "city", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "", + "readMoreUrl": "", + "isConfirmed": false + }, + { + "title": "Dol Guldur", + "coordinates": [707.5, 865.75], + "type": "city", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "", + "readMoreUrl": "", + "isConfirmed": false + }, + { + "title": "Goblin-town", + "coordinates": [807.25, 814.5], + "type": "city", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "The Goblin King (AKA The Great Goblin) rules from Goblin-town, a vast network of caves and tunnels deep within the Misty Mountains.", + "readMoreUrl": "", + "isConfirmed": false + }, + { + "title": "Beorn's Home", + "coordinates": [809, 840.75], + "type": "point of interest", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "Beorn, a powerful skin-changer who can transform into a giant black bear, known for his immense strength, love of animals, and crucial intervention in the Battle of the Five Armies. He's a solitary, gruff figure who lives between the Misty Mountains and Mirkwood", + "readMoreUrl": "", + "isConfirmed": false + }, + { + "title": "The Elf-path", + "coordinates": [827.75, 863.25], + "type": "event", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "Bilbo Baggins and the Dwarves encounter a large number of giant spiders in the northern section of Mirkwood, after they stray from the narrow Elf-path while trying to cross the forest", + "readMoreUrl": "", + "isConfirmed": false + }, + { + "title": "Lake-Town (Esgaroth)", + "coordinates": [834.25, 920.75], + "type": "city", + "image": "", + "episodes": [ + {"episode": 1, "season": 104} + ], + "decription": "", + "readMoreUrl": "", + "isConfirmed": false + }, { "title": "Grey Marshes", "coordinates": [599.6, 889.9], @@ -430,7 +504,8 @@ const DATA_MARKERS = { {"episode": 2, "season": 1}, {"episode": 3, "season": 1}, {"episode": 4, "season": 1}, - {"episode": 5, "season": 1} + {"episode": 5, "season": 1}, + {"episode": 1, "season": 2} ], "decription": "Mithlond, known also as Grey Havens, is the capital of the high elves, ruled by Gil-galad.", "readMoreUrl": "https://tolkiengateway.net/wiki/Grey_Havens", diff --git a/data/dataMarkerTypes.js b/data/dataMarkerTypes.js new file mode 100644 index 0000000..5969760 --- /dev/null +++ b/data/dataMarkerTypes.js @@ -0,0 +1,39 @@ +const DATA_MARKER_TYPES = { + "types": [ + { + "name": "area", + "icon": "area.webp", + "iconSize": [30, 30], + "iconAnchor": [15, 15], + "popupAnchor": [0, -5] + }, + { + "name": "point of interest", + "icon": "poi.webp", + "iconSize": [30, 30], + "iconAnchor": [15, 15], + "popupAnchor": [0, -5] + }, + { + "name": "village", + "icon": "village.webp", + "iconSize": [30, 30], + "iconAnchor": [15, 15], + "popupAnchor": [0, -5] + }, + { + "name": "city", + "icon": "city.webp", + "iconSize": [30, 30], + "iconAnchor": [15, 15], + "popupAnchor": [0, -5] + }, + { + "name": "event", + "icon": "event.webp", + "iconSize": [30, 30], + "iconAnchor": [15, 15], + "popupAnchor": [0, -5] + } + ] +} \ No newline at end of file diff --git a/data/paths-season1-lotr.js b/data/paths-season1-lotr.js new file mode 100644 index 0000000..dd2be96 --- /dev/null +++ b/data/paths-season1-lotr.js @@ -0,0 +1,159 @@ +DATA_PATHS.paths.push( + { + "character": "Frodo and Sam", + "season": 100, + "episode": 1, + "isConfirmed": true, + "coordinates": [ + [779.7, 625.7], + [771, 631.8], + [773.5, 646.8], + [779.1, 652], + [777, 662.5], + [776.6, 674.1], + [781, 676], + [786.2, 680.2], + [788.2, 685.8], + [786.8, 698.2], + [785.3, 702.2], + [786.3, 709.3], + [779.6, 715.3], + [779.3, 721.3], + [782.7, 731.5], + [786.6, 741.7], + [787.2, 746.3], + [789, 754.3], + [790.6, 757.2], + [793.8, 762.5], + [792.5, 772.9], + [785.8, 780], + [785.6, 784,5], + [786.6, 788.1], + [787.5, 789.3], + [790.8, 790], + [785, 790.6], + [776.1, 789], + [762.1, 780.5], + [745.8, 769.6], + [743.4, 773.6], + [745, 785.6], + [748.9, 795.7], + [742.6, 786], + [742, 779.6], + [739.2, 770.2], + [733.9, 767.9], + [730.3, 769.6], + [729.1, 772.4], + [729.4, 778.3], + [730.3, 782.7], + [727.4, 787.3], + [726.1, 791], + [726, 793.9], + [723, 794.5], + [721.5, 796.1], + [715, 801.1], + [708.6, 805.3], + [706.1, 810.2], + [705.6, 817], + [700.5, 822.1], + [697.5, 826.8], + [693.9, 829], + [690.6, 829.9], + [688.9, 831.2], + [683.2, 831.8], + [679.4, 834.4], + [677.1, 837.4], + [676.1, 840.5], + [673.3, 845.4], + [674.3, 848.4], + [672.5, 851.5], + [669.0625, 851.375], + [665.5625, 848.8125], + [662.75, 848.1875], + [659.9375, 849.875], + [658.6875, 852.5], + [658.4375, 855], + [659.5625, 857.4375], + [661, 858.625], + [660.375, 860.875], + [658, 862.9375], + [654.8125, 863.5], + [652.875, 862.3125], + [651.8125, 859.5], + [650.125, 857.5], + [647.375, 858.25], + [645.9375, 860.25], + [646.8125, 863.4375], + [645.125, 865.75], + [643.6875, 866.3125], + [643.1875, 868.125], + [639.625, 868.5625], + [634.9375, 866.3125], + [629.3125, 864.75], + [624.625, 863.1875], + [620.1875, 863.6875], + [615.5625, 864.375], + [610.25, 865.3125], + [604.875, 866.5625], + [601.0625, 865.3125], + [598.625, 865.25], + [597.25, 866.625], + [594.8125, 866.1875], + [596.375, 871.8125], + [600.0625, 874.6875], + [602.5, 878.4375], + [604.0625, 882.625], + [603.3125, 886.625], + [603, 893.0625], + [604.75, 900.125], + [602.6875, 905.5], + [601.4375, 913.375], + [598, 918.9375], + [595.1875, 924.6875], + [594.3125, 927.625], + [593.8125, 923], + [592.3125, 917.5], + [589.875, 914.1875], + [580.625, 913.625], + [573.75, 913], + [561.125, 913.75], + [553.5, 914.125], + [548.375, 914.875], + [546.1, 912], + [545.75, 916.125], + [545.8125, 921.5625], + [545.5, 924.4375], + [546.9375, 927.6875], + [548.0625, 930.4375], + [549, 932.5625], + [547.5625, 935.25], + [545.8125, 937.6875], + [550.125, 939.25], + [555.5, 938.625], + [559.5625, 937.75], + [563.75, 935.625], + [568.4375, 934.3125], + [571.5, 934.1875], + [574.125, 934.8125], + [578.625, 937], + [579.3125, 941.625], + [565.25, 956.5] + ] + }, + { + "character": "Frodo and Sam", + "season": 100, + "episode": 1, + "isConfirmed": false, + "coordinates": [[565.25, 956.5], [545.875, 902], [550.25, 898.75], [553.125, 892.625], [556.5, 876.875], [561.5, 852.75], [567.125, 831.25], [572.25, 818.25], [575.25, 814.375], [579.25, 811.625], [583.125, 811.125], [586.875, 812.5], [590, 811.25], [591.25, 799.625], [592.625, 795.625], [601.125, 783.875], [606.75, 772.625], [611.875, 766.875], [614.125, 761.75], [614.25, 754.625], [613.5, 748.875], [614.875, 739.75], [618, 729.625], [622.25, 722.75], [626.375, 718.875], [634.75, 714.625], [644.25, 712.75], [653.5, 714.375], [662.375, 717.375], [675.5, 718.625], [688.875, 717.5], [696.375, 715.25], [703.5, 711.75], [708.125, 708.375], [710.125, 705.875], [717.875, 703.875], [726, 700.125], [731.375, 695.25], [738, 687.5], [741.5, 682], [743.75, 677.75], [747.375, 671], [748.375, 664.5], [748.375, 656.125], [749, 650.625], [751, 645.125], [755, 641.625], [759.875, 638.5], [766.125, 634.5], [769, 630.625], [770.625, 627.375], [774.375, 624], [777.875, 622.125], [779.5, 625.875], [781.375, 617.125], [780.5, 610.875], [783.375, 603.875], [784.375, 596.125], [784.375, 589.75], [783.125, 582.5], [782, 577.75], [780.75, 573], [780.5, 570], [780, 564.375], [774.5, 555.875], [771.125, 545.125], [770.5, 532.125], [769.625, 522], [768, 509.75], [764.625, 500.125], [756.625, 489.5], [737.5, 446.25], [710, 412.25], [152, 14]] + }, + { + "character": "Bilbo and Thorin", + "season": 104, + "episode": 1, + "isConfirmed": true, + "coordinates": [ + [779.375, 626.625], [779.625, 631.75], [781.875, 636.875], [784.125, 640.625], [785.75, 644.125], [785.875, 647.5], [786.625, 653.625], [788.375, 659.375], [788.875, 665], [787.375, 668.5], [784.125, 670.875], [781.75, 673.25], [781.25, 676.25], [782.375, 681.25], [783.375, 688.375], [782.75, 694.25], [781.25, 699.625], [780.375, 704.875], [781.75, 712.5], [783.75, 719.5], [787.125, 727.875], [788.75, 734], [790, 740.5], [789.75, 745.875], [789.125, 752.75], [788.375, 760.75], [787.5, 768.125], [786.375, 776.25], [785.625, 781.375], [786.125, 786.125], [787.125, 789.125], [789.25, 789.75], [790.625, 787.875], [794, 793.6875], [795, 797], [794.9375, 800.1875], [795.9375, 802.875], [799.75, 805.5], [803.5625, 810.25], [805.125, 814.0625], [807.5, 814.9375], [809.8125, 817.5], [810.5625, 821.0625], [815.125, 820.4375], [814.3125, 824.3125], [813.25, 825.875], [811.5625, 827.8125], [809.75, 829.5], [808.4375, 831.625], [807.625, 833.8125], [806.6875, 836], [806.875, 838.375], [809.625, 840], [812.75, 840.625], [816.625, 840.25], [819.625, 839.5], [822.875, 839.125], [826.5, 839.5], [828.75, 842.875], [828.5, 853.5], [827.25, 863.375], [826.75, 872.25], [827, 880.125], [826.125, 887.625], [825.25, 894], [828.75, 898], [836.625, 899], [834.5, 900.375], [833.625, 903.75], [833.875, 906.9375], [833.6875, 909.5625], [833.0625, 911.125], [834.3125, 913.3125], [834.625, 915.75], [833.625, 918.75], [833.625, 920.875], [837.1875, 921.875], [838.9375, 922.375], [840.125, 923.3125], [841.9375, 922.6875], [842.4375, 920.5], [843.5, 919.625], [845, 921], [845.875, 918.75] + ] + } +); \ No newline at end of file diff --git a/data/paths.js b/data/paths-season1-rop.js similarity index 73% rename from data/paths.js rename to data/paths-season1-rop.js index 70cbe85..bd5ff5d 100644 --- a/data/paths.js +++ b/data/paths-season1-rop.js @@ -1,196 +1,5 @@ const DATA_PATHS = { - "characters": [ - { - "name": "Nori", - "color": "#2f4f22" - }, - { - "name": "Galadriel", - "color": "#3c76b1" - }, - { - "name": "Elrond", - "color": "#907826" - }, - { - "name": "Arondir", - "color": "#81639f" - }, - { - "name": "Elendil", - "color": "#494949" - }, - { - "name": "Halbrand", - "color": "#a9531f" - }, - { - "name": "Frodo and Sam", - "color": "#912222" - }, - { - "name": "Bilbo and Thorin", - "color": "#4e2291" - }, - ], "paths": [ - { - "character": "Frodo and Sam", - "season": 100, - "episode": 1, - "isConfirmed": true, - "coordinates": [ - [779.7, 625.7], - [771, 631.8], - [773.5, 646.8], - [779.1, 652], - [777, 662.5], - [776.6, 674.1], - [781, 676], - [786.2, 680.2], - [788.2, 685.8], - [786.8, 698.2], - [785.3, 702.2], - [786.3, 709.3], - [779.6, 715.3], - [779.3, 721.3], - [782.7, 731.5], - [786.6, 741.7], - [787.2, 746.3], - [789, 754.3], - [790.6, 757.2], - [793.8, 762.5], - [792.5, 772.9], - [785.8, 780], - [785.6, 784,5], - [786.6, 788.1], - [787.5, 789.3], - [790.8, 790], - [785, 790.6], - [776.1, 789], - [762.1, 780.5], - [745.8, 769.6], - [743.4, 773.6], - [745, 785.6], - [748.9, 795.7], - [742.6, 786], - [742, 779.6], - [739.2, 770.2], - [733.9, 767.9], - [730.3, 769.6], - [729.1, 772.4], - [729.4, 778.3], - [730.3, 782.7], - [727.4, 787.3], - [726.1, 791], - [726, 793.9], - [723, 794.5], - [721.5, 796.1], - [715, 801.1], - [708.6, 805.3], - [706.1, 810.2], - [705.6, 817], - [700.5, 822.1], - [697.5, 826.8], - [693.9, 829], - [690.6, 829.9], - [688.9, 831.2], - [683.2, 831.8], - [679.4, 834.4], - [677.1, 837.4], - [676.1, 840.5], - [673.3, 845.4], - [674.3, 848.4], - [672.5, 851.5], - [669.0625, 851.375], - [665.5625, 848.8125], - [662.75, 848.1875], - [659.9375, 849.875], - [658.6875, 852.5], - [658.4375, 855], - [659.5625, 857.4375], - [661, 858.625], - [660.375, 860.875], - [658, 862.9375], - [654.8125, 863.5], - [652.875, 862.3125], - [651.8125, 859.5], - [650.125, 857.5], - [647.375, 858.25], - [645.9375, 860.25], - [646.8125, 863.4375], - [645.125, 865.75], - [643.6875, 866.3125], - [643.1875, 868.125], - [639.625, 868.5625], - [634.9375, 866.3125], - [629.3125, 864.75], - [624.625, 863.1875], - [620.1875, 863.6875], - [615.5625, 864.375], - [610.25, 865.3125], - [604.875, 866.5625], - [601.0625, 865.3125], - [598.625, 865.25], - [597.25, 866.625], - [594.8125, 866.1875], - [596.375, 871.8125], - [600.0625, 874.6875], - [602.5, 878.4375], - [604.0625, 882.625], - [603.3125, 886.625], - [603, 893.0625], - [604.75, 900.125], - [602.6875, 905.5], - [601.4375, 913.375], - [598, 918.9375], - [595.1875, 924.6875], - [594.3125, 927.625], - [593.8125, 923], - [592.3125, 917.5], - [589.875, 914.1875], - [580.625, 913.625], - [573.75, 913], - [561.125, 913.75], - [553.5, 914.125], - [548.375, 914.875], - [546.1, 912], - [545.75, 916.125], - [545.8125, 921.5625], - [545.5, 924.4375], - [546.9375, 927.6875], - [548.0625, 930.4375], - [549, 932.5625], - [547.5625, 935.25], - [545.8125, 937.6875], - [550.125, 939.25], - [555.5, 938.625], - [559.5625, 937.75], - [563.75, 935.625], - [568.4375, 934.3125], - [571.5, 934.1875], - [574.125, 934.8125], - [578.625, 937], - [579.3125, 941.625], - [565.25, 956.5] - ] - }, - { - "character": "Frodo and Sam", - "season": 100, - "episode": 1, - "isConfirmed": false, - "coordinates": [[565.25, 956.5], [545.875, 902], [550.25, 898.75], [553.125, 892.625], [556.5, 876.875], [561.5, 852.75], [567.125, 831.25], [572.25, 818.25], [575.25, 814.375], [579.25, 811.625], [583.125, 811.125], [586.875, 812.5], [590, 811.25], [591.25, 799.625], [592.625, 795.625], [601.125, 783.875], [606.75, 772.625], [611.875, 766.875], [614.125, 761.75], [614.25, 754.625], [613.5, 748.875], [614.875, 739.75], [618, 729.625], [622.25, 722.75], [626.375, 718.875], [634.75, 714.625], [644.25, 712.75], [653.5, 714.375], [662.375, 717.375], [675.5, 718.625], [688.875, 717.5], [696.375, 715.25], [703.5, 711.75], [708.125, 708.375], [710.125, 705.875], [717.875, 703.875], [726, 700.125], [731.375, 695.25], [738, 687.5], [741.5, 682], [743.75, 677.75], [747.375, 671], [748.375, 664.5], [748.375, 656.125], [749, 650.625], [751, 645.125], [755, 641.625], [759.875, 638.5], [766.125, 634.5], [769, 630.625], [770.625, 627.375], [774.375, 624], [777.875, 622.125], [779.5, 625.875], [781.375, 617.125], [780.5, 610.875], [783.375, 603.875], [784.375, 596.125], [784.375, 589.75], [783.125, 582.5], [782, 577.75], [780.75, 573], [780.5, 570], [780, 564.375], [774.5, 555.875], [771.125, 545.125], [770.5, 532.125], [769.625, 522], [768, 509.75], [764.625, 500.125], [756.625, 489.5], [737.5, 446.25], [710, 412.25], [152, 14]] - }, - { - "character": "Bilbo and Thorin", - "season": 101, - "episode": 1, - "isConfirmed": true, - "coordinates": [ - [779.375, 626.625], [779.625, 631.75], [781.875, 636.875], [784.125, 640.625], [785.75, 644.125], [785.875, 647.5], [786.625, 653.625], [788.375, 659.375], [788.875, 665], [787.375, 668.5], [784.125, 670.875], [781.75, 673.25], [781.25, 676.25], [782.375, 681.25], [783.375, 688.375], [782.75, 694.25], [781.25, 699.625], [780.375, 704.875], [781.75, 712.5], [783.75, 719.5], [787.125, 727.875], [788.75, 734], [790, 740.5], [789.75, 745.875], [789.125, 752.75], [788.375, 760.75], [787.5, 768.125], [786.375, 776.25], [785.625, 781.375], [786.125, 786.125], [787.125, 789.125], [789.25, 789.75], [790.625, 787.875], [794, 793.6875], [795, 797], [794.9375, 800.1875], [795.9375, 802.875], [799.75, 805.5], [803.5625, 810.25], [805.125, 814.0625], [807.5, 814.9375], [809.8125, 817.5], [810.5625, 821.0625], [815.125, 820.4375], [814.3125, 824.3125], [813.25, 825.875], [811.5625, 827.8125], [809.75, 829.5], [808.4375, 831.625], [807.625, 833.8125], [806.6875, 836], [806.875, 838.375], [809.625, 840], [812.75, 840.625], [816.625, 840.25], [819.625, 839.5], [822.875, 839.125], [826.5, 839.5], [828.75, 842.875], [828.5, 853.5], [827.25, 863.375], [826.75, 872.25], [827, 880.125], [826.125, 887.625], [825.25, 894], [828.75, 898], [836.625, 899], [834.5, 900.375], [833.625, 903.75], [833.875, 906.9375], [833.6875, 909.5625], [833.0625, 911.125], [834.3125, 913.3125], [834.625, 915.75], [833.625, 918.75], [833.625, 920.875], [837.1875, 921.875], [838.9375, 922.375], [840.125, 923.3125], [841.9375, 922.6875], [842.4375, 920.5], [843.5, 919.625], [845, 921], [845.875, 918.75] - ] - }, { "character": "Nori", "season": 1, diff --git a/data/paths-season2-rop.js b/data/paths-season2-rop.js new file mode 100644 index 0000000..7eee156 --- /dev/null +++ b/data/paths-season2-rop.js @@ -0,0 +1,119 @@ +// Season 2 paths for Rings of Power +DATA_PATHS.paths.push( + // Nori + { + "character": "Nori", + "season": 2, + "episode": 1, + "isConfirmed": true, + "coordinates": [[650, 1050], [655, 1042], [660, 1035], [665, 1028], [670, 1020], [675, 1015], [680, 1010]] + }, + { + "character": "Nori", + "season": 2, + "episode": 2, + "isConfirmed": true, + "coordinates": [[680, 1010], [685, 1005], [690, 1000], [695, 995], [700, 990]] + }, + { + "character": "Nori", + "season": 2, + "episode": 3, + "isConfirmed": true, + "coordinates": [[700, 990], [705, 985], [710, 980], [715, 975], [720, 970]] + }, + + // Galadriel + { + "character": "Galadriel", + "season": 2, + "episode": 1, + "isConfirmed": true, + "coordinates": [[780.3125, 571.6250], [780.3125, 671.6250]] + }, + { + "character": "Galadriel", + "season": 2, + "episode": 2, + "isConfirmed": true, + "coordinates": [] + }, + { + "character": "Galadriel", + "season": 2, + "episode": 3, + "isConfirmed": false, + "coordinates": [] + }, + + // Elrond + { + "character": "Elrond", + "season": 2, + "episode": 1, + "isConfirmed": true, + "coordinates": [[730, 780], [735, 770], [740, 760], [745, 750], [750, 740]] + }, + { + "character": "Elrond", + "season": 2, + "episode": 2, + "isConfirmed": false, + "coordinates": [[750, 740], [755, 730], [760, 720], [765, 710], [770, 700]] + }, + + // Arondir + { + "character": "Arondir", + "season": 2, + "episode": 1, + "isConfirmed": true, + "coordinates": [[540, 950], [545, 945], [550, 940], [555, 935]] + }, + { + "character": "Arondir", + "season": 2, + "episode": 2, + "isConfirmed": true, + "coordinates": [[555, 935], [560, 930], [565, 925], [570, 920]] + }, + { + "character": "Arondir", + "season": 2, + "episode": 3, + "isConfirmed": false, + "coordinates": [[570, 920], [575, 915], [580, 910], [585, 905]] + }, + + // Halbrand + { + "character": "Halbrand", + "season": 2, + "episode": 1, + "isConfirmed": true, + "coordinates": [[400, 600], [410, 590], [420, 580], [430, 570], [440, 560]] + }, + { + "character": "Halbrand", + "season": 2, + "episode": 2, + "isConfirmed": false, + "coordinates": [[440, 560], [450, 550], [460, 540], [470, 530]] + }, + + // Elendil + { + "character": "Elendil", + "season": 2, + "episode": 1, + "isConfirmed": true, + "coordinates": [[50, 100], [60, 110], [70, 120], [80, 130], [90, 140]] + }, + { + "character": "Elendil", + "season": 2, + "episode": 2, + "isConfirmed": true, + "coordinates": [[90, 140], [100, 150], [110, 160], [120, 170]] + } +); \ No newline at end of file diff --git a/data/seasonsData.js b/data/seasonsData.js new file mode 100644 index 0000000..def2699 --- /dev/null +++ b/data/seasonsData.js @@ -0,0 +1,43 @@ +// rop-map/data/seasonsData.js +const DATA_SEASONS = [ + { + id: 1, + name: "Rings of Power - Season 1", + episodes: 8, + showInSelect: true, + characters: ["Arondir","Elendil","Elrond","Galadriel","Halbrand","Nori"], + markersRelevant: marker => marker.episodes.some(e => e.season === 1) + }, + { + id: 2, + name: "Rings of Power - Season 2", + episodes: 8, + showInSelect: true, + characters: ["Galadriel","Arondir"], + markersRelevant: marker => marker.episodes.some(e => e.season === 2) + }, + { + id: 3, + name: "Rings of Power - Season 3", + episodes: 8, + showInSelect: false, // hidden for now + characters: ["Elendil","Elrond","Halbrand","Nori"], + markersRelevant: marker => marker.episodes.some(e => e.season === 3) + }, + { + id: 100, + name: "The Lord of the Rings (Movies)", + episodes: 1, + showInSelect: true, + characters: ["Frodo and Sam"], + markersRelevant: marker => marker.episodes.some(e => e.season === 100 || e.season === 103) + }, + { + id: 104, + name: "The Hobbit (Movies)", + episodes: 1, + showInSelect: true, + characters: ["Bilbo and Thorin"], + markersRelevant: marker => marker.episodes.some(e => e.season === 104 || e.season === 106) + } +]; \ No newline at end of file diff --git a/index.html b/index.html index 153d4cb..8663e03 100644 --- a/index.html +++ b/index.html @@ -1,3 +1,4 @@ + @@ -5,19 +6,19 @@ - + - + + Lord of The Rings: Rings of Power - Interactive map + @@ -28,15 +29,37 @@ - - - + + + + + + + + + + + + + + - + + + + + + + + + + + + + @@ -49,7 +72,15 @@

ROP Map

diff --git a/js/core/CharacterCheckBoxController.js b/js/core/CharacterCheckBoxController.js new file mode 100644 index 0000000..07ef0df --- /dev/null +++ b/js/core/CharacterCheckBoxController.js @@ -0,0 +1,74 @@ +// rop-map/js/core/CharacterCheckBoxController.js +window.CharacterCheckBoxController = (function () { + const grid = document.getElementById('pathsgrid'); + // ------------------------------ + // rebuild() + // ------------------------------ + function rebuild() { + const season = SeasonController.getCurrentSeason(); + const series = season.series; // "rop" or "lotr" + + grid.innerHTML = ''; + + + DATA_CHARACTERS.characters + .filter(char => + char.series === series && + char.seasons.includes(season.id) + ) + .forEach(char => { + const id = `checkbox-${char.name.replace(/\s+/g, '')}`; + const disabled = !PathsController.hasPaths(char.name, season.id); + + grid.insertAdjacentHTML('beforeend', ` +
+ + + +
+ `); + }); + } + + // ------------------------------ + // setPath(element) + // Toggles a character's path on the map when the corresponding button or element is clicked + // ------------------------------ + function setPath(element) { + console.log("A character checkbox icon was clicked"); + PathsController.togglePath(element.name); + MarkersController.clearMarkers(); + MarkersController.addMarkers(); + }; + + // ------------------------------ + // resetCharacterSelection() + // ------------------------------ + function resetCharacterSelection() { + // Uncheck all UI checkboxes + document + .querySelectorAll('.pathsgrid input[type="checkbox"]') + .forEach(cb => { + cb.checked = false; + }); + + // Clear paths from the map + PathsController.clearPaths(); + } + + return { + rebuild, + setPath, + resetCharacterSelection + }; +})(); \ No newline at end of file diff --git a/js/core/appState.js b/js/core/appState.js new file mode 100644 index 0000000..b821a06 --- /dev/null +++ b/js/core/appState.js @@ -0,0 +1,9 @@ +// rop-map/js/core/appState.js +// Shared global state +window.AppState = { + LIST_PATHS: {}, + LIST_MARKERS: [], + CURRENT_RANGE: [0, 8], + PATH_SPEED_ANIMATION: 400, + PATH_WEIGHT: 4 +}; \ No newline at end of file diff --git a/js/core/mapController.js b/js/core/mapController.js new file mode 100644 index 0000000..a122bc3 --- /dev/null +++ b/js/core/mapController.js @@ -0,0 +1,57 @@ +// rop-map/js/core/mapController.js +window.MapController = (function () { + + const map = L.map('map', { + crs: L.CRS.Simple, + attributionControl: false, + zoom: 0, + minZoom: -1, + maxZoom: 4, + zoomControl: false + }); + + L.control.zoom({ position: 'topright' }).addTo(map); + + const c = new L.Control.Coordinates(); + c.addTo(map); + map.on('click', e => c.setCoordinates(e)); + + const bounds = [[0, 0], [1000, 1366]]; + L.imageOverlay('./img/map.webp', bounds).addTo(map); + map.setView([500, 683]); + + // Draw tool + const drawControl = new L.Control.Draw({ + position: 'topright', + draw: { + polyline: { shapeOptions: { color: '#5e81ac', weight: 4 } }, + polygon: false, + circle: false, + rectangle: false, + circlemarker: false, + marker: false + } + }); + + map.addControl(drawControl); + L.Draw.Polyline.prototype._onTouch = L.Util.falseFn; + + // Dev helper + map.on(L.Draw.Event.CREATED, function (e) { + let output = ""; + e.layer.getLatLngs().forEach(p => { + output += `[${p.lat}, ${p.lng}], `; + }); + console.log(output); + }); + + return { map }; + +})(); + + + +// Now everywhere you used `map`, call +/* +MapController.map +*/ \ No newline at end of file diff --git a/js/core/markersController.js b/js/core/markersController.js new file mode 100644 index 0000000..a4ff852 --- /dev/null +++ b/js/core/markersController.js @@ -0,0 +1,101 @@ +// rop-map/js/core/markersController.js +// ============================== +// MarkersController +// ============================== +// Manages the creation, display, and removal of markers on the map. +// Markers are filtered by the currently selected season and displayed +// using Leaflet with custom icons and popups. + +window.MarkersController = (function () { + + // ------------------------------ + // clearMarkers() + // ------------------------------ + // Removes all currently displayed markers from the map and + // clears the list of markers in AppState. + function clearMarkers() { + // Remove each marker from the map + AppState.LIST_MARKERS.forEach(marker => marker.removeFrom(MapController.map)); + // Clear the array holding references to markers + AppState.LIST_MARKERS.length = 0; + } + + // ------------------------------ + // addMarkers() + // ------------------------------ + // Adds markers to the map based on the currently selected season. + // Only markers relevant to the selected season are displayed. + function addMarkers() { + // Get the currently selected season + const currentSeason = SeasonController.getCurrentSeason(); + const [from, to] = AppState.CURRENT_RANGE; + + // Loop through all markers defined in DATA_MARKERS + DATA_MARKERS.markers.forEach(marker => { + + // Skip markers that are not relevant to the current season + const matchesSeason = currentSeason.markersRelevant(marker); + const matchesEpisodeRange = marker.episodes.some(e => + e.season === currentSeason.id && + e.episode >= from && + e.episode <= to + ); + if (!matchesSeason || !matchesEpisodeRange) return; + + // Find marker type information (icon, size, anchor points) + const type = DATA_MARKER_TYPES.types.find(t => t.name === marker.type); + + // Create a Leaflet marker at the marker's coordinates + const leafletMarker = L.marker(marker.coordinates, { + icon: L.icon({ + iconUrl: `img/markers/${type.icon}`, // path to the icon image + iconSize: type.iconSize, // size of the icon + iconAnchor: type.iconAnchor, // anchor point for the icon + popupAnchor: type.popupAnchor // anchor point for the popup + }), + title: marker.title // tooltip title when hovering over the marker + }).bindPopup( + // HTML content for the popup + `
+
+ + + +
+
+
+
+

${marker.title}

+
${marker.type}
+ ${marker.isConfirmed ? '' : "
coordinates not confirmed
"} +
+
+ ${marker.decription} +
+ Seen in: + ${marker.episodes.map(e => { + // Format the episodes in which this marker appears + if (e.season === 100) return "The Lord of the Rings (Movies)"; + if (e.season === 104) return "The Hobbit (Movies)"; + return `S0${e.season}E0${e.episode}`; // Standard season/episode format + }).join(", ")} +
+ ${marker.readMoreUrl ? `` : ''} +
+
` + ) + // Add the marker to the map + .addTo(MapController.map); + + // Store the marker reference in AppState for later clearing or updates + AppState.LIST_MARKERS.push(leafletMarker); + }); + } + + // Expose public functions + return { + clearMarkers, // Function to remove all markers + addMarkers // Function to add markers for the current season + }; + +})(); diff --git a/js/core/pathsController.js b/js/core/pathsController.js new file mode 100644 index 0000000..085862c --- /dev/null +++ b/js/core/pathsController.js @@ -0,0 +1,89 @@ +// rop-map/js/core/pathsController.js + +window.PathsController = (function () { + // ------------------------------ + // togglePath(characterName) + // ------------------------------ + function togglePath(characterName) { + const paths = AppState.LIST_PATHS; + const currentSeason = SeasonController.getCurrentSeason(); + + if (!paths[characterName]) { + const layer = L.layerGroup( + getPolylinesFromName(characterName, currentSeason.id), + { snakingPause: AppState.PATH_SPEED_ANIMATION } + ).addTo(MapController.map); + + layer.snakeIn(); // animate first add + paths[characterName] = layer; + } else { + paths[characterName].removeFrom(MapController.map); + delete paths[characterName]; + } + } + + // ------------------------------ + // clearPaths() + // ------------------------------ + function clearPaths() { + Object.keys(AppState.LIST_PATHS).forEach(characterName => { + AppState.LIST_PATHS[characterName].removeFrom(MapController.map); + }); + } + + // ------------------------------ + // addPaths() + // ------------------------------ + function addPaths() { + const currentSeason = SeasonController.getCurrentSeason(); + + Object.keys(AppState.LIST_PATHS).forEach(characterName => { + AppState.LIST_PATHS[characterName] = L.layerGroup( + getPolylinesFromName(characterName, currentSeason.id) + ).addTo(MapController.map); + }); + } + + // ------------------------------ + // hasPaths(characterName, seasonId) + // ------------------------------ + function hasPaths(characterName, seasonId) { + return DATA_PATHS.paths.some(p => + p.character === characterName && + (p.season === seasonId || p.season >= 100) + ); + } + + // ------------------------------ + // getPolylinesFromName(characterName, seasonId) + // ------------------------------ + function getPolylinesFromName(characterName, seasonId) { + const [from, to] = AppState.CURRENT_RANGE; + + const filteredPaths = DATA_PATHS.paths.filter(p => + p.character === characterName && + (p.season === seasonId || p.season >= 100) && + p.episode >= from && + p.episode <= to + ); + + const color = DATA_CHARACTERS.characters.find(c => c.name === characterName).color; + + return filteredPaths.map(p => + L.polyline(p.coordinates, { + color, + weight: AppState.PATH_WEIGHT, + dashArray: p.isConfirmed ? '0' : '2 6', + opacity: p.isConfirmed ? 1 : 0.7 + }) + ); + } + + return { + togglePath, + clearPaths, + addPaths, + hasPaths + }; + +})(); diff --git a/js/core/seasonController.js b/js/core/seasonController.js new file mode 100644 index 0000000..64949c8 --- /dev/null +++ b/js/core/seasonController.js @@ -0,0 +1,47 @@ +// rop-map/js/core/seasonController.js +window.SeasonController = (function () { + const seasonSelect = document.getElementById('seasonSelect'); + + // Default season = first visible season + let currentSeason = DATA_SEASONS.find(s => s.showInSelect); + + function initDropdown() { + seasonSelect.innerHTML = ""; + + DATA_SEASONS.forEach((season, index) => { + if (!season.showInSelect) return; + + const option = document.createElement('option'); + option.value = index; // IMPORTANT: original index + option.textContent = season.name; + seasonSelect.appendChild(option); + }); + + // Select first visible season + seasonSelect.selectedIndex = 0; + currentSeason = DATA_SEASONS[seasonSelect.value]; + + seasonSelect.addEventListener('change', () => { + const selectedIndex = Number(seasonSelect.value); + currentSeason = DATA_SEASONS[selectedIndex]; + + PathsController.clearPaths(); + MarkersController.clearMarkers(); + + AppState.LIST_PATHS = {}; + + SliderController.updateSlider(currentSeason.episodes); + + PathsController.addPaths(); + MarkersController.addMarkers(); + + CharacterCheckBoxController.resetCharacterSelection(); + }); + } + + function getCurrentSeason() { + return currentSeason; + } + + return { initDropdown, getCurrentSeason }; +})(); diff --git a/js/core/sliderController.js b/js/core/sliderController.js new file mode 100644 index 0000000..95ffd33 --- /dev/null +++ b/js/core/sliderController.js @@ -0,0 +1,69 @@ +// rop-map/js/core/sliderController.js +window.SliderController = (function () { + let slider; + + // ------------------------------ + // createSlider(count) + // ------------------------------ + function createSlider(count) { + if (slider.noUiSlider) { + slider.noUiSlider.destroy(); + } + + noUiSlider.create(slider, { + start: [0, count], + connect: true, + step: 1, + range: { min: 0, max: count }, + pips: { + mode: 'steps', + density: 100, + filter: () => 2, + format: { + to: value => (value === 0 ? 'Prologue' : `Episode ${value}`), + from: value => Number(value) + } + } + }); + + slider.noUiSlider.on('update', values => { + sliderChange([Number(values[0]), Number(values[1])]); + }); + } + + // ------------------------------ + // init() + // ------------------------------ + function init() { + slider = document.getElementById('slider'); + + const season = SeasonController.getCurrentSeason(); + createSlider(season.episodes); + } + + // ------------------------------ + // updateSlider(newCount) + // ------------------------------ + function updateSlider(newCount) { + createSlider(newCount); + } + + // ------------------------------ + // sliderChange(range) + // ------------------------------ + function sliderChange(range) { + AppState.CURRENT_RANGE = range; + + PathsController.clearPaths(); + PathsController.addPaths(); + + MarkersController.clearMarkers(); + MarkersController.addMarkers(); + } + + return { + init, + updateSlider, + sliderChange + }; +})(); diff --git a/js/core/uiController.js b/js/core/uiController.js new file mode 100644 index 0000000..46271d0 --- /dev/null +++ b/js/core/uiController.js @@ -0,0 +1,44 @@ +// rop-map/js/core/uiController.js +window.UIController = (function () { + + /* + * Function: Hide or Show the main menu + */ + function hideshow() { + const button = document.getElementById("main__button-input"); + const menu = document.getElementById("main"); + const timeline = document.getElementById("rangeselect"); + + const isHidden = button.classList.contains('main__button-input--hidden'); + + if (!isHidden) { + button.classList.add("main__button-input--hidden"); + menu.classList.add("menu--hidden"); + timeline.classList.add("slider--hidden"); + } else { + button.classList.remove("main__button-input--hidden"); + menu.classList.remove("menu--hidden"); + timeline.classList.remove("slider--hidden"); + } + } + + /* + * A11Y Function: Trigger checkbox when using keyboard on focused label + * @param event KeyboardEvent + */ + function interactionLabel(event) { + const checkbox = event.target.control; + + // Enter (13) or Space (32) + if (event.keyCode === 13 || event.keyCode === 32) { + checkbox.checked = !checkbox.checked; + CharacterCheckBoxController.setPath(checkbox); + } + } + + return { + hideshow, + interactionLabel + }; + +})(); diff --git a/js/main.js b/js/main.js index 2221d56..7a3d904 100644 --- a/js/main.js +++ b/js/main.js @@ -1,291 +1,33 @@ -// Global variables -const LIST_PATHS = {}; -const LIST_MARKERS = []; -const CURRENT_CHAR = []; -const CURRENT_RANGE = [null, null]; -const PATH_SPEED_ANIMATION = 400; -const PATH_WEIGHT = 4; - -// Leaflet map setup -let map = L.map('map', { - crs: L.CRS.Simple, - attributionControl: false, - zoom: 0, - minZoom: -1, - maxZoom: 4, - zoomControl: false -}); - -// Set zoom panel to the top right -L.control.zoom({ - position: 'topright' -}).addTo(map); - -// Show coordinates -let c = new L.Control.Coordinates(); -c.addTo(map); -map.on('click', function(e) { - c.setCoordinates(e); -}); - -// Draw options for measure distance feature -var drawPluginOptions = { - position: 'topright', - draw: { - polyline: { - shapeOptions: { - color: '#5e81ac', - weight: 4 - } - }, - polygon: false, - circle: false, - rectangle: false, - circlemarker: false, - marker: false - } -}; - -var drawControl = new L.Control.Draw(drawPluginOptions); -map.addControl(drawControl); -L.Draw.Polyline.prototype._onTouch = L.Util.falseFn; // Fix for touchscreen - -// Set the custom map -let bounds = [[0,0], [1000,1366]]; -let image = L.imageOverlay('./img/map.webp', bounds).addTo(map); -map.setView([500,683]); - -// Slider configuration -const slider = document.getElementById('slider'); - -const listEpisodes = ['Prologue', 'Episode 1', 'Episode 2', 'Episode 3', 'Episode 4', 'Episode 5', 'Episode 6', 'Episode 7', 'Episode 8']; -noUiSlider.create(slider, { - start: [0, 8], - connect: true, - step: 1, - range: { - 'min': 0, - 'max': 8 - }, - pips: { - mode: 'steps', - density: 100, - filter: (x) => 2, - format: { - to: function (value) { - return listEpisodes[value]; - }, - from: function (value) { - return Number(value); - } - } - } -}); - -slider.noUiSlider.on('update', function (values) { - timelineChange([Number(values[0]), Number(values[1])]); -}); - - -/* - * Alright, this thing is a real mess, need to be refactored asap -*/ -function setMarker() { - DATA_MARKERS.markers.forEach((marker) => { - - // Will break on season 2, todo - let isMarkerRelevant = marker.episodes.find((elem) => elem.episode >= CURRENT_RANGE[0] && elem.episode <= CURRENT_RANGE[1] && elem.season === 1 ) !== undefined ? true : false; - - if (marker.episodes.find((elem) => elem.season === 100) !== undefined && LIST_PATHS["Frodo and Sam"] !== undefined) - isMarkerRelevant = true; - if (marker.episodes.find((elem) => elem.season === 101) !== undefined && LIST_PATHS["Bilbo and Thorin"] !== undefined) - isMarkerRelevant = true; - - if (isMarkerRelevant) - { - let confirmed = (marker.isConfirmed) ? "" : `
coordinates not confirmed
`; - let readMore = (marker.readMoreUrl) ? `
Read more about ${marker.title}
` : ""; - let type = DATA_MARKERS.types.find((type) => type.name === marker.type); - - let listEpisodes = marker.episodes.map((elem) => { - switch (elem.season) { - case 100: - return "Lord Of The Rings (Movie)"; - case 101: - return "The Hobbit (Movie)"; - default: - return `S0${elem.season}E0${elem.episode}`; - } - }).join(", "); - - LIST_MARKERS.push(L.marker(marker.coordinates, - {icon: L.icon( - { - iconUrl: `img/markers/${type.icon}`, - iconSize: type.iconSize, - iconAnchor: type.iconAnchor, - popupAnchor: type.popupAnchor - } - ), - maxWidth: '500', - title: marker.title - }) - .bindPopup(`

${marker.title}

${marker.type}
${confirmed}
${marker.decription}
Seen in: ${listEpisodes}
${readMore}
`) - .addTo(map)); - } - }); -} - -/* - * Function that triggers setPath function through an input - * - * @param element the input checked/unchecked by the user -*/ -function togglePathCheckbox(element) { - setPath(element.name); -} - -/* - * Function that draw or remove a path from the map - * - * @param element Html checkbox -*/ -function setPath(element) { - const characterName = element.name; - - if (LIST_PATHS[characterName] === undefined) { - LIST_PATHS[characterName] = L.layerGroup(getPolylinesFromName(characterName), { snakingPause: PATH_SPEED_ANIMATION }).addTo(map).snakeIn(); // Set animation - } - else { - LIST_PATHS[characterName].removeFrom(map); - delete LIST_PATHS[characterName]; - } - - LIST_MARKERS.forEach(maker => { - maker.removeFrom(map); - }); - LIST_MAKERS = []; - setMarker(); -} - -/* - * Function that refresh all polylines drawn on the map - * -*/ -function refreshTimelinePaths() { - Object.keys(LIST_PATHS).forEach(characterName => { - if (LIST_PATHS[characterName] != undefined) - LIST_PATHS[characterName].removeFrom(map); - - LIST_PATHS[characterName] = L.layerGroup(getPolylinesFromName(characterName)).addTo(map); - }); - - LIST_MARKERS.forEach(maker => { - maker.removeFrom(map); - }); - LIST_MAKERS = []; - setMarker(); -} - -/* - * Function that returns every polyline of a character - * - * @param characterName string Name of the character - * @return array of L.Polyline -*/ -function getPolylinesFromName(characterName) { - let characterPaths, layerArray = []; - - // TODO : This thing will break on season 2 - characterPaths = DATA_PATHS.paths.filter(path => - (path.character === characterName && - path.episode >= CURRENT_RANGE[0] && - path.episode <= CURRENT_RANGE[1]) || ( - path.character === characterName && - path.season >= 100 // >= are movies - )); - characterColor = DATA_PATHS.characters.find(color => color.name === characterName).color; - - characterPaths.forEach(characterPath => { - let polyLine = L.polyline(characterPath.coordinates, - { - color: characterColor, - weight: PATH_WEIGHT, - dashArray: characterPath.isConfirmed ? '0' : '2 6', - opacity: characterPath.isConfirmed ? '1' : '.7' - }); - layerArray.push(polyLine); - }); - - return layerArray; -} - -/* - * A11Y Function: Trigger checkbox when using keyboard on focused label - * - * @param event of the label -*/ -function interactionLabel(event) { - const checkbox = event.target.control; - - if (event.keyCode === 13 || event.keyCode === 32) { - checkbox.checked === true ? checkbox.checked = false : checkbox.checked = true; - setPath(event.target.control); - } -} - -/* - * Function: Hide or Show the main menu - * -*/ -function hideshow() { - const button = document.getElementById("main__button-input"); - const menu = document.getElementById("main"); - const timeline = document.getElementById("rangeselect"); - - const isHidden = button.classList.contains('main__button-input--hidden'); - - if (!isHidden) { - button.classList.add("main__button-input--hidden"); - menu.classList.add("menu--hidden"); - timeline.classList.add("slider--hidden"); - } - else { - button.classList.remove("main__button-input--hidden"); - menu.classList.remove("menu--hidden"); - timeline.classList.remove("slider--hidden"); - } -} - -/* - * Function: Setter for episode range - * - * @param range Array of 2 ranges -*/ -function setCurrentRange(range) { - if (range != null && range.length === 2) { - CURRENT_RANGE[0] = range[0]; - CURRENT_RANGE[1] = range[1]; - } -} - -/* - * Function: Refresh timeline - * - * @param range Array of 2 ranges -*/ -function timelineChange(range) { - setCurrentRange(range); - refreshTimelinePaths(); // Refresh all the path on the map -} - -// Dev, show paths on console when drawing on the map -map.on(L.Draw.Event.CREATED, function (e) { - var layer = e.layer, output = ""; - - layer.getLatLngs().forEach(element => { - output += `[${element.lat}, ${element.lng}], ` - }); - - console.log(output); - }); \ No newline at end of file +// rop-map/js/main.js +// ============================== +// Global Handlers for Inline HTML +// ============================== + +// These handlers are attached to the global `window` object so that +// any inline HTML event attributes (e.g., onclick="setPath(this)") +// can still call these functions directly from the DOM. + +// Toggles a character's path on the map when the corresponding button or element is clicked +window.setPath = CharacterCheckBoxController.setPath +// Show or hide UI panels or elements +window.hideshow = UIController.hideshow; +// Display contextual labels or tooltips for interactions +window.interactionLabel = UIController.interactionLabel; +// subscribee to episode slider changing +window.timelineChange = SliderController.sliderChange; + + +document.addEventListener('DOMContentLoaded', () => { + // setup the season dropdown box + SeasonController.initDropdown(); + + // default episode range + AppState.CURRENT_RANGE ??= [0, 8]; + + // initialising the episode slider + SliderController.init(); + + // put the default markers on the map + MarkersController.clearMarkers(); + MarkersController.addMarkers(); +}); \ No newline at end of file