diff --git a/administrator/cache/autoload_psr4.php b/administrator/cache/autoload_psr4.php index b6a87785..f28dda41 100644 --- a/administrator/cache/autoload_psr4.php +++ b/administrator/cache/autoload_psr4.php @@ -274,6 +274,9 @@ return [ 'Joomla\\Plugin\\Workflow\\Featuring\\' => [JPATH_PLUGINS . '/workflow/featuring/src'], 'Joomla\\Plugin\\Workflow\\Notification\\' => [JPATH_PLUGINS . '/workflow/notification/src'], 'Joomla\\Plugin\\Workflow\\Publishing\\' => [JPATH_PLUGINS . '/workflow/publishing/src'], + 'Pcrt\\Component\\Highlights\\Administrator\\' => [JPATH_ADMINISTRATOR . '/components/com_highlights/src'], + 'Pcrt\\Component\\Highlights\\Site\\' => [JPATH_SITE . '/components/com_highlights/src'], + 'Pcrt\\Module\\Highlights\\Site\\' => [JPATH_SITE . '/modules/mod_highlights/src'], 'RegularLabs\\Component\\Conditions\\Administrator\\' => [JPATH_ADMINISTRATOR . '/components/com_conditions/src'], 'RegularLabs\\Library\\' => [JPATH_LIBRARIES . '/regularlabs/src'], 'RegularLabs\\Plugin\\EditorButton\\ConditionalContent\\' => [JPATH_PLUGINS . '/editors-xtd/conditionalcontent/src'], diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-04ed4a7f0d523202a41ff5850cb73d25.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-04ed4a7f0d523202a41ff5850cb73d25.php new file mode 100644 index 00000000..a8d0ffc8 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-04ed4a7f0d523202a41ff5850cb73d25.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3177";s:4:"name";s:27:"Strumenti ad arco e a corda";s:8:"headline";s:27:"Strumenti ad arco e a corda";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-08d2d978effe5e3356271682738ea805.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-08d2d978effe5e3356271682738ea805.php new file mode 100644 index 00000000..bee310f6 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-08d2d978effe5e3356271682738ea805.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:6:{s:5:"@type";s:6:"Person";s:3:"@id";s:66:"https://conservatorio2025.ddev.site/#/schema/com_contact/contact/3";s:4:"name";s:46:"Didattica Biennio, Erasmus e Internazionalità";s:8:"jobTitle";s:52:"Sig.ra Mariangela ASQUINI, assistente amministrativo";s:7:"address";a:1:{s:13:"streetAddress";s:48:"Utilizzare il form per inviare una mail. Grazie.";}s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-0a686d8cce2036297c6879589698bd01.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-0a686d8cce2036297c6879589698bd01.php new file mode 100644 index 00000000..afa17140 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-0a686d8cce2036297c6879589698bd01.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3058";s:4:"name";s:9:"Chi siamo";s:8:"headline";s:9:"Chi siamo";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-0df585d5b8a54b344605cb8fdce601b8.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-0df585d5b8a54b344605cb8fdce601b8.php new file mode 100644 index 00000000..2f7b15d9 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-0df585d5b8a54b344605cb8fdce601b8.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/4097";s:4:"name";s:13:"Arpa - DCPL01";s:8:"headline";s:13:"Arpa - DCPL01";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-17ce0be9c1646f4bb58c0694a6853071.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-17ce0be9c1646f4bb58c0694a6853071.php new file mode 100644 index 00000000..9207b8f3 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-17ce0be9c1646f4bb58c0694a6853071.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3182";s:4:"name";s:10:"Dove siamo";s:8:"headline";s:10:"Dove siamo";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-1b4a4af71975524e19ea091cca47cd4c.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-1b4a4af71975524e19ea091cca47cd4c.php new file mode 100644 index 00000000..6787a771 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-1b4a4af71975524e19ea091cca47cd4c.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:67:"https://conservatorio2025.ddev.site/#/schema/com_content/article/25";s:4:"name";s:20:"Storia dell'Istituto";s:8:"headline";s:20:"Storia dell'Istituto";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-33a003dc9d6392fc3c92340c2c78a250.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-33a003dc9d6392fc3c92340c2c78a250.php new file mode 100644 index 00000000..df7b6bf8 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-33a003dc9d6392fc3c92340c2c78a250.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3088";s:4:"name";s:23:"Data Protection Officer";s:8:"headline";s:23:"Data Protection Officer";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-36f4d7dc2d3b6b33e50cd0d440708a6b.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-36f4d7dc2d3b6b33e50cd0d440708a6b.php new file mode 100644 index 00000000..b6c825ac --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-36f4d7dc2d3b6b33e50cd0d440708a6b.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/6693";s:4:"name";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:8:"headline";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:10:"inLanguage";s:5:"it-IT";s:12:"dateModified";s:25:"2024-12-30T14:01:55+00:00";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-3c30e901b6c52ac582d73e3032832392.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-3c30e901b6c52ac582d73e3032832392.php new file mode 100644 index 00000000..a8d0ffc8 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-3c30e901b6c52ac582d73e3032832392.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3177";s:4:"name";s:27:"Strumenti ad arco e a corda";s:8:"headline";s:27:"Strumenti ad arco e a corda";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-3e3ad261c7f4b956497c1cab5d38d22c.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-3e3ad261c7f4b956497c1cab5d38d22c.php new file mode 100644 index 00000000..6a3c84cb --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-3e3ad261c7f4b956497c1cab5d38d22c.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3189";s:4:"name";s:19:"Ballarin Alessandro";s:8:"headline";s:19:"Ballarin Alessandro";s:10:"inLanguage";s:5:"it-IT";s:6:"author";a:2:{s:5:"@type";s:6:"Person";s:4:"name";s:19:"Ballarin Alessandro";}s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-46e5057c73170a83da397a968d195d7b.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-46e5057c73170a83da397a968d195d7b.php new file mode 100644 index 00000000..268ea44b --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-46e5057c73170a83da397a968d195d7b.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/1391";s:4:"name";s:16:"Il Conservatorio";s:8:"headline";s:16:"Il Conservatorio";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-59070946fdebbeac7d839397158e8db6.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-59070946fdebbeac7d839397158e8db6.php new file mode 100644 index 00000000..8544cc48 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-59070946fdebbeac7d839397158e8db6.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3224";s:4:"name";s:12:"Chini Andrea";s:8:"headline";s:12:"Chini Andrea";s:10:"inLanguage";s:5:"it-IT";s:6:"author";a:2:{s:5:"@type";s:6:"Person";s:4:"name";s:12:"Chini Andrea";}s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-597e70e20717ecae8e2139616ba0a8d6.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-597e70e20717ecae8e2139616ba0a8d6.php new file mode 100644 index 00000000..76948b1b --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-597e70e20717ecae8e2139616ba0a8d6.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3176";s:4:"name";s:28:"Nuovi linguaggi e tecnologie";s:8:"headline";s:28:"Nuovi linguaggi e tecnologie";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-5a9d90142dac6b24ec7bda7fb6d5e032.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-5a9d90142dac6b24ec7bda7fb6d5e032.php new file mode 100644 index 00000000..7b099425 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-5a9d90142dac6b24ec7bda7fb6d5e032.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3125";s:4:"name";s:26:"Accompagnamento pianistico";s:8:"headline";s:26:"Accompagnamento pianistico";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-60a6fe4bbdb56dceb287b6583513cd43.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-60a6fe4bbdb56dceb287b6583513cd43.php new file mode 100644 index 00000000..dfe587b0 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-60a6fe4bbdb56dceb287b6583513cd43.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3789";s:4:"name";s:41:"Clavicembalo e tastiere storiche - DCPL14";s:8:"headline";s:41:"Clavicembalo e tastiere storiche - DCPL14";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-6edb6dd689a7263e999da815fa37575f.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-6edb6dd689a7263e999da815fa37575f.php new file mode 100644 index 00000000..c9e41097 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-6edb6dd689a7263e999da815fa37575f.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:68:"https://conservatorio2025.ddev.site/#/schema/com_content/article/298";s:4:"name";s:3:"RSU";s:8:"headline";s:3:"RSU";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-71ab9d59ec54860454e30a4d04e28c69.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-71ab9d59ec54860454e30a4d04e28c69.php new file mode 100644 index 00000000..8b2625a4 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-71ab9d59ec54860454e30a4d04e28c69.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:68:"https://conservatorio2025.ddev.site/#/schema/com_content/article/290";s:4:"name";s:24:"Organi del Conservatorio";s:8:"headline";s:24:"Organi del Conservatorio";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-721ef0005992d2fd54415f7dc4aa0502.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-721ef0005992d2fd54415f7dc4aa0502.php new file mode 100644 index 00000000..95787526 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-721ef0005992d2fd54415f7dc4aa0502.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:67:"https://conservatorio2025.ddev.site/#/schema/com_content/article/18";s:4:"name";s:12:"Informazioni";s:8:"headline";s:12:"Informazioni";s:10:"inLanguage";s:5:"it-IT";s:12:"dateModified";s:25:"2024-11-15T10:37:18+00:00";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-7760ad5e0068170bb36883b1832ae7e4.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-7760ad5e0068170bb36883b1832ae7e4.php new file mode 100644 index 00000000..e225fb31 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-7760ad5e0068170bb36883b1832ae7e4.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3142";s:4:"name";s:4:"Arpa";s:8:"headline";s:4:"Arpa";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-80826294e3ded8a5a613e131d7c1a04c.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-80826294e3ded8a5a613e131d7c1a04c.php new file mode 100644 index 00000000..71feb0b6 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-80826294e3ded8a5a613e131d7c1a04c.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:68:"https://conservatorio2025.ddev.site/#/schema/com_content/article/109";s:4:"name";s:12:"Organigramma";s:8:"headline";s:12:"Organigramma";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-815ae486acca7a9f139cb6c20f0ccec6.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-815ae486acca7a9f139cb6c20f0ccec6.php new file mode 100644 index 00000000..26463a94 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-815ae486acca7a9f139cb6c20f0ccec6.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3301";s:4:"name";s:16:"Barbieri Roberto";s:8:"headline";s:16:"Barbieri Roberto";s:10:"inLanguage";s:5:"it-IT";s:6:"author";a:2:{s:5:"@type";s:6:"Person";s:4:"name";s:16:"Barbieri Roberto";}s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-8478dbea11a77851bbf7ada7033d4b29.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-8478dbea11a77851bbf7ada7033d4b29.php new file mode 100644 index 00000000..a138f840 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-8478dbea11a77851bbf7ada7033d4b29.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/2861";s:4:"name";s:9:"direttore";s:8:"headline";s:9:"direttore";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-87099c35842bf51250ef2682c54fc13f.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-87099c35842bf51250ef2682c54fc13f.php new file mode 100644 index 00000000..e225fb31 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-87099c35842bf51250ef2682c54fc13f.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3142";s:4:"name";s:4:"Arpa";s:8:"headline";s:4:"Arpa";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-98a68e4b9aad7238d67c58d286b4e5be.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-98a68e4b9aad7238d67c58d286b4e5be.php new file mode 100644 index 00000000..3a573004 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-98a68e4b9aad7238d67c58d286b4e5be.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:67:"https://conservatorio2025.ddev.site/#/schema/com_content/article/28";s:4:"name";s:25:"Sostieni il Conservatorio";s:8:"headline";s:25:"Sostieni il Conservatorio";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-9b46758827aaad71c53e379edd1999cd.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-9b46758827aaad71c53e379edd1999cd.php new file mode 100644 index 00000000..a8d0ffc8 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-9b46758827aaad71c53e379edd1999cd.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3177";s:4:"name";s:27:"Strumenti ad arco e a corda";s:8:"headline";s:27:"Strumenti ad arco e a corda";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-9cabd11161f838b255ab99b5c7d25ed4.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-9cabd11161f838b255ab99b5c7d25ed4.php new file mode 100644 index 00000000..a8d0ffc8 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-9cabd11161f838b255ab99b5c7d25ed4.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3177";s:4:"name";s:27:"Strumenti ad arco e a corda";s:8:"headline";s:27:"Strumenti ad arco e a corda";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-a87ca2ac4df1749b2537f9b10c816b0d.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-a87ca2ac4df1749b2537f9b10c816b0d.php new file mode 100644 index 00000000..eeb93f71 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-a87ca2ac4df1749b2537f9b10c816b0d.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/1392";s:4:"name";s:9:"Didattica";s:8:"headline";s:9:"Didattica";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-aa9e21553ea63e63a5308c1a466cf3cd.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-aa9e21553ea63e63a5308c1a466cf3cd.php new file mode 100644 index 00000000..2f7b15d9 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-aa9e21553ea63e63a5308c1a466cf3cd.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/4097";s:4:"name";s:13:"Arpa - DCPL01";s:8:"headline";s:13:"Arpa - DCPL01";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-af943475eaa6f779cfa9f4218cea0a86.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-af943475eaa6f779cfa9f4218cea0a86.php new file mode 100644 index 00000000..b6c825ac --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-af943475eaa6f779cfa9f4218cea0a86.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/6693";s:4:"name";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:8:"headline";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:10:"inLanguage";s:5:"it-IT";s:12:"dateModified";s:25:"2024-12-30T14:01:55+00:00";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b0e177b1f723b8005f5bd43098b11702.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b0e177b1f723b8005f5bd43098b11702.php new file mode 100644 index 00000000..268ea44b --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b0e177b1f723b8005f5bd43098b11702.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/1391";s:4:"name";s:16:"Il Conservatorio";s:8:"headline";s:16:"Il Conservatorio";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b2b5f832349501c31617630389ae902b.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b2b5f832349501c31617630389ae902b.php new file mode 100644 index 00000000..66687fb2 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b2b5f832349501c31617630389ae902b.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3319";s:4:"name";s:14:"Beltrami Carlo";s:8:"headline";s:14:"Beltrami Carlo";s:10:"inLanguage";s:5:"it-IT";s:6:"author";a:2:{s:5:"@type";s:6:"Person";s:4:"name";s:14:"Beltrami Carlo";}s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b452c343bdd4b64de8cb2f82a6f156dc.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b452c343bdd4b64de8cb2f82a6f156dc.php new file mode 100644 index 00000000..5296ba2c --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-b452c343bdd4b64de8cb2f82a6f156dc.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/6060";s:4:"name";s:12:"Armato Paolo";s:8:"headline";s:12:"Armato Paolo";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-c3a79738e7b8c10fd429017d10de1bf5.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-c3a79738e7b8c10fd429017d10de1bf5.php new file mode 100644 index 00000000..b6c825ac --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-c3a79738e7b8c10fd429017d10de1bf5.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/6693";s:4:"name";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:8:"headline";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:10:"inLanguage";s:5:"it-IT";s:12:"dateModified";s:25:"2024-12-30T14:01:55+00:00";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-cb6ba689b2b5096b15bcf029e71671e7.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-cb6ba689b2b5096b15bcf029e71671e7.php new file mode 100644 index 00000000..e9bd68ed --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-cb6ba689b2b5096b15bcf029e71671e7.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/5785";s:4:"name";s:22:"Saxofono jazz - DCSL42";s:8:"headline";s:22:"Saxofono jazz - DCSL42";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-cea6fbe7d0c8acbc4fe7fc0486e41b91.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-cea6fbe7d0c8acbc4fe7fc0486e41b91.php new file mode 100644 index 00000000..d5bd0182 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-cea6fbe7d0c8acbc4fe7fc0486e41b91.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3173";s:4:"name";s:23:"Canto e Teatro musicale";s:8:"headline";s:23:"Canto e Teatro musicale";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-dd0ba63a33dc123c0c21dadb155bbc3e.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-dd0ba63a33dc123c0c21dadb155bbc3e.php new file mode 100644 index 00000000..cdf68b5c --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-dd0ba63a33dc123c0c21dadb155bbc3e.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:7:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/6693";s:4:"name";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:8:"headline";s:61:"Regolamento dei Corsi accademici - valido dall'a.a. 2023/2024";s:10:"inLanguage";s:5:"it-IT";s:12:"dateModified";s:25:"2023-09-28T08:28:16+00:00";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-e4f9850b84ad5d48eb91093f613a98bb.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-e4f9850b84ad5d48eb91093f613a98bb.php new file mode 100644 index 00000000..45d99654 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-e4f9850b84ad5d48eb91093f613a98bb.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:67:"https://conservatorio2025.ddev.site/#/schema/com_content/article/26";s:4:"name";s:7:"La Sede";s:8:"headline";s:7:"La Sede";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-edca7c078c51929c552354bb78a0560a.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-edca7c078c51929c552354bb78a0560a.php new file mode 100644 index 00000000..c7236f26 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-edca7c078c51929c552354bb78a0560a.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/5244";s:4:"name";s:13:"Saxofono jazz";s:8:"headline";s:13:"Saxofono jazz";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-f237c1cf2de432fab0fa7eea2412e6e2.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-f237c1cf2de432fab0fa7eea2412e6e2.php new file mode 100644 index 00000000..afa17140 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-f237c1cf2de432fab0fa7eea2412e6e2.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3058";s:4:"name";s:9:"Chi siamo";s:8:"headline";s:9:"Chi siamo";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-f7d56f47aed0cf0931bcef734ece610f.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-f7d56f47aed0cf0931bcef734ece610f.php new file mode 100644 index 00000000..464e1aa1 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-f7d56f47aed0cf0931bcef734ece610f.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:68:"https://conservatorio2025.ddev.site/#/schema/com_content/article/111";s:4:"name";s:10:"Biblioteca";s:8:"headline";s:10:"Biblioteca";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-fe063ce4a24faa7268f9538f24a65b67.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-fe063ce4a24faa7268f9538f24a65b67.php new file mode 100644 index 00000000..e79430bd --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-fe063ce4a24faa7268f9538f24a65b67.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/4159";s:4:"name";s:4:"Arpa";s:8:"headline";s:4:"Arpa";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-feef846d45a93a61524422d40a95aee9.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-feef846d45a93a61524422d40a95aee9.php new file mode 100644 index 00000000..becb1d27 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-feef846d45a93a61524422d40a95aee9.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:9:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3769";s:4:"name";s:54:"Informativa privacy sul trattamento dei dati personali";s:8:"headline";s:54:"Informativa privacy sul trattamento dei dati personali";s:10:"inLanguage";s:5:"it-IT";s:14:"articleSection";s:7:"Privacy";s:11:"dateCreated";s:25:"2018-10-13T07:54:51+00:00";s:12:"dateModified";s:25:"2020-12-18T12:02:40+00:00";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-ff61962fbf27a7a43f207a504ccceb6d.php b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-ff61962fbf27a7a43f207a504ccceb6d.php new file mode 100644 index 00000000..e225fb31 --- /dev/null +++ b/administrator/cache/schemaorg/7e36f8d5025463b06e6195883da3a379-cache-schemaorg-ff61962fbf27a7a43f207a504ccceb6d.php @@ -0,0 +1 @@ +#x#a:2:{s:6:"result";a:1:{i:0;a:6:{s:5:"@type";s:7:"Article";s:3:"@id";s:69:"https://conservatorio2025.ddev.site/#/schema/com_content/article/3142";s:4:"name";s:4:"Arpa";s:8:"headline";s:4:"Arpa";s:10:"inLanguage";s:5:"it-IT";s:8:"isPartOf";a:1:{s:3:"@id";s:57:"https://conservatorio2025.ddev.site/#/schema/WebPage/base";}}}s:6:"output";s:0:"";} \ No newline at end of file diff --git a/administrator/components/com_highlights/access.xml b/administrator/components/com_highlights/access.xml new file mode 100644 index 00000000..64b5532c --- /dev/null +++ b/administrator/components/com_highlights/access.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + +
+ +
\ No newline at end of file diff --git a/administrator/components/com_highlights/config.xml b/administrator/components/com_highlights/config.xml new file mode 100644 index 00000000..15b7782a --- /dev/null +++ b/administrator/components/com_highlights/config.xml @@ -0,0 +1,115 @@ + + +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ diff --git a/administrator/components/com_highlights/forms/etichetta.xml b/administrator/components/com_highlights/forms/etichetta.xml new file mode 100644 index 00000000..b8b92c3b --- /dev/null +++ b/administrator/components/com_highlights/forms/etichetta.xml @@ -0,0 +1,20 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/administrator/components/com_highlights/forms/filter_etichette.xml b/administrator/components/com_highlights/forms/filter_etichette.xml new file mode 100644 index 00000000..561a919a --- /dev/null +++ b/administrator/components/com_highlights/forms/filter_etichette.xml @@ -0,0 +1,43 @@ + +
+ + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/administrator/components/com_highlights/forms/filter_highlights.xml b/administrator/components/com_highlights/forms/filter_highlights.xml new file mode 100644 index 00000000..04142b7b --- /dev/null +++ b/administrator/components/com_highlights/forms/filter_highlights.xml @@ -0,0 +1,43 @@ + +
+ + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/administrator/components/com_highlights/forms/highlight.xml b/administrator/components/com_highlights/forms/highlight.xml new file mode 100644 index 00000000..4b173551 --- /dev/null +++ b/administrator/components/com_highlights/forms/highlight.xml @@ -0,0 +1,30 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/administrator/components/com_highlights/highlights.xml b/administrator/components/com_highlights/highlights.xml new file mode 100644 index 00000000..f825f864 --- /dev/null +++ b/administrator/components/com_highlights/highlights.xml @@ -0,0 +1,95 @@ + + + com_highlights + 2024-12-30 + 2024 Eddy Prosperi + GNU General Public License versione 2 o successiva; vedi LICENSE.txt + Eddy Prosperi + eddy.prosperi@protocollicreativi.it + http:// + CVS: 1.0.0 + + Pcrt\Component\Highlights + + script.php + + + + sql/install.mysql.utf8.sql + + + + + sql/updates + + + + + sql/uninstall.mysql.utf8.sql + + + + + + + src + forms + tmpl + + + css + js + joomla.asset.json + + + + en-GB/com_highlights.ini + it-IT/com_highlights.ini + + + COM_HIGHLIGHTS + + + COM_HIGHLIGHTS_TITLE_HIGHLIGHTS + + COM_HIGHLIGHTS_TITLE_ETICHETTE + + + + access.xml + config.xml + forms + src + tmpl + services + presets + sql + + + + en-GB/com_highlights.ini + en-GB/com_highlights.sys.ini + it-IT/com_highlights.ini + it-IT/com_highlights.sys.ini + + + + +
+ +
+
+
+ + + + + + + + + https://nocdn.component-creator.com/index.php?task=builder.preupdatecheckhook&option=com_combuilder&component=NzY0NzgtMjEzOTAw + +
+ + diff --git a/administrator/components/com_highlights/presets/content.xml b/administrator/components/com_highlights/presets/content.xml new file mode 100644 index 00000000..214cf4c8 --- /dev/null +++ b/administrator/components/com_highlights/presets/content.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/administrator/components/com_highlights/script.php b/administrator/components/com_highlights/script.php new file mode 100644 index 00000000..fe1f628e --- /dev/null +++ b/administrator/components/com_highlights/script.php @@ -0,0 +1,1081 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +define('MODIFIED', 1); +define('NOT_MODIFIED', 2); + +defined('_JEXEC') or die(); + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Installer\Installer; +use \Joomla\CMS\Installer\InstallerScript; + +/** + * Updates the database structure of the component + * + * @version Release: 0.2b + * @author Component Creator + * @since 0.1b + */ +class com_highlightsInstallerScript extends InstallerScript +{ + /** + * The title of the component (printed on installation and uninstallation messages) + * + * @var string + */ + protected $extension = 'Highlights'; + + /** + * The minimum Joomla! version required to install this extension + * + * @var string + */ + protected $minimumJoomla = '4.0'; + + /** + * Method called before install/update the component. Note: This method won't be called during uninstall process. + * + * @param string $type Type of process [install | update] + * @param mixed $parent Object who called this method + * + * @return boolean True if the process should continue, false otherwise + * @throws Exception + */ + public function preflight($type, $parent) + { + $result = parent::preflight($type, $parent); + + if (!$result) + { + return $result; + } + + // logic for preflight before install + return $result; + } + + /** + * Method to install the component + * + * @param mixed $parent Object who called this method. + * + * @return void + * + * @since 0.2b + */ + public function install($parent) + { + $this->installDb($parent); + $this->installPlugins($parent); + $this->installModules($parent); + } + + /** + * Method to update the DB of the component + * + * @param mixed $parent Object who started the upgrading process + * + * @return void + * + * @since 0.2b + * @throws Exception + */ + private function installDb($parent) + { + $installation_folder = $parent->getParent()->getPath('source'); + + $app = Factory::getApplication(); + + if (function_exists('simplexml_load_file') && file_exists($installation_folder . '/installer/structure.xml')) + { + $component_data = simplexml_load_file($installation_folder . '/installer/structure.xml'); + + // Check if there are tables to import. + foreach ($component_data->children() as $table) + { + $this->processTable($app, $table); + } + } + else + { + if (!function_exists('simplexml_load_file')) + { + $app->enqueueMessage(Text::_('This script needs \'simplexml_load_file\' to update the component')); + } + else + { + $app->enqueueMessage(Text::_('Structure file was not found.')); + } + } + } + + /** + * Process a table + * + * @param CMSApplication $app Application object + * @param SimpleXMLElement $table Table to process + * + * @return void + * + * @since 0.2b + */ + private function processTable($app, $table) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + + $table_added = false; + + if (isset($table['action'])) + { + switch ($table['action']) + { + case 'add': + + // Check if the table exists before create the statement + if (!$this->existsTable($table['table_name'])) + { + $create_statement = $this->generateCreateTableStatement($table); + $db->setQuery($create_statement); + + try + { + $db->execute(); + $app->enqueueMessage( + Text::sprintf( + 'Table `%s` has been successfully created', + (string) $table['table_name'] + ) + ); + $table_added = true; + } catch (Exception $ex) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error creating the table `%s`. Error: %s', + (string) $table['table_name'], + $ex->getMessage() + ), 'error' + ); + } + } + break; + case 'change': + + // Check if the table exists first to avoid errors. + if ($this->existsTable($table['old_name']) && !$this->existsTable($table['new_name'])) + { + try + { + $db->renameTable($table['old_name'], $table['new_name']); + $app->enqueueMessage( + Text::sprintf( + 'Table `%s` was successfully renamed to `%s`', + $table['old_name'], + $table['new_name'] + ) + ); + } catch (Exception $ex) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error renaming the table `%s`. Error: %s', + $table['old_name'], + $ex->getMessage() + ), 'error' + ); + } + } + else + { + if (!$this->existsTable($table['table_name'])) + { + // If the table does not exists, let's create it. + $create_statement = $this->generateCreateTableStatement($table); + $db->setQuery($create_statement); + + try + { + $db->execute(); + $app->enqueueMessage( + Text::sprintf('Table `%s` has been successfully created', $table['table_name']) + ); + $table_added = true; + } catch (Exception $ex) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error creating the table `%s`. Error: %s', + $table['table_name'], + $ex->getMessage() + ), 'error' + ); + } + } + } + break; + case 'remove': + + try + { + // We make sure that the table will be removed only if it exists specifying ifExists argument as true. + $db->dropTable((string) $table['table_name'], true); + $app->enqueueMessage( + Text::sprintf('Table `%s` was successfully deleted', $table['table_name']) + ); + } catch (Exception $ex) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error deleting Table `%s`. Error: %s', + $table['table_name'], $ex->getMessage() + ), 'error' + ); + } + + break; + } + } + + // If the table wasn't added before, let's process the fields of the table + if (!$table_added) + { + if ($this->existsTable($table['table_name'])) + { + $this->executeFieldsUpdating($app, $table); + } + } + } + + /** + * Checks if a certain exists on the current database + * + * @param string $table_name Name of the table + * + * @return boolean True if it exists, false if it does not. + */ + private function existsTable($table_name) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + + $table_name = str_replace('#__', $db->getPrefix(), (string) $table_name); + + return in_array($table_name, $db->getTableList()); + } + + /** + * Generates a 'CREATE TABLE' statement for the tables passed by argument. + * + * @param SimpleXMLElement $table Table of the database + * + * @return string 'CREATE TABLE' statement + */ + private function generateCreateTableStatement($table) + { + $create_table_statement = ''; + + if (isset($table->field)) + { + $fields = $table->children(); + + $fields_definitions = array(); + $indexes = array(); + + $db = Factory::getContainer()->get('DatabaseDriver'); + + foreach ($fields as $field) + { + $field_definition = $this->generateColumnDeclaration($field); + + if ($field_definition !== false) + { + $fields_definitions[] = $field_definition; + } + + if ($field['index'] == 'index') + { + $indexes[] = $field['field_name']; + } + } + + foreach ($indexes as $index) + { + $fields_definitions[] = Text::sprintf( + 'INDEX %s (%s ASC)', + $db->quoteName((string) $index), $index + ); + } + + // Avoid duplicate PK definition + if (strpos(implode(',', $fields_definitions), 'PRIMARY KEY') === false) + { + $fields_definitions[] = 'PRIMARY KEY (`id`)'; + } + + $create_table_statement = Text::sprintf( + 'CREATE TABLE IF NOT EXISTS %s (%s)', + $table['table_name'], + implode(',', $fields_definitions) + ); + + if(isset($table['storage_engine']) && !empty($table['storage_engine'])) + { + $create_table_statement .= " ENGINE=" . $table['storage_engine']; + } + if(isset($table['collation'])) + { + $create_table_statement .= " DEFAULT COLLATE=" . $table['collation']; + } + } + return $create_table_statement; + } + + /** + * Generate a column declaration + * + * @param SimpleXMLElement $field Field data + * + * @return string Column declaration + */ + private function generateColumnDeclaration($field) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $col_name = $db->quoteName((string) $field['field_name']); + $data_type = $this->getFieldType($field); + + if ($data_type !== false) + { + $default_value = (isset($field['default'])) ? 'DEFAULT ' . $field['default'] : ''; + + $other_data = ''; + + if (isset($field['is_autoincrement']) && $field['is_autoincrement'] == 1) + { + $other_data .= ' AUTO_INCREMENT PRIMARY KEY'; + } + + $comment_value = (isset($field['description'])) ? 'COMMENT ' . $db->quote((string) $field['description']) : ''; + + if(strtolower($field['field_type']) == 'datetime' || strtolower($field['field_type']) == 'text') + { + return Text::sprintf( + '%s %s %s %s %s', $col_name, $data_type, + $default_value, $other_data, $comment_value + ); + } + + if((isset($field['required']) && $field['required'] == 1) || $field['field_name'] == 'id') + { + return Text::sprintf( + '%s %s NOT NULL %s %s %s', $col_name, $data_type, + $default_value, $other_data, $comment_value + ); + } + + return Text::sprintf( + '%s %s NULL %s %s %s', $col_name, $data_type, + $default_value, $other_data, $comment_value + ); + + } + + return false; + } + + /** + * Generates SQL field type of a field. + * + * @param SimpleXMLElement $field Field information + * + * @return mixed SQL string data type, false on failure. + */ + private function getFieldType($field) + { + $data_type = (string) $field['field_type']; + + if (isset($field['field_length']) && ($this->allowsLengthField($data_type) || $data_type == 'ENUM')) + { + $data_type .= '(' . (string) $field['field_length'] . ')'; + } + + return (!empty($data_type)) ? $data_type : false; + } + + /** + * Check if a SQL type allows length values. + * + * @param string $field_type SQL type + * + * @return boolean True if it allows length values, false if it does not. + */ + private function allowsLengthField($field_type) + { + $allow_length = array( + 'INT', + 'VARCHAR', + 'CHAR', + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INTEGER', + 'BIGINT', + 'FLOAT', + 'DOUBLE', + 'DECIMAL', + 'NUMERIC' + ); + + return (in_array((string) $field_type, $allow_length)); + } + + /** + * Updates all the fields related to a table. + * + * @param CMSApplication $app Application Object + * @param SimpleXMLElement $table Table information. + * + * @return void + */ + private function executeFieldsUpdating($app, $table) + { + if (isset($table->field)) + { + foreach ($table->children() as $field) + { + $table_name = (string) $table['table_name']; + + $this->processField($app, $table_name, $field); + } + } + } + + /** + * Process a certain field. + * + * @param CMSApplication $app Application object + * @param string $table_name The name of the table that contains the field. + * @param SimpleXMLElement $field Field Information. + * + * @return void + */ + private function processField($app, $table_name, $field) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + + if (isset($field['action'])) + { + switch ($field['action']) + { + case 'add': + $result = $this->addField($table_name, $field); + + if ($result === MODIFIED) + { + $app->enqueueMessage( + Text::sprintf('Field `%s` has been successfully added', $field['field_name']) + ); + } + else + { + if ($result !== NOT_MODIFIED) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error adding the field `%s`. Error: %s', + $field['field_name'], $result + ), 'error' + ); + } + } + break; + case 'change': + + if (isset($field['old_name']) && isset($field['new_name'])) + { + if ($this->existsField($table_name, $field['old_name']) && !$this->existsField($table_name, $field['new_name'])) + { + $renaming_statement = Text::sprintf( + 'ALTER TABLE %s CHANGE %s %s %s', + $table_name, $db->quoteName($field['old_name']->__toString()), + $db->quoteName($field['new_name']->__toString()), + $this->getFieldType($field) + ); + $db->setQuery($renaming_statement); + + try + { + $db->execute(); + $app->enqueueMessage( + Text::sprintf('Field `%s` has been successfully modified', $field['old_name']) + ); + } catch (Exception $ex) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error modifying the field `%s`. Error: %s', + $field['field_name'], + $ex->getMessage() + ), 'error' + ); + } + } + else + { + $result = $this->addField($table_name, $field); + + if ($result === MODIFIED) + { + $app->enqueueMessage( + Text::sprintf('Field `%s` has been successfully modified', $field['field_name']) + ); + } + else + { + if ($result !== NOT_MODIFIED) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error modifying the field `%s`. Error: %s', + $field['field_name'], $result + ), 'error' + ); + } + } + } + } + else + { + $result = $this->addField($table_name, $field); + + if ($result === MODIFIED) + { + $app->enqueueMessage( + Text::sprintf('Field `%s` has been successfully modified', $field['field_name']) + ); + } + else + { + if ($result !== NOT_MODIFIED) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error modifying the field `%s`. Error: %s', + $field['field_name'], $result + ), 'error' + ); + } + } + } + + break; + case 'remove': + + // Check if the field exists first to prevent issue removing the field + if ($this->existsField($table_name, $field['field_name'])) + { + $drop_statement = Text::sprintf( + 'ALTER TABLE `%s` DROP COLUMN `%s`', + $table_name, $field['field_name'] + ); + $db->setQuery($drop_statement); + + try + { + $db->execute(); + $app->enqueueMessage( + Text::sprintf('Field `%s` has been successfully deleted', $field['field_name']) + ); + } catch (Exception $ex) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error deleting the field `%s`. Error: %s', + $field['field_name'], + $ex->getMessage() + ), 'error' + ); + } + } + + break; + } + } + else + { + $result = $this->addField($table_name, $field); + + if ($result === MODIFIED) + { + $app->enqueueMessage( + Text::sprintf('Field `%s` has been successfully added', $field['field_name']) + ); + } + else + { + if ($result !== NOT_MODIFIED) + { + $app->enqueueMessage( + Text::sprintf( + 'There was an error adding the field `%s`. Error: %s', + $field['field_name'], $result + ), 'error' + ); + } + } + } + } + + /** + * Add a field if it does not exists or modify it if it does. + * + * @param string $table_name Table name + * @param SimpleXMLElement $field Field Information + * + * @return mixed Constant on success(self::$MODIFIED | self::$NOT_MODIFIED), error message if an error occurred + */ + private function addField($table_name, $field) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + + $query_generated = false; + + // Check if the field exists first to prevent issues adding the field + if ($this->existsField($table_name, $field['field_name'])) + { + if ($this->needsToUpdate($table_name, $field)) + { + $change_statement = $this->generateChangeFieldStatement($table_name, $field); + $db->setQuery($change_statement); + $query_generated = true; + } + } + else + { + $add_statement = $this->generateAddFieldStatement($table_name, $field); + $db->setQuery($add_statement); + $query_generated = true; + } + + if ($query_generated) + { + try + { + $db->execute(); + + return MODIFIED; + } catch (Exception $ex) + { + return $ex->getMessage(); + } + } + + return NOT_MODIFIED; + } + + /** + * Checks if a field exists on a table + * + * @param string $table_name Table name + * @param string $field_name Field name + * + * @return boolean True if exists, false if it do + */ + private function existsField($table_name, $field_name) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + + return in_array((string) $field_name, array_keys($db->getTableColumns($table_name))); + } + + /** + * Check if a field needs to be updated. + * + * @param string $table_name Table name + * @param SimpleXMLElement $field Field information + * + * @return boolean True if the field has to be updated, false otherwise + */ + private function needsToUpdate($table_name, $field) + { + + if(!isset($field['action']) || $field['field_name'] == 'id') + { + return false; + } + + $db = Factory::getContainer()->get('DatabaseDriver'); + + $query = Text::sprintf( + 'SHOW FULL COLUMNS FROM `%s` WHERE Field LIKE %s', $table_name, $db->quote((string) $field['field_name']) + ); + $db->setQuery($query); + + $field_info = $db->loadObject(); + + if (strcasecmp($field_info->Type, $this->getFieldType($field)) !=0) + { + return true; + } + else + { + return false; + } + } + + /** + * Generates an change column statement + * + * @param string $table_name Table name + * @param SimpleXMLElement $field Field Information + * + * @return string Change column statement + */ + private function generateChangeFieldStatement($table_name, $field) + { + $column_declaration = $this->generateColumnDeclaration($field); + + return Text::sprintf('ALTER TABLE %s MODIFY %s', $table_name, $column_declaration); + } + + /** + * Generates an add column statement + * + * @param string $table_name Table name + * @param SimpleXMLElement $field Field Information + * + * @return string Add column statement + */ + private function generateAddFieldStatement($table_name, $field) + { + $column_declaration = $this->generateColumnDeclaration($field); + + return Text::sprintf('ALTER TABLE %s ADD %s', $table_name, $column_declaration); + } + + /** + * Installs plugins for this component + * + * @param mixed $parent Object who called the install/update method + * + * @return void + */ + private function installPlugins($parent) + { + $installation_folder = $parent->getParent()->getPath('source'); + $app = Factory::getApplication(); + + /* @var $plugins SimpleXMLElement */ + if (method_exists($parent, 'getManifest')) + { + $plugins = $parent->getManifest()->plugins; + } + else + { + $plugins = $parent->get('manifest')->plugins; + } + + if (count($plugins->children())) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + foreach ($plugins->children() as $plugin) + { + $pluginName = (string) $plugin['plugin']; + $pluginGroup = (string) $plugin['group']; + $path = $installation_folder . '/plugins/' . $pluginGroup . '/' . $pluginName; + $installer = new Installer; + + if (!$this->isAlreadyInstalled('plugin', $pluginName, $pluginGroup)) + { + $result = $installer->install($path); + } + else + { + $result = $installer->update($path); + } + + if ($result) + { + $app->enqueueMessage('Plugin ' . $pluginName . ' was installed successfully'); + } + else + { + $app->enqueueMessage('There was an issue installing the plugin ' . $pluginName, + 'error'); + } + + $query + ->clear() + ->update('#__extensions') + ->set('enabled = 1') + ->where( + array( + 'type LIKE ' . $db->quote('plugin'), + 'element LIKE ' . $db->quote($pluginName), + 'folder LIKE ' . $db->quote($pluginGroup) + ) + ); + $db->setQuery($query); + $db->execute(); + } + } + } + + /** + * Check if an extension is already installed in the system + * + * @param string $type Extension type + * @param string $name Extension name + * @param mixed $folder Extension folder(for plugins) + * + * @return boolean + */ + private function isAlreadyInstalled($type, $name, $folder = null) + { + $result = false; + + switch ($type) + { + case 'plugin': + $result = file_exists(JPATH_PLUGINS . '/' . $folder . '/' . $name); + break; + case 'module': + $result = file_exists(JPATH_SITE . '/modules/' . $name); + break; + } + + return $result; + } + + /** + * Installs plugins for this component + * + * @param mixed $parent Object who called the install/update method + * + * @return void + */ + private function installModules($parent) + { + $installation_folder = $parent->getParent()->getPath('source'); + $app = Factory::getApplication(); + + if (method_exists($parent, 'getManifest')) + { + $modules = $parent->getManifest()->modules; + } + else + { + $modules = $parent->get('manifest')->modules; + } + + if (!empty($modules)) + { + + if (count($modules->children())) + { + foreach ($modules->children() as $module) + { + $moduleName = (string) $module['module']; + $path = $installation_folder . '/modules/' . $moduleName; + $installer = new Installer; + + if (!$this->isAlreadyInstalled('module', $moduleName)) + { + $result = $installer->install($path); + } + else + { + $result = $installer->update($path); + } + + if ($result) + { + $app->enqueueMessage('Module ' . $moduleName . ' was installed successfully'); + } + else + { + $app->enqueueMessage('There was an issue installing the module ' . $moduleName, + 'error'); + } + } + } + } + } + + /** + * Method to update the component + * + * @param mixed $parent Object who called this method. + * + * @return void + */ + public function update($parent) + { + $this->installDb($parent); + $this->installPlugins($parent); + $this->installModules($parent); + } + + /** + * Method to uninstall the component + * + * @param mixed $parent Object who called this method. + * + * @return void + */ + public function uninstall($parent) + { + $this->uninstallPlugins($parent); + $this->uninstallModules($parent); + } + + /** + * Uninstalls plugins + * + * @param mixed $parent Object who called the uninstall method + * + * @return void + */ + private function uninstallPlugins($parent) + { + $app = Factory::getApplication(); + + if (method_exists($parent, 'getManifest')) + { + $plugins = $parent->getManifest()->plugins; + } + else + { + $plugins = $parent->get('manifest')->plugins; + } + + if (count($plugins->children())) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + foreach ($plugins->children() as $plugin) + { + $pluginName = (string) $plugin['plugin']; + $pluginGroup = (string) $plugin['group']; + $query + ->clear() + ->select('extension_id') + ->from('#__extensions') + ->where( + array( + 'type LIKE ' . $db->quote('plugin'), + 'element LIKE ' . $db->quote($pluginName), + 'folder LIKE ' . $db->quote($pluginGroup) + ) + ); + $db->setQuery($query); + $extension = $db->loadResult(); + + if (!empty($extension)) + { + $installer = new Installer; + $result = $installer->uninstall('plugin', $extension); + + if ($result) + { + $app->enqueueMessage('Plugin ' . $pluginName . ' was uninstalled successfully'); + } + else + { + $app->enqueueMessage('There was an issue uninstalling the plugin ' . $pluginName, + 'error'); + } + } + } + } + } + + /** + * Uninstalls plugins + * + * @param mixed $parent Object who called the uninstall method + * + * @return void + */ + private function uninstallModules($parent) + { + $app = Factory::getApplication(); + + if (method_exists($parent, 'getManifest')) + { + $modules = $parent->getManifest()->modules; + } + else + { + $modules = $parent->get('manifest')->modules; + } + + if (!empty($modules)) + { + + if (count($modules->children())) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + foreach ($modules->children() as $plugin) + { + $moduleName = (string) $plugin['module']; + $query + ->clear() + ->select('extension_id') + ->from('#__extensions') + ->where( + array( + 'type LIKE ' . $db->quote('module'), + 'element LIKE ' . $db->quote($moduleName) + ) + ); + $db->setQuery($query); + $extension = $db->loadResult(); + + if (!empty($extension)) + { + $installer = new Installer; + $result = $installer->uninstall('module', $extension); + + if ($result) + { + $app->enqueueMessage('Module ' . $moduleName . ' was uninstalled successfully'); + } + else + { + $app->enqueueMessage('There was an issue uninstalling the module ' . $moduleName, + 'error'); + } + } + } + } + } + } + + /** + * @param string $type type + * @param string $parent parent + * + * @return boolean + * @since Kunena + */ + public function postflight($type, $parent) + { + + + return true; + } + + +} diff --git a/administrator/components/com_highlights/services/provider.php b/administrator/components/com_highlights/services/provider.php new file mode 100644 index 00000000..c1655089 --- /dev/null +++ b/administrator/components/com_highlights/services/provider.php @@ -0,0 +1,66 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Categories\CategoryFactoryInterface; +use Joomla\CMS\Component\Router\RouterFactoryInterface; +use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface; +use Joomla\CMS\Extension\ComponentInterface; +use Joomla\CMS\Extension\Service\Provider\CategoryFactory; +use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory; +use Joomla\CMS\Extension\Service\Provider\MVCFactory; +use Joomla\CMS\Extension\Service\Provider\RouterFactory; +use Joomla\CMS\HTML\Registry; +use Joomla\CMS\MVC\Factory\MVCFactoryInterface; +use Pcrt\Component\Highlights\Administrator\Extension\HighlightsComponent; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; + + +/** + * The Highlights service provider. + * + * @since 1.0.0 + */ +return new class implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since 1.0.0 + */ + public function register(Container $container) + { + + $container->registerServiceProvider(new CategoryFactory('\\Pcrt\\Component\\Highlights')); + $container->registerServiceProvider(new MVCFactory('\\Pcrt\\Component\\Highlights')); + $container->registerServiceProvider(new ComponentDispatcherFactory('\\Pcrt\\Component\\Highlights')); + $container->registerServiceProvider(new RouterFactory('\\Pcrt\\Component\\Highlights')); + + $container->set( + ComponentInterface::class, + function (Container $container) + { + $component = new HighlightsComponent($container->get(ComponentDispatcherFactoryInterface::class)); + + $component->setRegistry($container->get(Registry::class)); + $component->setMVCFactory($container->get(MVCFactoryInterface::class)); + $component->setCategoryFactory($container->get(CategoryFactoryInterface::class)); + $component->setRouterFactory($container->get(RouterFactoryInterface::class)); + + return $component; + } + ); + } +}; diff --git a/administrator/components/com_highlights/sql/index.html b/administrator/components/com_highlights/sql/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/administrator/components/com_highlights/sql/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/administrator/components/com_highlights/sql/install.mysql.utf8.sql b/administrator/components/com_highlights/sql/install.mysql.utf8.sql new file mode 100644 index 00000000..e34f3280 --- /dev/null +++ b/administrator/components/com_highlights/sql/install.mysql.utf8.sql @@ -0,0 +1,48 @@ +CREATE TABLE IF NOT EXISTS `#__highlights_` ( +`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + +`state` TINYINT(1) NULL DEFAULT 1, +`ordering` INT(11) NULL DEFAULT 0, +`checked_out` INT(11) UNSIGNED, +`checked_out_time` DATETIME NULL DEFAULT NULL , +`created_by` INT(11) NULL DEFAULT 0, +`modified_by` INT(11) NULL DEFAULT 0, +`etichetta` VARCHAR(255) NULL DEFAULT "", +`titolo` VARCHAR(255) NULL DEFAULT "", +`sottotitolo` VARCHAR(255) NULL DEFAULT "", +`descrizione` TEXT NULL , +`lingua` VARCHAR(5) NULL DEFAULT "", +`link_pulsante` VARCHAR(255) NULL DEFAULT "", +`testo_pulsante` VARCHAR(255) NULL DEFAULT "", +`data` DATETIME NULL DEFAULT NULL , +`immagine_main` TEXT NULL , +`immagine_secondaria` TEXT NULL , +`data_inizio_pubblicazione` DATETIME NULL DEFAULT NULL , +`data_fine_pubblicazione` DATETIME NULL DEFAULT NULL , +PRIMARY KEY (`id`) +,KEY `idx_state` (`state`) +,KEY `idx_checked_out` (`checked_out`) +,KEY `idx_created_by` (`created_by`) +,KEY `idx_modified_by` (`modified_by`) +) DEFAULT COLLATE=utf8mb4_unicode_ci; + +CREATE INDEX `#__highlights__etichetta` ON `#__highlights_`(`etichetta`); + +CREATE TABLE IF NOT EXISTS `#__highlights_etichetta` ( +`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + +`state` TINYINT(1) NULL DEFAULT 1, +`ordering` INT(11) NULL DEFAULT 0, +`checked_out` INT(11) UNSIGNED, +`checked_out_time` DATETIME NULL DEFAULT NULL , +`created_by` INT(11) NULL DEFAULT 0, +`modified_by` INT(11) NULL DEFAULT 0, +`nome` VARCHAR(255) NULL DEFAULT "", +`lingua` VARCHAR(5) NULL DEFAULT "", +PRIMARY KEY (`id`) +,KEY `idx_state` (`state`) +,KEY `idx_checked_out` (`checked_out`) +,KEY `idx_created_by` (`created_by`) +,KEY `idx_modified_by` (`modified_by`) +) DEFAULT COLLATE=utf8mb4_unicode_ci; + diff --git a/administrator/components/com_highlights/sql/uninstall.mysql.utf8.sql b/administrator/components/com_highlights/sql/uninstall.mysql.utf8.sql new file mode 100644 index 00000000..1113e093 --- /dev/null +++ b/administrator/components/com_highlights/sql/uninstall.mysql.utf8.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS `#__highlights_`; +DROP TABLE IF EXISTS `#__highlights_etichetta`; diff --git a/administrator/components/com_highlights/sql/updates/1.0.0.sql b/administrator/components/com_highlights/sql/updates/1.0.0.sql new file mode 100644 index 00000000..e69de29b diff --git a/administrator/components/com_highlights/sql/xml/mysql.xml b/administrator/components/com_highlights/sql/xml/mysql.xml new file mode 100644 index 00000000..b10de3f7 --- /dev/null +++ b/administrator/components/com_highlights/sql/xml/mysql.xml @@ -0,0 +1,15 @@ + + + + + + + mysql + mysqli + pdomysql + + + + + + \ No newline at end of file diff --git a/administrator/components/com_highlights/src/Controller/DisplayController.php b/administrator/components/com_highlights/src/Controller/DisplayController.php new file mode 100644 index 00000000..e7a89329 --- /dev/null +++ b/administrator/components/com_highlights/src/Controller/DisplayController.php @@ -0,0 +1,48 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\CMS\Router\Route; + +/** + * Highlights master display controller. + * + * @since 1.0.0 + */ +class DisplayController extends BaseController +{ + /** + * The default view. + * + * @var string + * @since 1.0.0 + */ + protected $default_view = 'highlights'; + + /** + * Method to display a view. + * + * @param boolean $cachable If true, the view output will be cached + * @param array $urlparams An array of safe URL parameters and their variable types, for valid values see {@link InputFilter::clean()}. + * + * @return BaseController|boolean This object to support chaining. + * + * @since 1.0.0 + */ + public function display($cachable = false, $urlparams = array()) + { + return parent::display(); + } +} diff --git a/administrator/components/com_highlights/src/Controller/EtichettaController.php b/administrator/components/com_highlights/src/Controller/EtichettaController.php new file mode 100644 index 00000000..bb80a586 --- /dev/null +++ b/administrator/components/com_highlights/src/Controller/EtichettaController.php @@ -0,0 +1,24 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\MVC\Controller\FormController; + +/** + * Etichetta controller class. + * + * @since 1.0.0 + */ +class EtichettaController extends FormController +{ + protected $view_list = 'etichette'; +} diff --git a/administrator/components/com_highlights/src/Controller/EtichetteController.php b/administrator/components/com_highlights/src/Controller/EtichetteController.php new file mode 100644 index 00000000..fd35d63a --- /dev/null +++ b/administrator/components/com_highlights/src/Controller/EtichetteController.php @@ -0,0 +1,116 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Application\SiteApplication; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Multilanguage; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; +use Joomla\Utilities\ArrayHelper; + +/** + * Etichette list controller class. + * + * @since 1.0.0 + */ +class EtichetteController extends AdminController +{ + /** + * Method to clone existing Etichette + * + * @return void + * + * @throws Exception + */ + public function duplicate() + { + // Check for request forgeries + $this->checkToken(); + + // Get id(s) + $pks = $this->input->post->get('cid', array(), 'array'); + + try + { + if (empty($pks)) + { + throw new \Exception(Text::_('COM_HIGHLIGHTS_NO_ELEMENT_SELECTED')); + } + + ArrayHelper::toInteger($pks); + $model = $this->getModel(); + $model->duplicate($pks); + $this->setMessage(Text::_('COM_HIGHLIGHTS_ITEMS_SUCCESS_DUPLICATED')); + } + catch (\Exception $e) + { + Factory::getApplication()->enqueueMessage($e->getMessage(), 'warning'); + } + + $this->setRedirect('index.php?option=com_highlights&view=etichette'); + } + + /** + * Proxy for getModel. + * + * @param string $name Optional. Model name + * @param string $prefix Optional. Class prefix + * @param array $config Optional. Configuration array for model + * + * @return object The Model + * + * @since 1.0.0 + */ + public function getModel($name = 'Etichetta', $prefix = 'Administrator', $config = array()) + { + return parent::getModel($name, $prefix, array('ignore_request' => true)); + } + + + + /** + * Method to save the submitted ordering values for records via AJAX. + * + * @return void + * + * @since 1.0.0 + * + * @throws Exception + */ + public function saveOrderAjax() + { + // Get the input + $pks = $this->input->post->get('cid', array(), 'array'); + $order = $this->input->post->get('order', array(), 'array'); + + // Sanitize the input + ArrayHelper::toInteger($pks); + ArrayHelper::toInteger($order); + + // Get the model + $model = $this->getModel(); + + // Save the ordering + $return = $model->saveorder($pks, $order); + + if ($return) + { + echo "1"; + } + + // Close the application + Factory::getApplication()->close(); + } +} diff --git a/administrator/components/com_highlights/src/Controller/HighlightController.php b/administrator/components/com_highlights/src/Controller/HighlightController.php new file mode 100644 index 00000000..31328f08 --- /dev/null +++ b/administrator/components/com_highlights/src/Controller/HighlightController.php @@ -0,0 +1,24 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\MVC\Controller\FormController; + +/** + * Highlight controller class. + * + * @since 1.0.0 + */ +class HighlightController extends FormController +{ + protected $view_list = 'highlights'; +} diff --git a/administrator/components/com_highlights/src/Controller/HighlightsController.php b/administrator/components/com_highlights/src/Controller/HighlightsController.php new file mode 100644 index 00000000..81a21e44 --- /dev/null +++ b/administrator/components/com_highlights/src/Controller/HighlightsController.php @@ -0,0 +1,116 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Application\SiteApplication; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Multilanguage; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; +use Joomla\Utilities\ArrayHelper; + +/** + * Highlights list controller class. + * + * @since 1.0.0 + */ +class HighlightsController extends AdminController +{ + /** + * Method to clone existing Highlights + * + * @return void + * + * @throws Exception + */ + public function duplicate() + { + // Check for request forgeries + $this->checkToken(); + + // Get id(s) + $pks = $this->input->post->get('cid', array(), 'array'); + + try + { + if (empty($pks)) + { + throw new \Exception(Text::_('COM_HIGHLIGHTS_NO_ELEMENT_SELECTED')); + } + + ArrayHelper::toInteger($pks); + $model = $this->getModel(); + $model->duplicate($pks); + $this->setMessage(Text::_('COM_HIGHLIGHTS_ITEMS_SUCCESS_DUPLICATED')); + } + catch (\Exception $e) + { + Factory::getApplication()->enqueueMessage($e->getMessage(), 'warning'); + } + + $this->setRedirect('index.php?option=com_highlights&view=highlights'); + } + + /** + * Proxy for getModel. + * + * @param string $name Optional. Model name + * @param string $prefix Optional. Class prefix + * @param array $config Optional. Configuration array for model + * + * @return object The Model + * + * @since 1.0.0 + */ + public function getModel($name = 'Highlight', $prefix = 'Administrator', $config = array()) + { + return parent::getModel($name, $prefix, array('ignore_request' => true)); + } + + + + /** + * Method to save the submitted ordering values for records via AJAX. + * + * @return void + * + * @since 1.0.0 + * + * @throws Exception + */ + public function saveOrderAjax() + { + // Get the input + $pks = $this->input->post->get('cid', array(), 'array'); + $order = $this->input->post->get('order', array(), 'array'); + + // Sanitize the input + ArrayHelper::toInteger($pks); + ArrayHelper::toInteger($order); + + // Get the model + $model = $this->getModel(); + + // Save the ordering + $return = $model->saveorder($pks, $order); + + if ($return) + { + echo "1"; + } + + // Close the application + Factory::getApplication()->close(); + } +} diff --git a/administrator/components/com_highlights/src/Extension/HighlightsComponent.php b/administrator/components/com_highlights/src/Extension/HighlightsComponent.php new file mode 100644 index 00000000..5da29850 --- /dev/null +++ b/administrator/components/com_highlights/src/Extension/HighlightsComponent.php @@ -0,0 +1,77 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Extension; + +defined('JPATH_PLATFORM') or die; + +use Pcrt\Component\Highlights\Administrator\Service\Html\HIGHLIGHTS; +use Joomla\CMS\Application\SiteApplication; +use Joomla\CMS\Association\AssociationServiceInterface; +use Joomla\CMS\Association\AssociationServiceTrait; +use Joomla\CMS\Categories\CategoryServiceTrait; +use Joomla\CMS\Component\Router\RouterServiceInterface; +use Joomla\CMS\Component\Router\RouterServiceTrait; +use Joomla\CMS\Extension\BootableExtensionInterface; +use Joomla\CMS\Extension\MVCComponent; +use Joomla\CMS\HTML\HTMLRegistryAwareTrait; +use Joomla\CMS\Tag\TagServiceTrait; +use Psr\Container\ContainerInterface; +use Joomla\CMS\Categories\CategoryServiceInterface; + +/** + * Component class for Highlights + * + * @since 1.0.0 + */ +class HighlightsComponent extends MVCComponent implements RouterServiceInterface, BootableExtensionInterface, CategoryServiceInterface +{ + use AssociationServiceTrait; + use RouterServiceTrait; + use HTMLRegistryAwareTrait; + use CategoryServiceTrait, TagServiceTrait { + CategoryServiceTrait::getTableNameForSection insteadof TagServiceTrait; + CategoryServiceTrait::getStateColumnForSection insteadof TagServiceTrait; + } + + /** @inheritdoc */ + public function boot(ContainerInterface $container) + { + $db = $container->get('DatabaseDriver'); + $this->getRegistry()->register('highlights', new HIGHLIGHTS($db)); + } + + +/** + * Returns the table for the count items functions for the given section. + * + * @param string The section + * + * * @return string|null + * + * @since 4.0.0 + */ + protected function getTableNameForSection(string $section = null) + { + } + + /** + * Adds Count Items for Category Manager. + * + * @param \stdClass[] $items The category objects + * @param string $section The section + * + * @return void + * + * @since 4.0.0 + */ + public function countItems(array $items, string $section) + { + } +} \ No newline at end of file diff --git a/administrator/components/com_highlights/src/Field/CreatedbyField.php b/administrator/components/com_highlights/src/Field/CreatedbyField.php new file mode 100644 index 00000000..8032a4d1 --- /dev/null +++ b/administrator/components/com_highlights/src/Field/CreatedbyField.php @@ -0,0 +1,67 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\User\UserFactoryInterface; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class CreatedbyField extends FormField +{ + /** + * The form field type. + * + * @var tring + * @since 1.0.0 + */ + protected $type = 'createdby'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + + // Load user + $user_id = $this->value; + + if ($user_id) + { + $container = \Joomla\CMS\Factory::getContainer(); + $userFactory = $container->get(UserFactoryInterface::class); + $user = $userFactory->loadUserById($user_id); + } + else + { + $user = Factory::getApplication()->getIdentity(); + $html[] = ''; + } + + if (!$this->hidden) + { + $html[] = "
" . $user->name . " (" . $user->username . ")
"; + } + + return implode($html); + } +} diff --git a/administrator/components/com_highlights/src/Field/ForeignkeyField.php b/administrator/components/com_highlights/src/Field/ForeignkeyField.php new file mode 100644 index 00000000..4d95115f --- /dev/null +++ b/administrator/components/com_highlights/src/Field/ForeignkeyField.php @@ -0,0 +1,297 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\Field\ListField; + +/** + * Supports a value from an external table + * + * @since 1.0.0 + */ +#[\AllowDynamicProperties] + class ForeignKeyField extends ListField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'foreignkey'; + + protected $layout = 'joomla.form.field.list-fancy-select'; + + /** + * The translate. + * + * @var boolean + * @since 1.0.0 + */ + protected $translate = true; + + protected $header = false; + + private $input_type; + + private $table; + + private $key_field; + + private $value_field; + + private $option_key_field; + + private $option_value_field; + + private $condition; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function processQuery() + { + // Type of input the field shows + $this->input_type = $this->getAttribute('input_type'); + + // Database Table + $this->table = $this->getAttribute('table'); + + // The field that the field will save on the database + $this->key_field = (string) $this->getAttribute('key_field'); + + // The column that the field shows in the input + $this->value_field = (string) $this->getAttribute('value_field'); + + // The option field that the field will save on the database + $this->option_key_field = (string) $this->getAttribute('option_key_field'); + + // The option value that the field shows in the input + $this->option_value_field = (string) $this->getAttribute('option_value_field'); + + // Flag to identify if the fk_value is multiple + $this->value_multiple = (int) $this->getAttribute('value_multiple', 0); + + $this->required = (string) $this->getAttribute('required', 0); + + // Flag to identify if the fk_value hides the trashed items + $this->hideTrashed = (int) $this->getAttribute('hide_trashed', 0); + + // Flag to identify if the fk_value hides the unpublished items + $this->hideUnpublished = (int) $this->getAttribute('hide_unpublished', 0); + + // Flag to identify if the fk_value hides the published items + $this->hidePublished = (int) $this->getAttribute('hide_published', 0); + + // Flag to identify if the fk_value hides the archived items + $this->hideArchived = (int) $this->getAttribute('hide_archived', 0); + + // Flag to identify if the fk has default order + $this->fk_ordering = (string) $this->getAttribute('fk_ordering'); + + // The where SQL for foreignkey + $this->condition = (string) $this->getAttribute('condition'); + + // Flag for translate options + $this->translate = (bool) $this->getAttribute('translate'); + + // Initialize variables. + $html = ''; + $fk_value = ''; + + // Load all the field options + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + // Support for multiple fields on fk_values + if ($this->value_multiple == 1) + { + // Get the fields for multiple value + $this->value_fields = (string) $this->getAttribute('value_field_multiple'); + $this->value_fields = explode(',', $this->value_fields); + $this->separator = (string) $this->getAttribute('separator'); + + $fk_value = ' CONCAT('; + + foreach ($this->value_fields as $field) + { + $fk_value .= $db->quoteName($field) . ', \'' . $this->separator . '\', '; + } + + $fk_value = substr($fk_value, 0, -(strlen($this->separator) + 6)); + $fk_value .= ') AS ' . $db->quoteName($this->value_field); + } + else + { + $fk_value = $db->quoteName($this->value_field); + } + + $query + ->select( + array( + $db->quoteName($this->key_field), + $fk_value + ) + ) + ->from($this->table); + + if ($this->hideTrashed) + { + $query->where($db->quoteName('state') . ' != -2'); + } + + if ($this->hideUnpublished) + { + $query->where($db->quoteName('state') . ' != 0'); + } + + if ($this->hidePublished) + { + $query->where($db->quoteName('state') . ' != 1'); + } + + if ($this->hideArchived) + { + $query->where($db->quoteName('state') . ' != 2'); + } + + if ($this->fk_ordering) + { + $query->order($this->fk_ordering); + } + + if($this->condition) + { + $query->where($this->condition); + } + + + + return $query; + } + + /** + * Method to get the field input for a foreignkey field. + * + * @return string The field input. + * + * @since 1.0.0 + */ + protected function getInput() + { + $data = $this->getLayoutData(); + + if (!\is_array($this->value) && !empty($this->value)) + { + if (\is_object($this->value)) + { + $this->value = get_object_vars($this->value); + } + + // String in format 2,5,4 + if (\is_string($this->value)) + { + $this->value = explode(',', $this->value); + } + + // Integer is given + if (\is_int($this->value)) + { + $this->value = array($this->value); + } + + $data['value'] = $this->value; + } + + $data['options'] = $this->getOptions(); + + return $this->getRenderer($this->layout)->render($data); + } + + /** + * Method to get the field options. + * + * @return array The field option objects. + * + * @since 1.0.0 + */ + protected function getOptions() + { + $options = array(); + $db = Factory::getContainer()->get('DatabaseDriver'); + try + { + $db->setQuery($this->processQuery()); + $results = $db->loadObjectList(); + } + catch (ExecutionFailureException $e) + { + Factory::getApplication()->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error'); + } + + // Add header. + if (!empty($this->header)) + { + $options[] = (object) ["value" => '', "text" => Text::_($this->header)]; + } + + if(!empty($this->option_value_field) || !empty($this->option_key_field)) + { + $options[] = (object) ["value" => $this->option_key_field, "text" => Text::_($this->option_value_field)]; + } + + // Build the field options. + if (!empty($results)) + { + foreach ($results as $item) + { + $options[] = (object) [ + "value" => $item->{$this->key_field}, + "text" => $this->translate == true ? Text::_($item->{$this->value_field}) : $item->{$this->value_field} + ]; + } + } + + // Merge any additional options in the XML definition. + $options = array_merge(parent::getOptions(), $options); + + return $options; + } + + /** + * Wrapper method for getting attributes from the form element + * + * @param string $attr_name Attribute name + * @param mixed $default Optional value to return if attribute not found + * + * @return mixed The value of the attribute if it exists, null otherwise + */ + public function getAttribute($attr_name, $default = null) + { + if (!empty($this->element[$attr_name])) + { + return $this->element[$attr_name]; + } + else + { + return $default; + } + } +} diff --git a/administrator/components/com_highlights/src/Field/ModifiedbyField.php b/administrator/components/com_highlights/src/Field/ModifiedbyField.php new file mode 100644 index 00000000..7b005263 --- /dev/null +++ b/administrator/components/com_highlights/src/Field/ModifiedbyField.php @@ -0,0 +1,52 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\Factory; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class ModifiedbyField extends \Joomla\CMS\Form\FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'modifiedby'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + $user = Factory::getApplication()->getIdentity(); + $html[] = ''; + if (!$this->hidden) + { + $html[] = "
" . $user->name . " (" . $user->username . ")
"; + } + + return implode($html); + } +} diff --git a/administrator/components/com_highlights/src/Field/NestedparentField.php b/administrator/components/com_highlights/src/Field/NestedparentField.php new file mode 100644 index 00000000..5e1008ec --- /dev/null +++ b/administrator/components/com_highlights/src/Field/NestedparentField.php @@ -0,0 +1,83 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Field; + +defined('JPATH_BASE') or die; + +use Joomla\CMS\Helper\UserGroupsHelper; +use \Joomla\CMS\Factory; +use Joomla\CMS\Form\Field\ListField; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class NestedparentField extends ListField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'nestedparent'; + + /** + * Method to get the field options. + * + * @return array The field option objects. + * + * @since 1.0.0 + */ + protected function getOptions() + { + $options = array(); + $table = $this->getAttribute('table'); + + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true) + ->select('DISTINCT(a.id) AS value, a.title AS text, a.level, a.lft') + ->from($table . ' AS a'); + + + // Prevent parenting to children of this item. + if ($id = $this->form->getValue('id')) + { + $query->join('LEFT', $db->quoteName($table) . ' AS p ON p.id = ' . (int) $id) + ->where('NOT(a.lft >= p.lft AND a.rgt <= p.rgt)'); + } + + $query->order('a.lft ASC'); + + // Get the options. + $db->setQuery($query); + + try + { + $options = $db->loadObjectList(); + } + catch (\RuntimeException $e) + { + throw new \Exception($e->getMessage(), 500); + } + + // Pad the option text with spaces using depth level as a multiplier. + for ($i = 0, $n = count($options); $i < $n; $i++) + { + $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->text; + } + + // Merge any additional options in the XML definition. + $options = array_merge(parent::getOptions(), $options); + + return $options; + } +} diff --git a/administrator/components/com_highlights/src/Field/TimecreatedField.php b/administrator/components/com_highlights/src/Field/TimecreatedField.php new file mode 100644 index 00000000..2fe6ab0c --- /dev/null +++ b/administrator/components/com_highlights/src/Field/TimecreatedField.php @@ -0,0 +1,65 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\Date\Date; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class TimecreatedField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'timecreated'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + + $time_created = $this->value; + + if (!strtotime($time_created)) + { + $time_created = Factory::getDate('now', Factory::getConfig()->get('offset'))->toSql(true); + $html[] = ''; + } + + $hidden = (boolean) $this->element['hidden']; + + if ($hidden == null || !$hidden) + { + $jdate = new Date($time_created); + $pretty_date = $jdate->format(Text::_('DATE_FORMAT_LC2')); + $html[] = "
" . $pretty_date . "
"; + } + + return implode($html); + } +} diff --git a/administrator/components/com_highlights/src/Field/TimeupdatedField.php b/administrator/components/com_highlights/src/Field/TimeupdatedField.php new file mode 100644 index 00000000..4ca1c272 --- /dev/null +++ b/administrator/components/com_highlights/src/Field/TimeupdatedField.php @@ -0,0 +1,68 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Date\Date; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class TimeupdatedField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'timeupdated'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + + $old_time_updated = $this->value; + $hidden = (boolean) $this->element['hidden']; + + if ($hidden == null || !$hidden) + { + if (!strtotime($old_time_updated)) + { + $html[] = '-'; + } + else + { + $jdate = new Date($old_time_updated); + $pretty_date = $jdate->format(Text::_('DATE_FORMAT_LC2')); + $html[] = "
" . $pretty_date . "
"; + } + } + + $time_updated = Factory::getDate('now', Factory::getConfig()->get('offset'))->toSql(true); + $html[] = ''; + + return implode($html); + } +} diff --git a/administrator/components/com_highlights/src/Field/index.html b/administrator/components/com_highlights/src/Field/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/administrator/components/com_highlights/src/Field/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/administrator/components/com_highlights/src/Helper/HighlightsHelper.php b/administrator/components/com_highlights/src/Helper/HighlightsHelper.php new file mode 100644 index 00000000..c294fc42 --- /dev/null +++ b/administrator/components/com_highlights/src/Helper/HighlightsHelper.php @@ -0,0 +1,77 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Helper; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Object\CMSObject; + +/** + * Highlights helper. + * + * @since 1.0.0 + */ +class HighlightsHelper +{ + /** + * Gets the files attached to an item + * + * @param int $pk The item's id + * + * @param string $table The table's name + * + * @param string $field The field's name + * + * @return array The files + */ + public static function getFiles($pk, $table, $field) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + $query + ->select($field) + ->from($table) + ->where('id = ' . (int) $pk); + + $db->setQuery($query); + + return explode(',', $db->loadResult()); + } + + /** + * Gets a list of the actions that can be performed. + * + * @return CMSObject + * + * @since 1.0.0 + */ + public static function getActions() + { + $user = Factory::getApplication()->getIdentity(); + $result = new CMSObject; + + $assetName = 'com_highlights'; + + $actions = array( + 'core.admin', 'core.manage', 'core.create', 'core.edit', 'core.edit.own', 'core.edit.state', 'core.delete' + ); + + foreach ($actions as $action) + { + $result->set($action, $user->authorise($action, $assetName)); + } + + return $result; + } +} + diff --git a/administrator/components/com_highlights/src/Model/EtichettaModel.php b/administrator/components/com_highlights/src/Model/EtichettaModel.php new file mode 100644 index 00000000..0bc123bd --- /dev/null +++ b/administrator/components/com_highlights/src/Model/EtichettaModel.php @@ -0,0 +1,277 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\Table\Table; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Plugin\PluginHelper; +use \Joomla\CMS\MVC\Model\AdminModel; +use \Joomla\CMS\Helper\TagsHelper; +use \Joomla\CMS\Filter\OutputFilter; +use \Joomla\CMS\Event\Model; +use Joomla\CMS\Event\AbstractEvent; + + +/** + * Etichetta model. + * + * @since 1.0.0 + */ +class EtichettaModel extends AdminModel +{ + /** + * @var string The prefix to use with controller messages. + * + * @since 1.0.0 + */ + protected $text_prefix = 'COM_HIGHLIGHTS'; + + /** + * @var string Alias to manage history control + * + * @since 1.0.0 + */ + public $typeAlias = 'com_highlights.etichetta'; + + /** + * @var null Item data + * + * @since 1.0.0 + */ + protected $item = null; + + + + + /** + * Returns a reference to the a Table object, always creating it. + * + * @param string $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return Table A database object + * + * @since 1.0.0 + */ + public function getTable($type = 'Etichetta', $prefix = 'Administrator', $config = array()) + { + return parent::getTable($type, $prefix, $config); + } + + /** + * Method to get the record form. + * + * @param array $data An optional array of data for the form to interogate. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * + * @return \JForm|boolean A \JForm object on success, false on failure + * + * @since 1.0.0 + */ + public function getForm($data = array(), $loadData = true) + { + // Initialise variables. + $app = Factory::getApplication(); + + // Get the form. + $form = $this->loadForm( + 'com_highlights.etichetta', + 'etichetta', + array( + 'control' => 'jform', + 'load_data' => $loadData + ) + ); + + + + if (empty($form)) + { + return false; + } + + return $form; + } + + + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.0.0 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = Factory::getApplication()->getUserState('com_highlights.edit.etichetta.data', array()); + + if (empty($data)) + { + if ($this->item === null) + { + $this->item = $this->getItem(); + } + + $data = $this->item; + + } + + return $data; + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.0.0 + */ + public function getItem($pk = null) + { + + if ($item = parent::getItem($pk)) + { + if (isset($item->params)) + { + $item->params = json_encode($item->params); + } + + // Do any procesing on fields here if needed + } + + return $item; + + } + + /** + * Method to duplicate an Etichetta + * + * @param array &$pks An array of primary key IDs. + * + * @return boolean True if successful. + * + * @throws Exception + */ + public function duplicate(&$pks) + { + $app = Factory::getApplication(); + $user = $app->getIdentity(); + $dispatcher = $this->getDispatcher(); + + // Access checks. + if (!$user->authorise('core.create', 'com_highlights')) + { + throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED')); + } + + $context = $this->option . '.' . $this->name; + + // Include the plugins for the save events. + PluginHelper::importPlugin($this->events_map['save']); + + $table = $this->getTable(); + + foreach ($pks as $pk) + { + + if ($table->load($pk, true)) + { + // Reset the id to create a new record. + $table->id = 0; + + if (!$table->check()) + { + throw new \Exception($table->getError()); + } + + + // Create the before save event. + $beforeSaveEvent = AbstractEvent::create( + $this->event_before_save, + [ + 'context' => $context, + 'subject' => $table, + 'isNew' => true, + 'data' => $table, + ] + ); + + // Trigger the before save event. + $dispatchResult = Factory::getApplication()->getDispatcher()->dispatch($this->event_before_save, $beforeSaveEvent); + + // Check if dispatch result is an array and handle accordingly + $result = isset($dispatchResult['result']) ? $dispatchResult['result'] : []; + + // Proceed with your logic + if (in_array(false, $result, true) || !$table->store()) { + throw new \Exception($table->getError()); + } + + // Trigger the after save event. + Factory::getApplication()->getDispatcher()->dispatch( + $this->event_after_save, + AbstractEvent::create( + $this->event_after_save, + [ + 'context' => $context, + 'subject' => $table, + 'isNew' => true, + 'data' => $table, + ] + ) + ); + } + else + { + throw new \Exception($table->getError()); + } + + } + + // Clean cache + $this->cleanCache(); + + return true; + } + + /** + * Prepare and sanitise the table prior to saving. + * + * @param Table $table Table Object + * + * @return void + * + * @since 1.0.0 + */ + protected function prepareTable($table) + { + jimport('joomla.filter.output'); + + if (empty($table->id)) + { + // Set ordering to the last item if not set + if (@$table->ordering === '') + { + $db = $this->getDbo(); + $db->setQuery('SELECT MAX(ordering) FROM #__highlights_etichetta'); + $max = $db->loadResult(); + $table->ordering = $max + 1; + } + } + } +} diff --git a/administrator/components/com_highlights/src/Model/EtichetteModel.php b/administrator/components/com_highlights/src/Model/EtichetteModel.php new file mode 100644 index 00000000..ab0d2482 --- /dev/null +++ b/administrator/components/com_highlights/src/Model/EtichetteModel.php @@ -0,0 +1,206 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\MVC\Model\ListModel; +use \Joomla\Component\Fields\Administrator\Helper\FieldsHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Helper\TagsHelper; +use \Joomla\Database\ParameterType; +use \Joomla\Utilities\ArrayHelper; +use Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; + +/** + * Methods supporting a list of Etichette records. + * + * @since 1.0.0 + */ +class EtichetteModel extends ListModel +{ + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * + * @see JController + * @since 1.6 + */ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'id', 'a.id', + 'state', 'a.state', + 'ordering', 'a.ordering', + 'created_by', 'a.created_by', + 'modified_by', 'a.modified_by', + 'nome', 'a.nome', + 'lingua', 'a.lingua', + ); + } + + parent::__construct($config); + } + + + + + + + + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering Elements order + * @param string $direction Order direction + * + * @return void + * + * @throws Exception + */ + protected function populateState($ordering = null, $direction = null) + { + // List state information. + parent::populateState("a.id", "ASC"); + + $context = $this->getUserStateFromRequest($this->context.'.filter.search', 'filter_search'); + $this->setState('filter.search', $context); + + // Split context into component and optional section + if (!empty($context)) + { + $parts = FieldsHelper::extract($context); + + if ($parts) + { + $this->setState('filter.component', $parts[0]); + $this->setState('filter.section', $parts[1]); + } + } + } + + /** + * Method to get a store id based on model configuration state. + * + * This is necessary because the model is used by the component and + * different modules that might need different sets of data or different + * ordering requirements. + * + * @param string $id A prefix for the store id. + * + * @return string A store id. + * + * @since 1.0.0 + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.state'); + + + return parent::getStoreId($id); + + } + + /** + * Build an SQL query to load the list data. + * + * @return DatabaseQuery + * + * @since 1.0.0 + */ + protected function getListQuery() + { + // Create a new query object. + $db = $this->getDbo(); + $query = $db->getQuery(true); + + // Select the required fields from the table. + $query->select( + $this->getState( + 'list.select', 'DISTINCT a.*' + ) + ); + $query->from('`#__highlights_etichetta` AS a'); + + // Join over the users for the checked out user + $query->select("uc.name AS uEditor"); + $query->join("LEFT", "#__users AS uc ON uc.id=a.checked_out"); + + // Join over the user field 'created_by' + $query->select('`created_by`.name AS `created_by`'); + $query->join('LEFT', '#__users AS `created_by` ON `created_by`.id = a.`created_by`'); + + // Join over the user field 'modified_by' + $query->select('`modified_by`.name AS `modified_by`'); + $query->join('LEFT', '#__users AS `modified_by` ON `modified_by`.id = a.`modified_by`'); + + + // Filter by published state + $published = $this->getState('filter.state'); + + if (is_numeric($published)) + { + $query->where('a.state = ' . (int) $published); + } + elseif (empty($published)) + { + $query->where('(a.state IN (0, 1))'); + } + + // Filter by search in title + $search = $this->getState('filter.search'); + + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->Quote('%' . $db->escape($search, true) . '%'); + + } + } + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', "a.id"); + $orderDirn = $this->state->get('list.direction', "ASC"); + + if ($orderCol && $orderDirn) + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Get an array of data items + * + * @return mixed Array of data items on success, false on failure. + */ + public function getItems() + { + $items = parent::getItems(); + + + return $items; + } +} diff --git a/administrator/components/com_highlights/src/Model/HighlightModel.php b/administrator/components/com_highlights/src/Model/HighlightModel.php new file mode 100644 index 00000000..75f6a472 --- /dev/null +++ b/administrator/components/com_highlights/src/Model/HighlightModel.php @@ -0,0 +1,289 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\Table\Table; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Plugin\PluginHelper; +use \Joomla\CMS\MVC\Model\AdminModel; +use \Joomla\CMS\Helper\TagsHelper; +use \Joomla\CMS\Filter\OutputFilter; +use \Joomla\CMS\Event\Model; +use Joomla\CMS\Event\AbstractEvent; + + +/** + * Highlight model. + * + * @since 1.0.0 + */ +class HighlightModel extends AdminModel +{ + /** + * @var string The prefix to use with controller messages. + * + * @since 1.0.0 + */ + protected $text_prefix = 'COM_HIGHLIGHTS'; + + /** + * @var string Alias to manage history control + * + * @since 1.0.0 + */ + public $typeAlias = 'com_highlights.highlight'; + + /** + * @var null Item data + * + * @since 1.0.0 + */ + protected $item = null; + + + + + /** + * Returns a reference to the a Table object, always creating it. + * + * @param string $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return Table A database object + * + * @since 1.0.0 + */ + public function getTable($type = 'Highlight', $prefix = 'Administrator', $config = array()) + { + return parent::getTable($type, $prefix, $config); + } + + /** + * Method to get the record form. + * + * @param array $data An optional array of data for the form to interogate. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * + * @return \JForm|boolean A \JForm object on success, false on failure + * + * @since 1.0.0 + */ + public function getForm($data = array(), $loadData = true) + { + // Initialise variables. + $app = Factory::getApplication(); + + // Get the form. + $form = $this->loadForm( + 'com_highlights.highlight', + 'highlight', + array( + 'control' => 'jform', + 'load_data' => $loadData + ) + ); + + + + if (empty($form)) + { + return false; + } + + return $form; + } + + + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.0.0 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = Factory::getApplication()->getUserState('com_highlights.edit.highlight.data', array()); + + if (empty($data)) + { + if ($this->item === null) + { + $this->item = $this->getItem(); + } + + $data = $this->item; + + } + + return $data; + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.0.0 + */ + public function getItem($pk = null) + { + + if ($item = parent::getItem($pk)) + { + if (isset($item->params)) + { + $item->params = json_encode($item->params); + } + + // Do any procesing on fields here if needed + } + + return $item; + + } + + /** + * Method to duplicate an Highlight + * + * @param array &$pks An array of primary key IDs. + * + * @return boolean True if successful. + * + * @throws Exception + */ + public function duplicate(&$pks) + { + $app = Factory::getApplication(); + $user = $app->getIdentity(); + $dispatcher = $this->getDispatcher(); + + // Access checks. + if (!$user->authorise('core.create', 'com_highlights')) + { + throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED')); + } + + $context = $this->option . '.' . $this->name; + + // Include the plugins for the save events. + PluginHelper::importPlugin($this->events_map['save']); + + $table = $this->getTable(); + + foreach ($pks as $pk) + { + + if ($table->load($pk, true)) + { + // Reset the id to create a new record. + $table->id = 0; + + if (!$table->check()) + { + throw new \Exception($table->getError()); + } + + if (!empty($table->etichetta)) + { + if (is_array($table->etichetta)) + { + $table->etichetta = implode(',', $table->etichetta); + } + } + else + { + $table->etichetta = ''; + } + + + // Create the before save event. + $beforeSaveEvent = AbstractEvent::create( + $this->event_before_save, + [ + 'context' => $context, + 'subject' => $table, + 'isNew' => true, + 'data' => $table, + ] + ); + + // Trigger the before save event. + $dispatchResult = Factory::getApplication()->getDispatcher()->dispatch($this->event_before_save, $beforeSaveEvent); + + // Check if dispatch result is an array and handle accordingly + $result = isset($dispatchResult['result']) ? $dispatchResult['result'] : []; + + // Proceed with your logic + if (in_array(false, $result, true) || !$table->store()) { + throw new \Exception($table->getError()); + } + + // Trigger the after save event. + Factory::getApplication()->getDispatcher()->dispatch( + $this->event_after_save, + AbstractEvent::create( + $this->event_after_save, + [ + 'context' => $context, + 'subject' => $table, + 'isNew' => true, + 'data' => $table, + ] + ) + ); + } + else + { + throw new \Exception($table->getError()); + } + + } + + // Clean cache + $this->cleanCache(); + + return true; + } + + /** + * Prepare and sanitise the table prior to saving. + * + * @param Table $table Table Object + * + * @return void + * + * @since 1.0.0 + */ + protected function prepareTable($table) + { + jimport('joomla.filter.output'); + + if (empty($table->id)) + { + // Set ordering to the last item if not set + if (@$table->ordering === '') + { + $db = $this->getDbo(); + $db->setQuery('SELECT MAX(ordering) FROM #__highlights_'); + $max = $db->loadResult(); + $table->ordering = $max + 1; + } + } + } +} diff --git a/administrator/components/com_highlights/src/Model/HighlightsModel.php b/administrator/components/com_highlights/src/Model/HighlightsModel.php new file mode 100644 index 00000000..82f14885 --- /dev/null +++ b/administrator/components/com_highlights/src/Model/HighlightsModel.php @@ -0,0 +1,248 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\MVC\Model\ListModel; +use \Joomla\Component\Fields\Administrator\Helper\FieldsHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Helper\TagsHelper; +use \Joomla\Database\ParameterType; +use \Joomla\Utilities\ArrayHelper; +use Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; + +/** + * Methods supporting a list of Highlights records. + * + * @since 1.0.0 + */ +class HighlightsModel extends ListModel +{ + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * + * @see JController + * @since 1.6 + */ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'id', 'a.id', + 'state', 'a.state', + 'ordering', 'a.ordering', + 'created_by', 'a.created_by', + 'modified_by', 'a.modified_by', + 'etichetta', 'a.etichetta', + 'titolo', 'a.titolo', + 'sottotitolo', 'a.sottotitolo', + 'descrizione', 'a.descrizione', + 'lingua', 'a.lingua', + 'link_pulsante', 'a.link_pulsante', + 'testo_pulsante', 'a.testo_pulsante', + 'data', 'a.data', + 'immagine_main', 'a.immagine_main', + 'immagine_secondaria', 'a.immagine_secondaria', + 'data_inizio_pubblicazione', 'a.data_inizio_pubblicazione', + 'data_fine_pubblicazione', 'a.data_fine_pubblicazione', + ); + } + + parent::__construct($config); + } + + + + + + + + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering Elements order + * @param string $direction Order direction + * + * @return void + * + * @throws Exception + */ + protected function populateState($ordering = null, $direction = null) + { + // List state information. + parent::populateState('id', 'ASC'); + + $context = $this->getUserStateFromRequest($this->context.'.filter.search', 'filter_search'); + $this->setState('filter.search', $context); + + // Split context into component and optional section + if (!empty($context)) + { + $parts = FieldsHelper::extract($context); + + if ($parts) + { + $this->setState('filter.component', $parts[0]); + $this->setState('filter.section', $parts[1]); + } + } + } + + /** + * Method to get a store id based on model configuration state. + * + * This is necessary because the model is used by the component and + * different modules that might need different sets of data or different + * ordering requirements. + * + * @param string $id A prefix for the store id. + * + * @return string A store id. + * + * @since 1.0.0 + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.state'); + + + return parent::getStoreId($id); + + } + + /** + * Build an SQL query to load the list data. + * + * @return DatabaseQuery + * + * @since 1.0.0 + */ + protected function getListQuery() + { + // Create a new query object. + $db = $this->getDbo(); + $query = $db->getQuery(true); + + // Select the required fields from the table. + $query->select( + $this->getState( + 'list.select', 'DISTINCT a.*' + ) + ); + $query->from('`#__highlights_` AS a'); + + // Join over the users for the checked out user + $query->select("uc.name AS uEditor"); + $query->join("LEFT", "#__users AS uc ON uc.id=a.checked_out"); + + // Join over the user field 'created_by' + $query->select('`created_by`.name AS `created_by`'); + $query->join('LEFT', '#__users AS `created_by` ON `created_by`.id = a.`created_by`'); + + // Join over the user field 'modified_by' + $query->select('`modified_by`.name AS `modified_by`'); + $query->join('LEFT', '#__users AS `modified_by` ON `modified_by`.id = a.`modified_by`'); + // Join over the foreign key 'etichetta' + $query->select('`#__highlights_etichetta_4129035`.`nome` AS etichette_fk_value_4129035'); + $query->join('LEFT', '#__highlights_etichetta AS #__highlights_etichetta_4129035 ON #__highlights_etichetta_4129035.`nome` = a.`etichetta`'); + + + // Filter by published state + $published = $this->getState('filter.state'); + + if (is_numeric($published)) + { + $query->where('a.state = ' . (int) $published); + } + elseif (empty($published)) + { + $query->where('(a.state IN (0, 1))'); + } + + // Filter by search in title + $search = $this->getState('filter.search'); + + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->Quote('%' . $db->escape($search, true) . '%'); + + } + } + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'id'); + $orderDirn = $this->state->get('list.direction', 'ASC'); + + if ($orderCol && $orderDirn) + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Get an array of data items + * + * @return mixed Array of data items on success, false on failure. + */ + public function getItems() + { + $items = parent::getItems(); + + foreach ($items as $oneItem) + { + + if (isset($oneItem->etichetta)) + { + $values = explode(',', $oneItem->etichetta); + $textValue = array(); + + foreach ($values as $value) + { + $db = $this->getDbo(); + $query = $db->getQuery(true); + $query + ->select('`#__highlights_etichetta_4129035`.`nome`') + ->from($db->quoteName('#__highlights_etichetta', '#__highlights_etichetta_4129035')) + ->where($db->quoteName('#__highlights_etichetta_4129035.nome') . ' = '. $db->quote($db->escape($value))); + + $db->setQuery($query); + $results = $db->loadObject(); + + if ($results) + { + $textValue[] = $results->nome; + } + } + + $oneItem->etichetta = !empty($textValue) ? implode(', ', $textValue) : $oneItem->etichetta; + } + } + + return $items; + } +} diff --git a/administrator/components/com_highlights/src/Service/Html/HIGHLIGHTS.php b/administrator/components/com_highlights/src/Service/Html/HIGHLIGHTS.php new file mode 100644 index 00000000..afaa17ac --- /dev/null +++ b/administrator/components/com_highlights/src/Service/Html/HIGHLIGHTS.php @@ -0,0 +1,53 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Service\Html; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\Utilities\ArrayHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Model\DatabaseAwareTrait; +use Joomla\Database\DatabaseDriver; + +/** + * Highlights HTML Helper. + * + * @since 1.0.0 + */ +class HIGHLIGHTS +{ + use DatabaseAwareTrait; + + /** + * Public constructor. + * + * @param DatabaseDriver $db The Joomla DB driver object for the site's database. + */ + public function __construct(DatabaseDriver $db) + { + $this->setDbo($db); + } + + public function toggle($value = 0, $view='', $field='', $i='') + { + $states = array( + 0 => array('icon-unpublish', Text::_('Toggle'), ''), + 1 => array('icon-publish', Text::_('Toggle'), '') + ); + + $state = ArrayHelper::getValue($states, (int) $value, $states[0]); + $text = ''; + $html = '' . $text . ''; + + return $html; + } +} diff --git a/administrator/components/com_highlights/src/Table/EtichettaTable.php b/administrator/components/com_highlights/src/Table/EtichettaTable.php new file mode 100644 index 00000000..820282dc --- /dev/null +++ b/administrator/components/com_highlights/src/Table/EtichettaTable.php @@ -0,0 +1,279 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Table; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\Utilities\ArrayHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Access\Access; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Table\Table as Table; +use \Joomla\CMS\Versioning\VersionableTableInterface; +use Joomla\CMS\Tag\TaggableTableInterface; +use Joomla\CMS\Tag\TaggableTableTrait; +use \Joomla\Database\DatabaseDriver; +use \Joomla\CMS\Filter\OutputFilter; +use \Joomla\CMS\Filesystem\File; +use \Joomla\Registry\Registry; +use \Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; +use \Joomla\CMS\Helper\ContentHelper; + + +/** + * Etichetta table + * + * @since 1.0.0 + */ +class EtichettaTable extends Table implements VersionableTableInterface, TaggableTableInterface +{ + use TaggableTableTrait; + + /** + * Indicates that columns fully support the NULL value in the database + * + * @var boolean + * @since 4.0.0 + */ + protected $_supportNullValue = true; + + + /** + * Constructor + * + * @param JDatabase &$db A database connector object + */ + public function __construct(DatabaseDriver $db) + { + $this->typeAlias = 'com_highlights.etichetta'; + parent::__construct('#__highlights_etichetta', 'id', $db); + $this->setColumnAlias('published', 'state'); + + } + + /** + * Get the type alias for the history table + * + * @return string The alias as described above + * + * @since 1.0.0 + */ + public function getTypeAlias() + { + return $this->typeAlias; + } + + /** + * Overloaded bind function to pre-process the params. + * + * @param array $array Named array + * @param mixed $ignore Optional array or list of parameters to ignore + * + * @return boolean True on success. + * + * @see Table:bind + * @since 1.0.0 + * @throws \InvalidArgumentException + */ + public function bind($array, $ignore = '') + { + $date = Factory::getDate(); + $task = Factory::getApplication()->input->get('task'); + $user = Factory::getApplication()->getIdentity(); + + $input = Factory::getApplication()->input; + $task = $input->getString('task', ''); + + if ($array['id'] == 0 && empty($array['created_by'])) + { + $array['created_by'] = Factory::getUser()->id; + } + + if ($array['id'] == 0 && empty($array['modified_by'])) + { + $array['modified_by'] = Factory::getUser()->id; + } + + if ($task == 'apply' || $task == 'save') + { + $array['modified_by'] = Factory::getUser()->id; + } + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new Registry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new Registry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + if (!$user->authorise('core.admin', 'com_highlights.etichetta.' . $array['id'])) + { + $actions = Access::getActionsFromFile( + JPATH_ADMINISTRATOR . '/components/com_highlights/access.xml', + "/access/section[@name='etichetta']/" + ); + $default_actions = Access::getAssetRules('com_highlights.etichetta.' . $array['id'])->getData(); + $array_jaccess = array(); + + foreach ($actions as $action) + { + if (key_exists($action->name, $default_actions)) + { + $array_jaccess[$action->name] = $default_actions[$action->name]; + } + } + + $array['rules'] = $this->JAccessRulestoArray($array_jaccess); + } + + // Bind the rules for ACL where supported. + if (isset($array['rules']) && is_array($array['rules'])) + { + $this->setRules($array['rules']); + } + + return parent::bind($array, $ignore); + } + + /** + * Method to store a row in the database from the Table instance properties. + * + * If a primary key value is set the row with that primary key value will be updated with the instance property values. + * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. + * + * @param boolean $updateNulls True to update fields even if they are null. + * + * @return boolean True on success. + * + * @since 1.0.0 + */ + public function store($updateNulls = true) + { + + + return parent::store($updateNulls); + } + + /** + * This function convert an array of Access objects into an rules array. + * + * @param array $jaccessrules An array of Access objects. + * + * @return array + */ + private function JAccessRulestoArray($jaccessrules) + { + $rules = array(); + + foreach ($jaccessrules as $action => $jaccess) + { + $actions = array(); + + if ($jaccess) + { + foreach ($jaccess->getData() as $group => $allow) + { + $actions[$group] = ((bool)$allow); + } + } + + $rules[$action] = $actions; + } + + return $rules; + } + + /** + * Overloaded check function + * + * @return bool + */ + public function check() + { + // If there is an ordering column and this is a new row then get the next ordering value + if (property_exists($this, 'ordering') && $this->id == 0) + { + $this->ordering = self::getNextOrder(); + } + + + + return parent::check(); + } + + /** + * Define a namespaced asset name for inclusion in the #__assets table + * + * @return string The asset name + * + * @see Table::_getAssetName + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + + return $this->typeAlias . '.' . (int) $this->$k; + } + + /** + * Returns the parent asset's id. If you have a tree structure, retrieve the parent's id using the external key field + * + * @param Table $table Table name + * @param integer $id Id + * + * @see Table::_getAssetParentId + * + * @return mixed The id on success, false on failure. + */ + protected function _getAssetParentId($table = null, $id = null) + { + // We will retrieve the parent-asset from the Asset-table + $assetParent = Table::getInstance('Asset'); + + // Default: if no asset-parent can be found we take the global asset + $assetParentId = $assetParent->getRootId(); + + // The item has the component as asset-parent + $assetParent->loadByName('com_highlights'); + + // Return the found asset-parent-id + if ($assetParent->id) + { + $assetParentId = $assetParent->id; + } + + return $assetParentId; + } + + //XXX_CUSTOM_TABLE_FUNCTION + + + /** + * Delete a record by id + * + * @param mixed $pk Primary key value to delete. Optional + * + * @return bool + */ + public function delete($pk = null) + { + $this->load($pk); + $result = parent::delete($pk); + + return $result; + } +} diff --git a/administrator/components/com_highlights/src/Table/HighlightTable.php b/administrator/components/com_highlights/src/Table/HighlightTable.php new file mode 100644 index 00000000..f2e44cd8 --- /dev/null +++ b/administrator/components/com_highlights/src/Table/HighlightTable.php @@ -0,0 +1,314 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\Table; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\Utilities\ArrayHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Access\Access; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Table\Table as Table; +use \Joomla\CMS\Versioning\VersionableTableInterface; +use Joomla\CMS\Tag\TaggableTableInterface; +use Joomla\CMS\Tag\TaggableTableTrait; +use \Joomla\Database\DatabaseDriver; +use \Joomla\CMS\Filter\OutputFilter; +use \Joomla\CMS\Filesystem\File; +use \Joomla\Registry\Registry; +use \Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; +use \Joomla\CMS\Helper\ContentHelper; + + +/** + * Highlight table + * + * @since 1.0.0 + */ +class HighlightTable extends Table implements VersionableTableInterface, TaggableTableInterface +{ + use TaggableTableTrait; + + /** + * Indicates that columns fully support the NULL value in the database + * + * @var boolean + * @since 4.0.0 + */ + protected $_supportNullValue = true; + + + /** + * Constructor + * + * @param JDatabase &$db A database connector object + */ + public function __construct(DatabaseDriver $db) + { + $this->typeAlias = 'com_highlights.highlight'; + parent::__construct('#__highlights_', 'id', $db); + $this->setColumnAlias('published', 'state'); + + } + + /** + * Get the type alias for the history table + * + * @return string The alias as described above + * + * @since 1.0.0 + */ + public function getTypeAlias() + { + return $this->typeAlias; + } + + /** + * Overloaded bind function to pre-process the params. + * + * @param array $array Named array + * @param mixed $ignore Optional array or list of parameters to ignore + * + * @return boolean True on success. + * + * @see Table:bind + * @since 1.0.0 + * @throws \InvalidArgumentException + */ + public function bind($array, $ignore = '') + { + $date = Factory::getDate(); + $task = Factory::getApplication()->input->get('task'); + $user = Factory::getApplication()->getIdentity(); + + $input = Factory::getApplication()->input; + $task = $input->getString('task', ''); + + if ($array['id'] == 0 && empty($array['created_by'])) + { + $array['created_by'] = Factory::getUser()->id; + } + + if ($array['id'] == 0 && empty($array['modified_by'])) + { + $array['modified_by'] = Factory::getUser()->id; + } + + if ($task == 'apply' || $task == 'save') + { + $array['modified_by'] = Factory::getUser()->id; + } + + // Support for multiple or not foreign key field: etichetta + if(!empty($array['etichetta'])) + { + if(is_array($array['etichetta'])){ + $array['etichetta'] = implode(',',$array['etichetta']); + } + else if(strrpos($array['etichetta'], ',') != false){ + $array['etichetta'] = explode(',',$array['etichetta']); + } + } + else { + $array['etichetta'] = 0; + } + + // Support for empty date field: data + if($array['data'] == '0000-00-00' || empty($array['data'])) + { + $array['data'] = NULL; + $this->data = NULL; + } + + // Support for empty date field: data_inizio_pubblicazione + if($array['data_inizio_pubblicazione'] == '0000-00-00' || empty($array['data_inizio_pubblicazione'])) + { + $array['data_inizio_pubblicazione'] = NULL; + $this->data_inizio_pubblicazione = NULL; + } + + // Support for empty date field: data_fine_pubblicazione + if($array['data_fine_pubblicazione'] == '0000-00-00' || empty($array['data_fine_pubblicazione'])) + { + $array['data_fine_pubblicazione'] = NULL; + $this->data_fine_pubblicazione = NULL; + } + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new Registry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new Registry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + if (!$user->authorise('core.admin', 'com_highlights.highlight.' . $array['id'])) + { + $actions = Access::getActionsFromFile( + JPATH_ADMINISTRATOR . '/components/com_highlights/access.xml', + "/access/section[@name='highlight']/" + ); + $default_actions = Access::getAssetRules('com_highlights.highlight.' . $array['id'])->getData(); + $array_jaccess = array(); + + foreach ($actions as $action) + { + if (key_exists($action->name, $default_actions)) + { + $array_jaccess[$action->name] = $default_actions[$action->name]; + } + } + + $array['rules'] = $this->JAccessRulestoArray($array_jaccess); + } + + // Bind the rules for ACL where supported. + if (isset($array['rules']) && is_array($array['rules'])) + { + $this->setRules($array['rules']); + } + + return parent::bind($array, $ignore); + } + + /** + * Method to store a row in the database from the Table instance properties. + * + * If a primary key value is set the row with that primary key value will be updated with the instance property values. + * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. + * + * @param boolean $updateNulls True to update fields even if they are null. + * + * @return boolean True on success. + * + * @since 1.0.0 + */ + public function store($updateNulls = true) + { + + + return parent::store($updateNulls); + } + + /** + * This function convert an array of Access objects into an rules array. + * + * @param array $jaccessrules An array of Access objects. + * + * @return array + */ + private function JAccessRulestoArray($jaccessrules) + { + $rules = array(); + + foreach ($jaccessrules as $action => $jaccess) + { + $actions = array(); + + if ($jaccess) + { + foreach ($jaccess->getData() as $group => $allow) + { + $actions[$group] = ((bool)$allow); + } + } + + $rules[$action] = $actions; + } + + return $rules; + } + + /** + * Overloaded check function + * + * @return bool + */ + public function check() + { + // If there is an ordering column and this is a new row then get the next ordering value + if (property_exists($this, 'ordering') && $this->id == 0) + { + $this->ordering = self::getNextOrder(); + } + + + + return parent::check(); + } + + /** + * Define a namespaced asset name for inclusion in the #__assets table + * + * @return string The asset name + * + * @see Table::_getAssetName + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + + return $this->typeAlias . '.' . (int) $this->$k; + } + + /** + * Returns the parent asset's id. If you have a tree structure, retrieve the parent's id using the external key field + * + * @param Table $table Table name + * @param integer $id Id + * + * @see Table::_getAssetParentId + * + * @return mixed The id on success, false on failure. + */ + protected function _getAssetParentId($table = null, $id = null) + { + // We will retrieve the parent-asset from the Asset-table + $assetParent = Table::getInstance('Asset'); + + // Default: if no asset-parent can be found we take the global asset + $assetParentId = $assetParent->getRootId(); + + // The item has the component as asset-parent + $assetParent->loadByName('com_highlights'); + + // Return the found asset-parent-id + if ($assetParent->id) + { + $assetParentId = $assetParent->id; + } + + return $assetParentId; + } + + //XXX_CUSTOM_TABLE_FUNCTION + + + /** + * Delete a record by id + * + * @param mixed $pk Primary key value to delete. Optional + * + * @return bool + */ + public function delete($pk = null) + { + $this->load($pk); + $result = parent::delete($pk); + + return $result; + } +} diff --git a/administrator/components/com_highlights/src/View/Etichetta/HtmlView.php b/administrator/components/com_highlights/src/View/Etichetta/HtmlView.php new file mode 100644 index 00000000..f05b0bd4 --- /dev/null +++ b/administrator/components/com_highlights/src/View/Etichetta/HtmlView.php @@ -0,0 +1,114 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\View\Etichetta; +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Joomla\CMS\Toolbar\ToolbarHelper; +use \Joomla\CMS\Factory; +use \Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; +use \Joomla\CMS\Language\Text; + +/** + * View class for a single Etichetta. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $state; + + protected $item; + + protected $form; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $this->state = $this->get('State'); + $this->item = $this->get('Item'); + $this->form = $this->get('Form'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + $this->addToolbar(); + + parent::display($tpl); + } + + /** + * Add the page title and toolbar. + * + * @return void + * + * @throws Exception + */ + protected function addToolbar() + { + Factory::getApplication()->input->set('hidemainmenu', true); + + $user = Factory::getApplication()->getIdentity(); + $isNew = ($this->item->id == 0); + + if (isset($this->item->checked_out)) + { + $checkedOut = !($this->item->checked_out == 0 || $this->item->checked_out == $user->get('id')); + } + else + { + $checkedOut = false; + } + + $canDo = HighlightsHelper::getActions(); + + ToolbarHelper::title(Text::_('COM_HIGHLIGHTS_TITLE_ETICHETTA'), "generic"); + + // If not checked out, can save the item. + if (!$checkedOut && ($canDo->get('core.edit') || ($canDo->get('core.create')))) + { + ToolbarHelper::apply('etichetta.apply', 'JTOOLBAR_APPLY'); + ToolbarHelper::save('etichetta.save', 'JTOOLBAR_SAVE'); + } + + if (!$checkedOut && ($canDo->get('core.create'))) + { + ToolbarHelper::custom('etichetta.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + + // If an existing item, can save to a copy. + if (!$isNew && $canDo->get('core.create')) + { + ToolbarHelper::custom('etichetta.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + + + + if (empty($this->item->id)) + { + ToolbarHelper::cancel('etichetta.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + ToolbarHelper::cancel('etichetta.cancel', 'JTOOLBAR_CLOSE'); + } + } +} diff --git a/administrator/components/com_highlights/src/View/Etichette/HtmlView.php b/administrator/components/com_highlights/src/View/Etichette/HtmlView.php new file mode 100644 index 00000000..a3d2dd0e --- /dev/null +++ b/administrator/components/com_highlights/src/View/Etichette/HtmlView.php @@ -0,0 +1,177 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\View\Etichette; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; +use \Joomla\CMS\Toolbar\Toolbar; +use \Joomla\CMS\Toolbar\ToolbarHelper; +use \Joomla\CMS\Language\Text; +use \Joomla\Component\Content\Administrator\Extension\ContentComponent; +use \Joomla\CMS\Form\Form; +use \Joomla\CMS\HTML\Helpers\Sidebar; +/** + * View class for a list of Etichette. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $items; + + protected $pagination; + + protected $state; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $this->state = $this->get('State'); + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->filterForm = $this->get('FilterForm'); + $this->activeFilters = $this->get('ActiveFilters'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + + $this->addToolbar(); + + $this->sidebar = Sidebar::render(); + parent::display($tpl); + } + + /** + * Add the page title and toolbar. + * + * @return void + * + * @since 1.0.0 + */ + protected function addToolbar() + { + $state = $this->get('State'); + $canDo = HighlightsHelper::getActions(); + + ToolbarHelper::title(Text::_('COM_HIGHLIGHTS_TITLE_ETICHETTE'), "generic"); + + $toolbar = Toolbar::getInstance('toolbar'); + + // Check if the form exists before showing the add/edit buttons + $formPath = JPATH_COMPONENT_ADMINISTRATOR . '/src/View/Etichette'; + + if (file_exists($formPath)) + { + if ($canDo->get('core.create')) + { + $toolbar->addNew('etichetta.add'); + } + } + + if ($canDo->get('core.edit.state')) + { + $dropdown = $toolbar->dropdownButton('status-group') + ->text('JTOOLBAR_CHANGE_STATUS') + ->toggleSplit(false) + ->icon('fas fa-ellipsis-h') + ->buttonClass('btn btn-action') + ->listCheck(true); + + $childBar = $dropdown->getChildToolbar(); + + if (isset($this->items[0]->state)) + { + $childBar->publish('etichette.publish')->listCheck(true); + $childBar->unpublish('etichette.unpublish')->listCheck(true); + $childBar->archive('etichette.archive')->listCheck(true); + } + + $childBar->standardButton('duplicate') + ->text('JTOOLBAR_DUPLICATE') + ->icon('fas fa-copy') + ->task('etichette.duplicate') + ->listCheck(true); + + if (isset($this->items[0]->checked_out)) + { + $childBar->checkin('etichette.checkin')->listCheck(true); + } + + if (isset($this->items[0]->state)) + { + $childBar->trash('etichette.trash')->listCheck(true); + } + } + + + + // Show trash and delete for components that uses the state field + if (isset($this->items[0]->state)) + { + + if ($this->state->get('filter.state') == ContentComponent::CONDITION_TRASHED && $canDo->get('core.delete')) + { + $toolbar->delete('etichette.delete') + ->text('JTOOLBAR_EMPTY_TRASH') + ->message('JGLOBAL_CONFIRM_DELETE') + ->listCheck(true); + } + } + + if ($canDo->get('core.admin')) + { + $toolbar->preferences('com_highlights'); + } + + // Set sidebar action + Sidebar::setAction('index.php?option=com_highlights&view=etichette'); + } + + /** + * Method to order fields + * + * @return void + */ + protected function getSortFields() + { + return array( + 'a.`id`' => Text::_('JGRID_HEADING_ID'), + 'a.`state`' => Text::_('JSTATUS'), + 'a.`ordering`' => Text::_('JGRID_HEADING_ORDERING'), + 'a.`nome`' => Text::_('COM_HIGHLIGHTS_ETICHETTE_NOME'), + 'a.`lingua`' => Text::_('COM_HIGHLIGHTS_ETICHETTE_LINGUA'), + ); + } + + /** + * Check if state is set + * + * @param mixed $state State + * + * @return bool + */ + public function getState($state) + { + return isset($this->state->{$state}) ? $this->state->{$state} : false; + } +} diff --git a/administrator/components/com_highlights/src/View/Highlight/HtmlView.php b/administrator/components/com_highlights/src/View/Highlight/HtmlView.php new file mode 100644 index 00000000..c0204c0c --- /dev/null +++ b/administrator/components/com_highlights/src/View/Highlight/HtmlView.php @@ -0,0 +1,114 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\View\Highlight; +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Joomla\CMS\Toolbar\ToolbarHelper; +use \Joomla\CMS\Factory; +use \Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; +use \Joomla\CMS\Language\Text; + +/** + * View class for a single Highlight. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $state; + + protected $item; + + protected $form; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $this->state = $this->get('State'); + $this->item = $this->get('Item'); + $this->form = $this->get('Form'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + $this->addToolbar(); + + parent::display($tpl); + } + + /** + * Add the page title and toolbar. + * + * @return void + * + * @throws Exception + */ + protected function addToolbar() + { + Factory::getApplication()->input->set('hidemainmenu', true); + + $user = Factory::getApplication()->getIdentity(); + $isNew = ($this->item->id == 0); + + if (isset($this->item->checked_out)) + { + $checkedOut = !($this->item->checked_out == 0 || $this->item->checked_out == $user->get('id')); + } + else + { + $checkedOut = false; + } + + $canDo = HighlightsHelper::getActions(); + + ToolbarHelper::title(Text::_('COM_HIGHLIGHTS_TITLE_HIGHLIGHT'), "generic"); + + // If not checked out, can save the item. + if (!$checkedOut && ($canDo->get('core.edit') || ($canDo->get('core.create')))) + { + ToolbarHelper::apply('highlight.apply', 'JTOOLBAR_APPLY'); + ToolbarHelper::save('highlight.save', 'JTOOLBAR_SAVE'); + } + + if (!$checkedOut && ($canDo->get('core.create'))) + { + ToolbarHelper::custom('highlight.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + + // If an existing item, can save to a copy. + if (!$isNew && $canDo->get('core.create')) + { + ToolbarHelper::custom('highlight.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + + + + if (empty($this->item->id)) + { + ToolbarHelper::cancel('highlight.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + ToolbarHelper::cancel('highlight.cancel', 'JTOOLBAR_CLOSE'); + } + } +} diff --git a/administrator/components/com_highlights/src/View/Highlights/HtmlView.php b/administrator/components/com_highlights/src/View/Highlights/HtmlView.php new file mode 100644 index 00000000..41136707 --- /dev/null +++ b/administrator/components/com_highlights/src/View/Highlights/HtmlView.php @@ -0,0 +1,177 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Administrator\View\Highlights; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Pcrt\Component\Highlights\Administrator\Helper\HighlightsHelper; +use \Joomla\CMS\Toolbar\Toolbar; +use \Joomla\CMS\Toolbar\ToolbarHelper; +use \Joomla\CMS\Language\Text; +use \Joomla\Component\Content\Administrator\Extension\ContentComponent; +use \Joomla\CMS\Form\Form; +use \Joomla\CMS\HTML\Helpers\Sidebar; +/** + * View class for a list of Highlights. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $items; + + protected $pagination; + + protected $state; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $this->state = $this->get('State'); + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->filterForm = $this->get('FilterForm'); + $this->activeFilters = $this->get('ActiveFilters'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + + $this->addToolbar(); + + $this->sidebar = Sidebar::render(); + parent::display($tpl); + } + + /** + * Add the page title and toolbar. + * + * @return void + * + * @since 1.0.0 + */ + protected function addToolbar() + { + $state = $this->get('State'); + $canDo = HighlightsHelper::getActions(); + + ToolbarHelper::title(Text::_('COM_HIGHLIGHTS_TITLE_HIGHLIGHTS'), "generic"); + + $toolbar = Toolbar::getInstance('toolbar'); + + // Check if the form exists before showing the add/edit buttons + $formPath = JPATH_COMPONENT_ADMINISTRATOR . '/src/View/Highlights'; + + if (file_exists($formPath)) + { + if ($canDo->get('core.create')) + { + $toolbar->addNew('highlight.add'); + } + } + + if ($canDo->get('core.edit.state')) + { + $dropdown = $toolbar->dropdownButton('status-group') + ->text('JTOOLBAR_CHANGE_STATUS') + ->toggleSplit(false) + ->icon('fas fa-ellipsis-h') + ->buttonClass('btn btn-action') + ->listCheck(true); + + $childBar = $dropdown->getChildToolbar(); + + if (isset($this->items[0]->state)) + { + $childBar->publish('highlights.publish')->listCheck(true); + $childBar->unpublish('highlights.unpublish')->listCheck(true); + $childBar->archive('highlights.archive')->listCheck(true); + } + + $childBar->standardButton('duplicate') + ->text('JTOOLBAR_DUPLICATE') + ->icon('fas fa-copy') + ->task('highlights.duplicate') + ->listCheck(true); + + if (isset($this->items[0]->checked_out)) + { + $childBar->checkin('highlights.checkin')->listCheck(true); + } + + if (isset($this->items[0]->state)) + { + $childBar->trash('highlights.trash')->listCheck(true); + } + } + + + + // Show trash and delete for components that uses the state field + if (isset($this->items[0]->state)) + { + + if ($this->state->get('filter.state') == ContentComponent::CONDITION_TRASHED && $canDo->get('core.delete')) + { + $toolbar->delete('highlights.delete') + ->text('JTOOLBAR_EMPTY_TRASH') + ->message('JGLOBAL_CONFIRM_DELETE') + ->listCheck(true); + } + } + + if ($canDo->get('core.admin')) + { + $toolbar->preferences('com_highlights'); + } + + // Set sidebar action + Sidebar::setAction('index.php?option=com_highlights&view=highlights'); + } + + /** + * Method to order fields + * + * @return void + */ + protected function getSortFields() + { + return array( + 'a.`id`' => Text::_('JGRID_HEADING_ID'), + 'a.`state`' => Text::_('JSTATUS'), + 'a.`ordering`' => Text::_('JGRID_HEADING_ORDERING'), + 'a.`etichetta`' => Text::_('COM_HIGHLIGHTS_HIGHLIGHTS_ETICHETTA'), + 'a.`titolo`' => Text::_('COM_HIGHLIGHTS_HIGHLIGHTS_TITOLO'), + ); + } + + /** + * Check if state is set + * + * @param mixed $state State + * + * @return bool + */ + public function getState($state) + { + return isset($this->state->{$state}) ? $this->state->{$state} : false; + } +} diff --git a/administrator/components/com_highlights/tmpl/etichetta/default.php b/administrator/components/com_highlights/tmpl/etichetta/default.php new file mode 100644 index 00000000..adae16f7 --- /dev/null +++ b/administrator/components/com_highlights/tmpl/etichetta/default.php @@ -0,0 +1,32 @@ + + * @copyright 2023 Super User + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Session\Session; +use Joomla\Utilities\ArrayHelper; + + +?> + +
+ + + + +
+ +
+ diff --git a/administrator/components/com_highlights/tmpl/etichetta/edit.php b/administrator/components/com_highlights/tmpl/etichetta/edit.php new file mode 100644 index 00000000..82da6562 --- /dev/null +++ b/administrator/components/com_highlights/tmpl/etichetta/edit.php @@ -0,0 +1,55 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +$wa = $this->document->getWebAssetManager(); +$wa->useScript('keepalive') + ->useScript('form.validate'); +HTMLHelper::_('bootstrap.tooltip'); +?> + +
+ + + 'etichetta')); ?> + +
+
+
+ + form->renderField('nome'); ?> + form->renderField('lingua'); ?> +
+
+
+ + + + + + form->renderField('created_by'); ?> + form->renderField('modified_by'); ?> + + + + + + + +
diff --git a/administrator/components/com_highlights/tmpl/etichette/default.php b/administrator/components/com_highlights/tmpl/etichette/default.php new file mode 100644 index 00000000..2ff767f2 --- /dev/null +++ b/administrator/components/com_highlights/tmpl/etichette/default.php @@ -0,0 +1,174 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Layout\LayoutHelper; +use \Joomla\CMS\Language\Text; +use Joomla\CMS\Session\Session; + +HTMLHelper::_('bootstrap.tooltip'); +HTMLHelper::_('behavior.multiselect'); + +// Import CSS +$wa = $this->document->getWebAssetManager(); +$wa->useStyle('com_highlights.admin') + ->useScript('com_highlights.admin'); + +$user = Factory::getApplication()->getIdentity(); +$userId = $user->get('id'); +$listOrder = $this->state->get('list.ordering'); +$listDirn = $this->state->get('list.direction'); +$canOrder = $user->authorise('core.edit.state', 'com_highlights'); + +$saveOrder = $listOrder == 'a.ordering'; + +if (!empty($saveOrder)) +{ + $saveOrderingUrl = 'index.php?option=com_highlights&task=etichette.saveOrderAjax&tmpl=component&' . Session::getFormToken() . '=1'; + HTMLHelper::_('draggablelist.draggable'); +} + +?> + +
+
+
+
+ $this)); ?> + +
+ + + + + + items[0]->ordering)): ?> + + + + + + + + + + + + + + + + + + class="js-draggable" data-url="" data-direction="" > + items as $i => $item) : + $ordering = ($listOrder == 'a.ordering'); + $canCreate = $user->authorise('core.create', 'com_highlights'); + $canEdit = $user->authorise('core.edit', 'com_highlights'); + $canCheckin = $user->authorise('core.manage', 'com_highlights'); + $canChange = $user->authorise('core.edit.state', 'com_highlights'); + ?> + + + + items[0]->ordering)) : ?> + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ pagination->getListFooter(); ?> +
+ id); ?> + + + + + + + + + + state, $i, 'etichette.', $canChange, 'cb'); ?> + + checked_out) && $item->checked_out && ($canEdit || $canChange)) : ?> + uEditor, $item->checked_out_time, 'etichette.', $canCheckin); ?> + + + + escape($item->nome); ?> + + + escape($item->nome); ?> + + + lingua; ?> + + id; ?> + +
+ + + + + +
+
+
+
\ No newline at end of file diff --git a/administrator/components/com_highlights/tmpl/highlight/default.php b/administrator/components/com_highlights/tmpl/highlight/default.php new file mode 100644 index 00000000..adae16f7 --- /dev/null +++ b/administrator/components/com_highlights/tmpl/highlight/default.php @@ -0,0 +1,32 @@ + + * @copyright 2023 Super User + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Session\Session; +use Joomla\Utilities\ArrayHelper; + + +?> + +
+ + + + +
+ +
+ diff --git a/administrator/components/com_highlights/tmpl/highlight/edit.php b/administrator/components/com_highlights/tmpl/highlight/edit.php new file mode 100644 index 00000000..31b35a7e --- /dev/null +++ b/administrator/components/com_highlights/tmpl/highlight/edit.php @@ -0,0 +1,83 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +$wa = $this->document->getWebAssetManager(); +$wa->useScript('keepalive') + ->useScript('form.validate'); +HTMLHelper::_('bootstrap.tooltip'); +?> + +
+ + + 'highlight')); ?> + +
+
+
+ + form->renderField('etichetta'); ?> + form->renderField('titolo'); ?> + form->renderField('sottotitolo'); ?> + form->renderField('descrizione'); ?> + form->renderField('lingua'); ?> + form->renderField('link_pulsante'); ?> + form->renderField('testo_pulsante'); ?> + form->renderField('data'); ?> +
+
+
+ + +
+
+
+ + form->renderField('immagine_main'); ?> + form->renderField('immagine_secondaria'); ?> +
+
+
+ + +
+
+
+ + form->renderField('data_inizio_pubblicazione'); ?> + form->renderField('data_fine_pubblicazione'); ?> +
+
+
+ + + + + + form->renderField('created_by'); ?> + form->renderField('modified_by'); ?> + + + + + + + +
diff --git a/administrator/components/com_highlights/tmpl/highlights/default.php b/administrator/components/com_highlights/tmpl/highlights/default.php new file mode 100644 index 00000000..fc790a26 --- /dev/null +++ b/administrator/components/com_highlights/tmpl/highlights/default.php @@ -0,0 +1,174 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Layout\LayoutHelper; +use \Joomla\CMS\Language\Text; +use Joomla\CMS\Session\Session; + +HTMLHelper::_('bootstrap.tooltip'); +HTMLHelper::_('behavior.multiselect'); + +// Import CSS +$wa = $this->document->getWebAssetManager(); +$wa->useStyle('com_highlights.admin') + ->useScript('com_highlights.admin'); + +$user = Factory::getApplication()->getIdentity(); +$userId = $user->get('id'); +$listOrder = $this->state->get('list.ordering'); +$listDirn = $this->state->get('list.direction'); +$canOrder = $user->authorise('core.edit.state', 'com_highlights'); + +$saveOrder = $listOrder == 'a.ordering'; + +if (!empty($saveOrder)) +{ + $saveOrderingUrl = 'index.php?option=com_highlights&task=highlights.saveOrderAjax&tmpl=component&' . Session::getFormToken() . '=1'; + HTMLHelper::_('draggablelist.draggable'); +} + +?> + +
+
+
+
+ $this)); ?> + +
+ + + + + + items[0]->ordering)): ?> + + + + + + + + + + + + + + + + + + class="js-draggable" data-url="" data-direction="" > + items as $i => $item) : + $ordering = ($listOrder == 'a.ordering'); + $canCreate = $user->authorise('core.create', 'com_highlights'); + $canEdit = $user->authorise('core.edit', 'com_highlights'); + $canCheckin = $user->authorise('core.manage', 'com_highlights'); + $canChange = $user->authorise('core.edit.state', 'com_highlights'); + ?> + + + + items[0]->ordering)) : ?> + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ pagination->getListFooter(); ?> +
+ id); ?> + + + + + + + + + + state, $i, 'highlights.', $canChange, 'cb'); ?> + + checked_out) && $item->checked_out && ($canEdit || $canChange)) : ?> + uEditor, $item->checked_out_time, 'highlights.', $canCheckin); ?> + + + + escape($item->titolo); ?> + + + escape($item->titolo); ?> + + + etichetta; ?> + + id; ?> + +
+ + + + + +
+
+
+
\ No newline at end of file diff --git a/administrator/components/com_highlights/tmpl/index.html b/administrator/components/com_highlights/tmpl/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/administrator/components/com_highlights/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/administrator/language/en-GB/com_highlights.ini b/administrator/language/en-GB/com_highlights.ini new file mode 100644 index 00000000..43e30133 --- /dev/null +++ b/administrator/language/en-GB/com_highlights.ini @@ -0,0 +1,188 @@ +COM_HIGHLIGHTS="Highlights" +COM_HIGHLIGHTS_COMPONENT_LABEL="Highlights" +COM_HIGHLIGHTS_CONFIGURATION="Highlights Configuration" +COM_HIGHLIGHTS_ACCESS_HEADING="Access" +COM_HIGHLIGHTS_COMPONENT_DESC="" +COM_HIGHLIGHTS_XML_DESCRIPTION="" +COM_HIGHLIGHTS_N_ITEMS_ARCHIVED="%d items successfully archived" +COM_HIGHLIGHTS_N_ITEMS_ARCHIVED_1="%d item successfully archived" +COM_HIGHLIGHTS_N_ITEMS_CHECKED_IN_0="No item successfully checked in" +COM_HIGHLIGHTS_N_ITEMS_CHECKED_IN_1="%d item successfully checked in" +COM_HIGHLIGHTS_N_ITEMS_CHECKED_IN_MORE="%d items successfully checked in" +COM_HIGHLIGHTS_N_ITEMS_DELETED="%d items successfully deleted" +COM_HIGHLIGHTS_N_ITEMS_DELETED_1="%d item successfully deleted" +COM_HIGHLIGHTS_N_ITEMS_PUBLISHED="%d items successfully published" +COM_HIGHLIGHTS_N_ITEMS_PUBLISHED_1="%d item successfully published" +COM_HIGHLIGHTS_N_ITEMS_TRASHED="%d items successfully trashed" +COM_HIGHLIGHTS_N_ITEMS_TRASHED_1="%d item successfully trashed" +COM_HIGHLIGHTS_N_ITEMS_UNPUBLISHED="%d items successfully unpublished" +COM_HIGHLIGHTS_N_ITEMS_UNPUBLISHED_1="%d item successfully unpublished" +COM_HIGHLIGHTS_NO_ITEM_SELECTED="No items selected" +COM_HIGHLIGHTS_SAVE_SUCCESS="Item successfully saved" +COM_HIGHLIGHTS_ITEM_ID_SELECT_LABEL="Select the item ID" +COM_HIGHLIGHTS_ITEM_ID_SELECT_LABEL_FORM="Select the Item ID to Edit (Set up as 0 if you want to set up as add form)" +COM_HIGHLIGHTS_FIELDSET_ITEM_ID_SELECT_LABEL="Required Settings" +COM_HIGHLIGHTS_FILTER_SELECT_LABEL=" - Select %s - " +COM_HIGHLIGHTS_TEST_LABEL="Test label" +COM_HIGHLIGHTS_FIELDSET_RULES="Permissions" +COM_HIGHLIGHTS_FROM_FILTER="From %s" +COM_HIGHLIGHTS_TO_FILTER="To %s" +COM_HIGHLIGHTS_VIEW_FILE="[View File]" +COM_HIGHLIGHTS_ITEMS_SUCCESS_DUPLICATED="Items successfully duplicated" + +COM_HIGHLIGHTS_SEARCH_FILTER_SUBMIT = "Search" +COM_HIGHLIGHTS_SEARCH_TOOLS = "Search Tools" +COM_HIGHLIGHTS_SEARCH_TOOLS_DESC = "Filter the list items" +COM_HIGHLIGHTS_SEARCH_FILTER_CLEAR = "Clear filter" + +COM_HIGHLIGHTS_XML_DESCRIPTION = "" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_HIGHLIGHTS_ID = "ID" +COM_HIGHLIGHTS_HIGHLIGHTS_STATE = "State" +COM_HIGHLIGHTS_HIGHLIGHTS_ORDERING = "Order" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CREATED_BY = "Created by" +COM_HIGHLIGHTS_HIGHLIGHTS_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_HIGHLIGHTS_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_HIGHLIGHTS_TITOLO = "Titolo" +COM_HIGHLIGHTS_HIGHLIGHTS_SOTTOTITOLO = "Sottotitolo" +COM_HIGHLIGHTS_HIGHLIGHTS_DESCRIZIONE = "Descrizione" +COM_HIGHLIGHTS_HIGHLIGHTS_LINGUA = "Lingua" +COM_HIGHLIGHTS_HIGHLIGHTS_LINK_PULSANTE = "Link Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_TESTO_PULSANTE = "Testo Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA = "Data" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_MAIN = "Immagine Main" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_SECONDARIA = "Immagine Secondaria" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE = "Data Inizio Pubblicazione" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE = "Data Fine Pubblicazione" +COM_HIGHLIGHTS_TITLE_ETICHETTE = "Etichette" +COM_HIGHLIGHTS_ETICHETTE_ID = "ID" +COM_HIGHLIGHTS_ETICHETTE_STATE = "State" +COM_HIGHLIGHTS_ETICHETTE_ORDERING = "Order" +COM_HIGHLIGHTS_ETICHETTE_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_ETICHETTE_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_ETICHETTE_CREATED_BY = "Created by" +COM_HIGHLIGHTS_ETICHETTE_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_ETICHETTE_NOME = "Nome" +COM_HIGHLIGHTS_ETICHETTE_LINGUA = "Lingua" +COM_HIGHLIGHTS_ID_DESC = "ID Descending" +COM_HIGHLIGHTS_STATE_DESC = "State Descending" +COM_HIGHLIGHTS_ORDERING_DESC = "Order Descending" +COM_HIGHLIGHTS_CHECKED_OUT_DESC = "N/A Descending" +COM_HIGHLIGHTS_CHECKED_OUT_TIME_DESC = "N/A Descending" +COM_HIGHLIGHTS_CREATED_BY_DESC = "Created by Descending" +COM_HIGHLIGHTS_MODIFIED_BY_DESC = "Modified by Descending" +COM_HIGHLIGHTS_ETICHETTA_DESC = "Etichetta Descending" +COM_HIGHLIGHTS_TITOLO_DESC = "Titolo Descending" +COM_HIGHLIGHTS_SOTTOTITOLO_DESC = "Sottotitolo Descending" +COM_HIGHLIGHTS_DESCRIZIONE_DESC = "Descrizione Descending" +COM_HIGHLIGHTS_LINGUA_DESC = "Lingua Descending" +COM_HIGHLIGHTS_LINK_PULSANTE_DESC = "Link Pulsante Descending" +COM_HIGHLIGHTS_TESTO_PULSANTE_DESC = "Testo Pulsante Descending" +COM_HIGHLIGHTS_DATA_DESC = "Data Descending" +COM_HIGHLIGHTS_IMMAGINE_MAIN_DESC = "Immagine Main Descending" +COM_HIGHLIGHTS_IMMAGINE_SECONDARIA_DESC = "Immagine Secondaria Descending" +COM_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE_DESC = "Data Inizio Pubblicazione Descending" +COM_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE_DESC = "Data Fine Pubblicazione Descending" +COM_HIGHLIGHTS_NOME_DESC = "Nome Descending" +COM_HIGHLIGHTS_ID_ASC = "ID Ascending" +COM_HIGHLIGHTS_STATE_ASC = "State Ascending" +COM_HIGHLIGHTS_ORDERING_ASC = "Order Ascending" +COM_HIGHLIGHTS_CHECKED_OUT_ASC = "N/A Ascending" +COM_HIGHLIGHTS_CHECKED_OUT_TIME_ASC = "N/A Ascending" +COM_HIGHLIGHTS_CREATED_BY_ASC = "Created by Ascending" +COM_HIGHLIGHTS_MODIFIED_BY_ASC = "Modified by Ascending" +COM_HIGHLIGHTS_ETICHETTA_ASC = "Etichetta Ascending" +COM_HIGHLIGHTS_TITOLO_ASC = "Titolo Ascending" +COM_HIGHLIGHTS_SOTTOTITOLO_ASC = "Sottotitolo Ascending" +COM_HIGHLIGHTS_DESCRIZIONE_ASC = "Descrizione Ascending" +COM_HIGHLIGHTS_LINGUA_ASC = "Lingua Ascending" +COM_HIGHLIGHTS_LINK_PULSANTE_ASC = "Link Pulsante Ascending" +COM_HIGHLIGHTS_TESTO_PULSANTE_ASC = "Testo Pulsante Ascending" +COM_HIGHLIGHTS_DATA_ASC = "Data Ascending" +COM_HIGHLIGHTS_IMMAGINE_MAIN_ASC = "Immagine Main Ascending" +COM_HIGHLIGHTS_IMMAGINE_SECONDARIA_ASC = "Immagine Secondaria Ascending" +COM_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE_ASC = "Data Inizio Pubblicazione Ascending" +COM_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE_ASC = "Data Fine Pubblicazione Ascending" +COM_HIGHLIGHTS_NOME_ASC = "Nome Ascending" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHT = "Item" +COM_HIGHLIGHTS_LEGEND_HIGHLIGHT = "Item" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ID = "ID" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ID = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_STATE = "State" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_STATE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ORDERING = "Order" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ORDERING = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT_TIME = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CREATED_BY = "Created by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CREATED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_MODIFIED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ETICHETTA = "" +COM_HIGHLIGHTS_TAB_HIGHLIGHT = "Highlight" +COM_HIGHLIGHTS_FIELDSET_HIGHLIGHT = "Highlight" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TITOLO = "Titolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TITOLO = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_SOTTOTITOLO = "Sottotitolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_SOTTOTITOLO = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DESCRIZIONE = "Descrizione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DESCRIZIONE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINGUA = "Lingua" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINGUA = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINK_PULSANTE = "Link Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINK_PULSANTE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TESTO_PULSANTE = "Testo Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TESTO_PULSANTE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA = "Data" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_MAIN = "Immagine Main" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_MAIN = "" +COM_HIGHLIGHTS_TAB_IMMAGINI = "Immagini" +COM_HIGHLIGHTS_FIELDSET_IMMAGINI = "Immagini" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_SECONDARIA = "Immagine Secondaria" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_SECONDARIA = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE = "Data Inizio Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE = "" +COM_HIGHLIGHTS_TAB_PUBBLICAZIONE = "Pubblicazione" +COM_HIGHLIGHTS_FIELDSET_PUBBLICAZIONE = "Pubblicazione" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE = "Data Fine Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE = "" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS_DESC = "Show a list of Highlights" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT = "Single Highlight" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT_DESC = "Show a specific Highlight" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT = "HighlightForm" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT_DESC = "Show a form to add or edit a Highlight" +COM_HIGHLIGHTS_TITLE_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_LEGEND_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ID = "ID" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_ID = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_STATE = "State" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_STATE = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ORDERING = "Order" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_ORDERING = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_CHECKED_OUT = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_CHECKED_OUT_TIME = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CREATED_BY = "Created by" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_CREATED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_MODIFIED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_NOME = "Nome" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_NOME = "" +COM_HIGHLIGHTS_TAB_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FIELDSET_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_LINGUA = "Lingua" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_LINGUA = "" + + + + diff --git a/administrator/language/en-GB/com_highlights.sys.ini b/administrator/language/en-GB/com_highlights.sys.ini new file mode 100644 index 00000000..08eba09f --- /dev/null +++ b/administrator/language/en-GB/com_highlights.sys.ini @@ -0,0 +1,62 @@ +COM_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_XML_DESCRIPTION = "" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHTS="Highlights" + +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS="Highlights" + +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS_DESC="Show a list of Highlights" + +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT="Single Highlight" + +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT_DESC="Show a specific Highlight" + +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT="HighlightForm" + +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT_DESC="Show a form to add or edit a Highlight" + +COM_HIGHLIGHTS_TITLE_ETICHETTE="Etichette" + +COM_HIGHLIGHTS_SHOW_TABLES_SQL_STATEMENT="SHOW FULL TABLES WHERE tables_in_%s LIKE %s" + +COM_HIGHLIGHTS_SHOW_COLUMNS_TABLE_SQL_STATEMENT="SHOW FULL COLUMNS FROM %s WHERE Field LIKE %s" + +COM_HIGHLIGHTS_CREATE_TABLE_SQL_STATEMENT="CREATE TABLE IF NOT EXISTS %s (%s)" + +COM_HIGHLIGHTS_CREATE_TABLE_COLUMN_DECLARATION_SQL_STATEMENT="%s %s NOT NULL %s %s %s" + +COM_HIGHLIGHTS_CREATE_TABLE_INDEX_SQL_STATEMENT="INDEX %s (%s ASC)" + +COM_HIGHLIGHTS_ADD_COLUMN_SQL_STATEMENT="ALTER TABLE %s ADD %s" + +COM_HIGHLIGHTS_RENAME_COLUMN_SQL_STATEMENT="ALTER TABLE %s CHANGE %s %s %s" + +COM_HIGHLIGHTS_CHANGE_COLUMN_TYPE_SQL_STATEMENT="ALTER TABLE %s MODIFY %s" + +COM_HIGHLIGHTS_DROP_COLUMN_SQL_STATEMENT="ALTER TABLE %s DROP COLUMN %s" + +COM_HIGHLIGHTS_IS_NOT_COMPATIBLE_JOOMLA_VERSION="This component is not compatible with installed Joomla version" + +COM_HIGHLIGHTS_SIMPLEXML_LOAD_FILE_FUNCTION_DOES_NOT_EXISTS="This script needs 'simplexml_load_file' to update the component" + +COM_HIGHLIGHTS_CREATING_TABLE_ACTION_COMPLETED_SUCCESFULLY="Table `%s` has been succesfully created" + +COM_HIGHLIGHTS_CREATING_TABLE_ACTION_AN_ERROR_OCCURRED="There was an error creating the table `%s`. Error: %s" + +COM_HIGHLIGHTS_RENAMING_TABLE_ACTION_COMPLETED_SUCCESFULLY="Table `%s` was succesfully renamed to `%s`" + +COM_HIGHLIGHTS_RENAMING_TABLE_ACTION_AN_ERROR_OCCURRED="There was an error renaming the table `%s`. Error: %s" + +COM_HIGHLIGHTS_DROPPING_TABLE_ACTION_COMPLETED_SUCCESSFULLY="Table `%s` was succesfully deleted" + +COM_HIGHLIGHTS_ADDING_FIELD_ACTION_COMPLETED_SUCCESSFULLY="Field `%s` has been succesfully added" + +COM_HIGHLIGHTS_ADDING_FIELD_ACTION_AN_ERROR_OCCURRED="There was an error adding the field `%s`. Error: %s" + +COM_HIGHLIGHTS_MODIFYING_FIELD_ACTION_COMPLETED_SUCCESSFULLY="Field `%s` has been succesfully modified" + +COM_HIGHLIGHTS_MODIFYING_FIELD_ACTION_AN_ERROR_OCCURRED="There was an error modifying the field `%s`. Error: %s" + +COM_HIGHLIGHTS_DROPPING_FIELD_ACTION_COMPLETED_SUCCESSFULLY="Field `%s` has been succesfully deleted" + +COM_HIGHLIGHTS_DROPPING_FIELD_ACTION_AN_ERROR_OCCURRED="There was an error deleting the field `%s`. Error: %s" diff --git a/administrator/language/en-GB/plg_finder_highlightshighlights.ini b/administrator/language/en-GB/plg_finder_highlightshighlights.ini new file mode 100644 index 00000000..23c532da --- /dev/null +++ b/administrator/language/en-GB/plg_finder_highlightshighlights.ini @@ -0,0 +1,4 @@ +PLG_FINDER_HIGHLIGHTS="Finder - Highlights" +PLG_FINDER_HIGHLIGHTS_XML_DESCRIPTION="" +PLG_FINDER_HIGHLIGHTS_FIELD_SEARCHLIMIT_DESC="Number of search items to return" +PLG_FINDER_HIGHLIGHTS_FIELD_SEARCHLIMIT_LABEL="Finder Limit" \ No newline at end of file diff --git a/administrator/language/en-GB/plg_finder_highlightshighlights.sys.ini b/administrator/language/en-GB/plg_finder_highlightshighlights.sys.ini new file mode 100644 index 00000000..7e63a805 --- /dev/null +++ b/administrator/language/en-GB/plg_finder_highlightshighlights.sys.ini @@ -0,0 +1,4 @@ +PLG_FINDER_HIGHLIGHTS="Finder - Highlights" +PLG_FINDER_COM_HIGHLIGHTS_XML_DESCRIPTION="" +PLG_FINDER_COM_HIGHLIGHTS_FIELD_SEARCHLIMIT_DESC="Number of search items to return" +PLG_FINDER_COM_HIGHLIGHTS_FIELD_SEARCHLIMIT_LABEL="Finder Limit" \ No newline at end of file diff --git a/administrator/language/it-IT/com_highlights.ini b/administrator/language/it-IT/com_highlights.ini new file mode 100644 index 00000000..43e30133 --- /dev/null +++ b/administrator/language/it-IT/com_highlights.ini @@ -0,0 +1,188 @@ +COM_HIGHLIGHTS="Highlights" +COM_HIGHLIGHTS_COMPONENT_LABEL="Highlights" +COM_HIGHLIGHTS_CONFIGURATION="Highlights Configuration" +COM_HIGHLIGHTS_ACCESS_HEADING="Access" +COM_HIGHLIGHTS_COMPONENT_DESC="" +COM_HIGHLIGHTS_XML_DESCRIPTION="" +COM_HIGHLIGHTS_N_ITEMS_ARCHIVED="%d items successfully archived" +COM_HIGHLIGHTS_N_ITEMS_ARCHIVED_1="%d item successfully archived" +COM_HIGHLIGHTS_N_ITEMS_CHECKED_IN_0="No item successfully checked in" +COM_HIGHLIGHTS_N_ITEMS_CHECKED_IN_1="%d item successfully checked in" +COM_HIGHLIGHTS_N_ITEMS_CHECKED_IN_MORE="%d items successfully checked in" +COM_HIGHLIGHTS_N_ITEMS_DELETED="%d items successfully deleted" +COM_HIGHLIGHTS_N_ITEMS_DELETED_1="%d item successfully deleted" +COM_HIGHLIGHTS_N_ITEMS_PUBLISHED="%d items successfully published" +COM_HIGHLIGHTS_N_ITEMS_PUBLISHED_1="%d item successfully published" +COM_HIGHLIGHTS_N_ITEMS_TRASHED="%d items successfully trashed" +COM_HIGHLIGHTS_N_ITEMS_TRASHED_1="%d item successfully trashed" +COM_HIGHLIGHTS_N_ITEMS_UNPUBLISHED="%d items successfully unpublished" +COM_HIGHLIGHTS_N_ITEMS_UNPUBLISHED_1="%d item successfully unpublished" +COM_HIGHLIGHTS_NO_ITEM_SELECTED="No items selected" +COM_HIGHLIGHTS_SAVE_SUCCESS="Item successfully saved" +COM_HIGHLIGHTS_ITEM_ID_SELECT_LABEL="Select the item ID" +COM_HIGHLIGHTS_ITEM_ID_SELECT_LABEL_FORM="Select the Item ID to Edit (Set up as 0 if you want to set up as add form)" +COM_HIGHLIGHTS_FIELDSET_ITEM_ID_SELECT_LABEL="Required Settings" +COM_HIGHLIGHTS_FILTER_SELECT_LABEL=" - Select %s - " +COM_HIGHLIGHTS_TEST_LABEL="Test label" +COM_HIGHLIGHTS_FIELDSET_RULES="Permissions" +COM_HIGHLIGHTS_FROM_FILTER="From %s" +COM_HIGHLIGHTS_TO_FILTER="To %s" +COM_HIGHLIGHTS_VIEW_FILE="[View File]" +COM_HIGHLIGHTS_ITEMS_SUCCESS_DUPLICATED="Items successfully duplicated" + +COM_HIGHLIGHTS_SEARCH_FILTER_SUBMIT = "Search" +COM_HIGHLIGHTS_SEARCH_TOOLS = "Search Tools" +COM_HIGHLIGHTS_SEARCH_TOOLS_DESC = "Filter the list items" +COM_HIGHLIGHTS_SEARCH_FILTER_CLEAR = "Clear filter" + +COM_HIGHLIGHTS_XML_DESCRIPTION = "" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_HIGHLIGHTS_ID = "ID" +COM_HIGHLIGHTS_HIGHLIGHTS_STATE = "State" +COM_HIGHLIGHTS_HIGHLIGHTS_ORDERING = "Order" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CREATED_BY = "Created by" +COM_HIGHLIGHTS_HIGHLIGHTS_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_HIGHLIGHTS_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_HIGHLIGHTS_TITOLO = "Titolo" +COM_HIGHLIGHTS_HIGHLIGHTS_SOTTOTITOLO = "Sottotitolo" +COM_HIGHLIGHTS_HIGHLIGHTS_DESCRIZIONE = "Descrizione" +COM_HIGHLIGHTS_HIGHLIGHTS_LINGUA = "Lingua" +COM_HIGHLIGHTS_HIGHLIGHTS_LINK_PULSANTE = "Link Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_TESTO_PULSANTE = "Testo Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA = "Data" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_MAIN = "Immagine Main" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_SECONDARIA = "Immagine Secondaria" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE = "Data Inizio Pubblicazione" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE = "Data Fine Pubblicazione" +COM_HIGHLIGHTS_TITLE_ETICHETTE = "Etichette" +COM_HIGHLIGHTS_ETICHETTE_ID = "ID" +COM_HIGHLIGHTS_ETICHETTE_STATE = "State" +COM_HIGHLIGHTS_ETICHETTE_ORDERING = "Order" +COM_HIGHLIGHTS_ETICHETTE_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_ETICHETTE_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_ETICHETTE_CREATED_BY = "Created by" +COM_HIGHLIGHTS_ETICHETTE_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_ETICHETTE_NOME = "Nome" +COM_HIGHLIGHTS_ETICHETTE_LINGUA = "Lingua" +COM_HIGHLIGHTS_ID_DESC = "ID Descending" +COM_HIGHLIGHTS_STATE_DESC = "State Descending" +COM_HIGHLIGHTS_ORDERING_DESC = "Order Descending" +COM_HIGHLIGHTS_CHECKED_OUT_DESC = "N/A Descending" +COM_HIGHLIGHTS_CHECKED_OUT_TIME_DESC = "N/A Descending" +COM_HIGHLIGHTS_CREATED_BY_DESC = "Created by Descending" +COM_HIGHLIGHTS_MODIFIED_BY_DESC = "Modified by Descending" +COM_HIGHLIGHTS_ETICHETTA_DESC = "Etichetta Descending" +COM_HIGHLIGHTS_TITOLO_DESC = "Titolo Descending" +COM_HIGHLIGHTS_SOTTOTITOLO_DESC = "Sottotitolo Descending" +COM_HIGHLIGHTS_DESCRIZIONE_DESC = "Descrizione Descending" +COM_HIGHLIGHTS_LINGUA_DESC = "Lingua Descending" +COM_HIGHLIGHTS_LINK_PULSANTE_DESC = "Link Pulsante Descending" +COM_HIGHLIGHTS_TESTO_PULSANTE_DESC = "Testo Pulsante Descending" +COM_HIGHLIGHTS_DATA_DESC = "Data Descending" +COM_HIGHLIGHTS_IMMAGINE_MAIN_DESC = "Immagine Main Descending" +COM_HIGHLIGHTS_IMMAGINE_SECONDARIA_DESC = "Immagine Secondaria Descending" +COM_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE_DESC = "Data Inizio Pubblicazione Descending" +COM_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE_DESC = "Data Fine Pubblicazione Descending" +COM_HIGHLIGHTS_NOME_DESC = "Nome Descending" +COM_HIGHLIGHTS_ID_ASC = "ID Ascending" +COM_HIGHLIGHTS_STATE_ASC = "State Ascending" +COM_HIGHLIGHTS_ORDERING_ASC = "Order Ascending" +COM_HIGHLIGHTS_CHECKED_OUT_ASC = "N/A Ascending" +COM_HIGHLIGHTS_CHECKED_OUT_TIME_ASC = "N/A Ascending" +COM_HIGHLIGHTS_CREATED_BY_ASC = "Created by Ascending" +COM_HIGHLIGHTS_MODIFIED_BY_ASC = "Modified by Ascending" +COM_HIGHLIGHTS_ETICHETTA_ASC = "Etichetta Ascending" +COM_HIGHLIGHTS_TITOLO_ASC = "Titolo Ascending" +COM_HIGHLIGHTS_SOTTOTITOLO_ASC = "Sottotitolo Ascending" +COM_HIGHLIGHTS_DESCRIZIONE_ASC = "Descrizione Ascending" +COM_HIGHLIGHTS_LINGUA_ASC = "Lingua Ascending" +COM_HIGHLIGHTS_LINK_PULSANTE_ASC = "Link Pulsante Ascending" +COM_HIGHLIGHTS_TESTO_PULSANTE_ASC = "Testo Pulsante Ascending" +COM_HIGHLIGHTS_DATA_ASC = "Data Ascending" +COM_HIGHLIGHTS_IMMAGINE_MAIN_ASC = "Immagine Main Ascending" +COM_HIGHLIGHTS_IMMAGINE_SECONDARIA_ASC = "Immagine Secondaria Ascending" +COM_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE_ASC = "Data Inizio Pubblicazione Ascending" +COM_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE_ASC = "Data Fine Pubblicazione Ascending" +COM_HIGHLIGHTS_NOME_ASC = "Nome Ascending" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHT = "Item" +COM_HIGHLIGHTS_LEGEND_HIGHLIGHT = "Item" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ID = "ID" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ID = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_STATE = "State" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_STATE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ORDERING = "Order" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ORDERING = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT_TIME = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CREATED_BY = "Created by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CREATED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_MODIFIED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ETICHETTA = "" +COM_HIGHLIGHTS_TAB_HIGHLIGHT = "Highlight" +COM_HIGHLIGHTS_FIELDSET_HIGHLIGHT = "Highlight" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TITOLO = "Titolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TITOLO = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_SOTTOTITOLO = "Sottotitolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_SOTTOTITOLO = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DESCRIZIONE = "Descrizione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DESCRIZIONE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINGUA = "Lingua" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINGUA = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINK_PULSANTE = "Link Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINK_PULSANTE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TESTO_PULSANTE = "Testo Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TESTO_PULSANTE = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA = "Data" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_MAIN = "Immagine Main" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_MAIN = "" +COM_HIGHLIGHTS_TAB_IMMAGINI = "Immagini" +COM_HIGHLIGHTS_FIELDSET_IMMAGINI = "Immagini" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_SECONDARIA = "Immagine Secondaria" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_SECONDARIA = "" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE = "Data Inizio Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE = "" +COM_HIGHLIGHTS_TAB_PUBBLICAZIONE = "Pubblicazione" +COM_HIGHLIGHTS_FIELDSET_PUBBLICAZIONE = "Pubblicazione" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE = "Data Fine Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE = "" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS_DESC = "Show a list of Highlights" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT = "Single Highlight" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT_DESC = "Show a specific Highlight" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT = "HighlightForm" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT_DESC = "Show a form to add or edit a Highlight" +COM_HIGHLIGHTS_TITLE_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_LEGEND_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ID = "ID" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_ID = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_STATE = "State" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_STATE = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ORDERING = "Order" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_ORDERING = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT = "N/A" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_CHECKED_OUT = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT_TIME = "N/A" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_CHECKED_OUT_TIME = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CREATED_BY = "Created by" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_CREATED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_MODIFIED_BY = "Modified by" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_MODIFIED_BY = "" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_NOME = "Nome" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_NOME = "" +COM_HIGHLIGHTS_TAB_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FIELDSET_ETICHETTA = "Etichetta" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_LINGUA = "Lingua" +COM_HIGHLIGHTS_FORM_DESC_ETICHETTA_LINGUA = "" + + + + diff --git a/administrator/language/it-IT/com_highlights.sys.ini b/administrator/language/it-IT/com_highlights.sys.ini new file mode 100644 index 00000000..08eba09f --- /dev/null +++ b/administrator/language/it-IT/com_highlights.sys.ini @@ -0,0 +1,62 @@ +COM_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_XML_DESCRIPTION = "" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHTS="Highlights" + +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS="Highlights" + +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS_DESC="Show a list of Highlights" + +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT="Single Highlight" + +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT_DESC="Show a specific Highlight" + +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT="HighlightForm" + +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT_DESC="Show a form to add or edit a Highlight" + +COM_HIGHLIGHTS_TITLE_ETICHETTE="Etichette" + +COM_HIGHLIGHTS_SHOW_TABLES_SQL_STATEMENT="SHOW FULL TABLES WHERE tables_in_%s LIKE %s" + +COM_HIGHLIGHTS_SHOW_COLUMNS_TABLE_SQL_STATEMENT="SHOW FULL COLUMNS FROM %s WHERE Field LIKE %s" + +COM_HIGHLIGHTS_CREATE_TABLE_SQL_STATEMENT="CREATE TABLE IF NOT EXISTS %s (%s)" + +COM_HIGHLIGHTS_CREATE_TABLE_COLUMN_DECLARATION_SQL_STATEMENT="%s %s NOT NULL %s %s %s" + +COM_HIGHLIGHTS_CREATE_TABLE_INDEX_SQL_STATEMENT="INDEX %s (%s ASC)" + +COM_HIGHLIGHTS_ADD_COLUMN_SQL_STATEMENT="ALTER TABLE %s ADD %s" + +COM_HIGHLIGHTS_RENAME_COLUMN_SQL_STATEMENT="ALTER TABLE %s CHANGE %s %s %s" + +COM_HIGHLIGHTS_CHANGE_COLUMN_TYPE_SQL_STATEMENT="ALTER TABLE %s MODIFY %s" + +COM_HIGHLIGHTS_DROP_COLUMN_SQL_STATEMENT="ALTER TABLE %s DROP COLUMN %s" + +COM_HIGHLIGHTS_IS_NOT_COMPATIBLE_JOOMLA_VERSION="This component is not compatible with installed Joomla version" + +COM_HIGHLIGHTS_SIMPLEXML_LOAD_FILE_FUNCTION_DOES_NOT_EXISTS="This script needs 'simplexml_load_file' to update the component" + +COM_HIGHLIGHTS_CREATING_TABLE_ACTION_COMPLETED_SUCCESFULLY="Table `%s` has been succesfully created" + +COM_HIGHLIGHTS_CREATING_TABLE_ACTION_AN_ERROR_OCCURRED="There was an error creating the table `%s`. Error: %s" + +COM_HIGHLIGHTS_RENAMING_TABLE_ACTION_COMPLETED_SUCCESFULLY="Table `%s` was succesfully renamed to `%s`" + +COM_HIGHLIGHTS_RENAMING_TABLE_ACTION_AN_ERROR_OCCURRED="There was an error renaming the table `%s`. Error: %s" + +COM_HIGHLIGHTS_DROPPING_TABLE_ACTION_COMPLETED_SUCCESSFULLY="Table `%s` was succesfully deleted" + +COM_HIGHLIGHTS_ADDING_FIELD_ACTION_COMPLETED_SUCCESSFULLY="Field `%s` has been succesfully added" + +COM_HIGHLIGHTS_ADDING_FIELD_ACTION_AN_ERROR_OCCURRED="There was an error adding the field `%s`. Error: %s" + +COM_HIGHLIGHTS_MODIFYING_FIELD_ACTION_COMPLETED_SUCCESSFULLY="Field `%s` has been succesfully modified" + +COM_HIGHLIGHTS_MODIFYING_FIELD_ACTION_AN_ERROR_OCCURRED="There was an error modifying the field `%s`. Error: %s" + +COM_HIGHLIGHTS_DROPPING_FIELD_ACTION_COMPLETED_SUCCESSFULLY="Field `%s` has been succesfully deleted" + +COM_HIGHLIGHTS_DROPPING_FIELD_ACTION_AN_ERROR_OCCURRED="There was an error deleting the field `%s`. Error: %s" diff --git a/administrator/language/it-IT/plg_finder_highlightshighlights.ini b/administrator/language/it-IT/plg_finder_highlightshighlights.ini new file mode 100644 index 00000000..23c532da --- /dev/null +++ b/administrator/language/it-IT/plg_finder_highlightshighlights.ini @@ -0,0 +1,4 @@ +PLG_FINDER_HIGHLIGHTS="Finder - Highlights" +PLG_FINDER_HIGHLIGHTS_XML_DESCRIPTION="" +PLG_FINDER_HIGHLIGHTS_FIELD_SEARCHLIMIT_DESC="Number of search items to return" +PLG_FINDER_HIGHLIGHTS_FIELD_SEARCHLIMIT_LABEL="Finder Limit" \ No newline at end of file diff --git a/administrator/language/it-IT/plg_finder_highlightshighlights.sys.ini b/administrator/language/it-IT/plg_finder_highlightshighlights.sys.ini new file mode 100644 index 00000000..7e63a805 --- /dev/null +++ b/administrator/language/it-IT/plg_finder_highlightshighlights.sys.ini @@ -0,0 +1,4 @@ +PLG_FINDER_HIGHLIGHTS="Finder - Highlights" +PLG_FINDER_COM_HIGHLIGHTS_XML_DESCRIPTION="" +PLG_FINDER_COM_HIGHLIGHTS_FIELD_SEARCHLIMIT_DESC="Number of search items to return" +PLG_FINDER_COM_HIGHLIGHTS_FIELD_SEARCHLIMIT_LABEL="Finder Limit" \ No newline at end of file diff --git a/administrator/logs/everything.php b/administrator/logs/everything.php index e590f5ea..6fe37847 100644 --- a/administrator/logs/everything.php +++ b/administrator/logs/everything.php @@ -354,3 +354,331 @@ 2024-12-21T11:13:38+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3159 Array ( [0] => 3142 [1] => 3159 ) 2024-12-21T11:13:38+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3159: 1 Array ( [0] => 3142 [1] => 3159 ) 2024-12-21T11:14:22+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3159 Array ( [0] => 3142 ) +2024-12-30T07:45:47+00:00 INFO 172.21.0.6 task4 Esecuzione attività#04 'Delete Action Logs'. +2024-12-30T07:45:47+00:00 INFO 172.21.0.6 task4 Attività> Delete Logs after 15 days +2024-12-30T07:45:47+00:00 INFO 172.21.0.6 task4 Attività> Delete Logs end +2024-12-30T07:45:47+00:00 INFO 172.21.0.6 task4 Attività terminata con successo#04 in 0.01 (rete 0.02) secondi. +2024-12-30T07:45:53+00:00 INFO 172.21.0.6 task2 Esecuzione attività#02 'Session GC'. +2024-12-30T07:45:53+00:00 INFO 172.21.0.6 task2 Attività> SessionGC end +2024-12-30T07:45:53+00:00 INFO 172.21.0.6 task2 Attività terminata con successo#02 in 0.01 (rete 0.01) secondi. +2024-12-30T07:46:05+00:00 INFO 172.21.0.6 task3 Esecuzione attività#03 'Update Notification'. +2024-12-30T07:46:06+00:00 INFO 172.21.0.6 task3 Attività terminata con successo#03 in 0.78 (rete 0.78) secondi. +2024-12-30T08:30:35+00:00 WARNING 172.21.0.6 assets No asset found for com_modules.module.557, falling back to com_modules +2024-12-30T08:30:35+00:00 WARNING 172.21.0.6 assets No asset found for com_modules.module.557, falling back to com_modules +2024-12-30T08:30:41+00:00 INFO 172.21.0.6 updater Loading information from update site #3 with name "Accredited Joomla! Translations" and URL https://update.joomla.org/language/translationlist_5.xml took 0.12 seconds +2024-12-30T08:30:41+00:00 INFO 172.21.0.6 updater Loading information from update site #4 with name "Joomla! Update Component" and URL https://update.joomla.org/core/extensions/com_joomlaupdate.xml took 0.09 seconds +2024-12-30T08:30:41+00:00 INFO 172.21.0.6 updater Loading information from update site #216 with name "Regular Labs - Conditional Content" and URL https://download.regularlabs.com/updates.xml?e=conditionalcontent&type=.xml took 0.29 seconds +2024-12-30T08:30:41+00:00 INFO 172.21.0.6 updater Loading information from update site #220 with name "Regular Labs - Articles" and URL https://download.regularlabs.com/updates.xml?e=articlesfield&type=.xml took 0.22 seconds +2024-12-30T08:30:41+00:00 INFO 172.21.0.6 updater Loading information from update site #223 with name "COM_PHOCADOWNLOAD" and URL https://raw.githubusercontent.com/PhocaCz/PhocaDownload/master/manifest.xml took 0.19 seconds +2024-12-30T08:30:42+00:00 INFO 172.21.0.6 updater Loading information from update site #224 with name "COM_PHOCAGALLERY" and URL https://raw.githubusercontent.com/PhocaCz/PhocaGallery/master/manifest.xml took 0.17 seconds +2024-12-30T08:30:42+00:00 INFO 172.21.0.6 updater Loading information from update site #226 with name "Tabulizer.com" and URL http://www.tabulizer.com/update/tabulizer-update.xml took 0.22 seconds +2024-12-30T08:30:42+00:00 INFO 172.21.0.6 updater Loading information from update site #233 with name "Akeeba FEF" and URL http://cdn.akeebabackup.com/updates/fef.xml took 0.06 seconds +2024-12-30T08:30:42+00:00 INFO 172.21.0.6 updater Loading information from update site #238 with name "FOF 3.x" and URL http://cdn.akeebabackup.com/updates/fof3.xml took 0.04 seconds +2024-12-30T08:30:42+00:00 INFO 172.21.0.6 updater Loading information from update site #242 with name "JEM Update Site" and URL http://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.12 seconds +2024-12-30T08:30:43+00:00 INFO 172.21.0.6 updater Loading information from update site #243 with name "JL Content Fields Filter" and URL https://joomline.net/update.html?extension_id=5.xml took 0.42 seconds +2024-12-30T08:30:43+00:00 INFO 172.21.0.6 updater Loading information from update site #285 with name "Akeeba FEF" and URL http://cdn.akeeba.com/updates/fef.xml took 0.06 seconds +2024-12-30T08:30:43+00:00 INFO 172.21.0.6 updater Loading information from update site #313 with name "Regular Labs Library" and URL https://download.regularlabs.com/updates.xml?e=library&type=.xml took 0.19 seconds +2024-12-30T08:30:43+00:00 INFO 172.21.0.6 updater Loading information from update site #347 with name "COM_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMaps/master/manifest.xml took 0.17 seconds +2024-12-30T08:30:43+00:00 INFO 172.21.0.6 updater Loading information from update site #348 with name "PLG_CONTENT_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMapsPlugin/master/manifest.xml took 0.18 seconds +2024-12-30T08:30:44+00:00 INFO 172.21.0.6 updater Loading information from update site #398 with name "plg_captcha_hcaptcha" and URL https://data2site.com/updates/hcaptcha took 0.35 seconds +2024-12-30T08:30:44+00:00 INFO 172.21.0.6 updater Loading information from update site #404 with name "OSMap Free" and URL https://deploy.ostraining.com/client/update/free/stable/com_osmap took 0.20 seconds +2024-12-30T08:30:44+00:00 INFO 172.21.0.6 updater Loading information from update site #406 with name "Joomlashack Extension Support" and URL https://deploy.ostraining.com/client/update/free/stable/plg_system_osmylicensesmanager took 0.27 seconds +2024-12-30T08:30:44+00:00 INFO 172.21.0.6 updater Loading information from update site #407 with name "Joomlashack Framework" and URL https://deploy.ostraining.com/client/update/free/stable/lib_allediaframework took 0.29 seconds +2024-12-30T08:30:44+00:00 INFO 172.21.0.6 updater Loading information from update site #408 with name "Search Update Site" and URL https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml took 0.08 seconds +2024-12-30T08:30:45+00:00 INFO 172.21.0.6 updater Loading information from update site #409 with name "Regular Labs - Conditions" and URL https://download.regularlabs.com/updates.xml?e=conditions&type=.xml took 0.16 seconds +2024-12-30T08:30:45+00:00 INFO 172.21.0.6 updater Loading information from update site #410 with name "JEM Update Site" and URL https://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.14 seconds +2024-12-30T08:30:45+00:00 INFO 172.21.0.6 updater Loading information from update site #411 with name "Multilanguages CK Update" and URL https://update.joomlack.fr/multilanguagesck_light_update.xml took 0.41 seconds +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:58:48+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.2530 Array ( [0] => 2530 ) +2024-12-30T08:59:03+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.2530 Array ( ) +2024-12-30T09:08:13+00:00 INFO 172.21.0.6 controller Holding edit ID com_categories.edit.category.154 Array ( [0] => 154 ) +2024-12-30T09:08:13+00:00 INFO 172.21.0.6 controller Checking edit ID com_categories.edit.category.154: 1 Array ( [0] => 154 ) +2024-12-30T09:08:22+00:00 INFO 172.21.0.6 controller Releasing edit ID com_categories.edit.category.154 Array ( ) +2024-12-30T09:25:03+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T09:25:11+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T09:25:15+00:00 INFO 172.21.0.6 controller Holding edit ID com_categories.edit.category.154 Array ( [0] => 154 ) +2024-12-30T09:25:16+00:00 ERROR 172.21.0.6 jerror Il file di configurazione delle ACL del componente manca o è strutturato impropriamente. +2024-12-30T09:25:16+00:00 WARNING 172.21.0.6 jerror L'attributo dell'estensione è vuoto nel campo categoria +2024-12-30T09:25:45+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T09:34:51+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.325 Array ( ) +2024-12-30T09:35:53+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T10:28:20+00:00 CRITICAL 172.21.0.6 error Uncaught Throwable of type Joomla\CMS\Router\Exception\RouteNotFoundException thrown with message "Pagina non trovata". Stack trace: #0 [ROOT]/libraries/src/Application/SiteApplication.php(754): Joomla\CMS\Router\Router->parse(Object(Joomla\CMS\Uri\Uri), true) +#1 [ROOT]/libraries/src/Application/SiteApplication.php(244): Joomla\CMS\Application\SiteApplication->route() +#2 [ROOT]/libraries/src/Application/CMSApplication.php(306): Joomla\CMS\Application\SiteApplication->doExecute() +#3 [ROOT]/includes/app.php(58): Joomla\CMS\Application\CMSApplication->execute() +#4 [ROOT]/index.php(32): require_once('[ROOT]/i...') +#5 {main} +2024-12-30T10:29:45+00:00 CRITICAL 172.21.0.6 error Uncaught Throwable of type Joomla\CMS\Router\Exception\RouteNotFoundException thrown with message "Pagina non trovata". Stack trace: #0 [ROOT]/libraries/src/Application/SiteApplication.php(754): Joomla\CMS\Router\Router->parse(Object(Joomla\CMS\Uri\Uri), true) +#1 [ROOT]/libraries/src/Application/SiteApplication.php(244): Joomla\CMS\Application\SiteApplication->route() +#2 [ROOT]/libraries/src/Application/CMSApplication.php(306): Joomla\CMS\Application\SiteApplication->doExecute() +#3 [ROOT]/includes/app.php(58): Joomla\CMS\Application\CMSApplication->execute() +#4 [ROOT]/index.php(32): require_once('[ROOT]/i...') +#5 {main} +2024-12-30T13:24:49+00:00 INFO 172.21.0.6 updater Loading information from update site #3 with name "Accredited Joomla! Translations" and URL https://update.joomla.org/language/translationlist_5.xml took 0.10 seconds +2024-12-30T13:24:50+00:00 INFO 172.21.0.6 updater Loading information from update site #4 with name "Joomla! Update Component" and URL https://update.joomla.org/core/extensions/com_joomlaupdate.xml took 0.10 seconds +2024-12-30T13:24:50+00:00 INFO 172.21.0.6 updater Loading information from update site #216 with name "Regular Labs - Conditional Content" and URL https://download.regularlabs.com/updates.xml?e=conditionalcontent&type=.xml took 0.28 seconds +2024-12-30T13:24:50+00:00 INFO 172.21.0.6 updater Loading information from update site #220 with name "Regular Labs - Articles" and URL https://download.regularlabs.com/updates.xml?e=articlesfield&type=.xml took 0.21 seconds +2024-12-30T13:24:50+00:00 INFO 172.21.0.6 updater Loading information from update site #223 with name "COM_PHOCADOWNLOAD" and URL https://raw.githubusercontent.com/PhocaCz/PhocaDownload/master/manifest.xml took 0.25 seconds +2024-12-30T13:24:50+00:00 INFO 172.21.0.6 updater Loading information from update site #224 with name "COM_PHOCAGALLERY" and URL https://raw.githubusercontent.com/PhocaCz/PhocaGallery/master/manifest.xml took 0.08 seconds +2024-12-30T13:24:51+00:00 INFO 172.21.0.6 updater Loading information from update site #226 with name "Tabulizer.com" and URL http://www.tabulizer.com/update/tabulizer-update.xml took 0.26 seconds +2024-12-30T13:24:51+00:00 INFO 172.21.0.6 updater Loading information from update site #233 with name "Akeeba FEF" and URL http://cdn.akeebabackup.com/updates/fef.xml took 0.07 seconds +2024-12-30T13:24:51+00:00 INFO 172.21.0.6 updater Loading information from update site #238 with name "FOF 3.x" and URL http://cdn.akeebabackup.com/updates/fof3.xml took 0.04 seconds +2024-12-30T13:24:51+00:00 INFO 172.21.0.6 updater Loading information from update site #242 with name "JEM Update Site" and URL http://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.12 seconds +2024-12-30T13:24:51+00:00 INFO 172.21.0.6 updater Loading information from update site #243 with name "JL Content Fields Filter" and URL https://joomline.net/update.html?extension_id=5.xml took 0.49 seconds +2024-12-30T13:24:52+00:00 INFO 172.21.0.6 updater Loading information from update site #285 with name "Akeeba FEF" and URL http://cdn.akeeba.com/updates/fef.xml took 0.08 seconds +2024-12-30T13:24:52+00:00 INFO 172.21.0.6 updater Loading information from update site #313 with name "Regular Labs Library" and URL https://download.regularlabs.com/updates.xml?e=library&type=.xml took 0.34 seconds +2024-12-30T13:24:52+00:00 INFO 172.21.0.6 updater Loading information from update site #347 with name "COM_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMaps/master/manifest.xml took 0.24 seconds +2024-12-30T13:24:52+00:00 INFO 172.21.0.6 updater Loading information from update site #348 with name "PLG_CONTENT_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMapsPlugin/master/manifest.xml took 0.29 seconds +2024-12-30T13:24:53+00:00 INFO 172.21.0.6 updater Loading information from update site #398 with name "plg_captcha_hcaptcha" and URL https://data2site.com/updates/hcaptcha took 0.32 seconds +2024-12-30T13:24:53+00:00 INFO 172.21.0.6 updater Loading information from update site #404 with name "OSMap Free" and URL https://deploy.ostraining.com/client/update/free/stable/com_osmap took 0.35 seconds +2024-12-30T13:24:53+00:00 INFO 172.21.0.6 updater Loading information from update site #406 with name "Joomlashack Extension Support" and URL https://deploy.ostraining.com/client/update/free/stable/plg_system_osmylicensesmanager took 0.29 seconds +2024-12-30T13:24:54+00:00 INFO 172.21.0.6 updater Loading information from update site #407 with name "Joomlashack Framework" and URL https://deploy.ostraining.com/client/update/free/stable/lib_allediaframework took 0.36 seconds +2024-12-30T13:24:54+00:00 INFO 172.21.0.6 updater Loading information from update site #408 with name "Search Update Site" and URL https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml took 0.10 seconds +2024-12-30T13:24:54+00:00 INFO 172.21.0.6 updater Loading information from update site #409 with name "Regular Labs - Conditions" and URL https://download.regularlabs.com/updates.xml?e=conditions&type=.xml took 0.21 seconds +2024-12-30T13:24:54+00:00 INFO 172.21.0.6 updater Loading information from update site #410 with name "JEM Update Site" and URL https://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.13 seconds +2024-12-30T13:24:56+00:00 INFO 172.21.0.6 updater Loading information from update site #411 with name "Multilanguages CK Update" and URL https://update.joomlack.fr/multilanguagesck_light_update.xml took 2.11 seconds +2024-12-30T13:55:02+00:00 INFO 172.21.0.6 updater Loading information from update site #413 with name "Advanced Custom Fields" and URL https://www.tassos.gr/updates/advanced-custom-fields-pro.xml took 0.16 seconds +2024-12-30T13:55:34+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.6693 Array ( [0] => 6693 ) +2024-12-30T13:55:34+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.6693: 1 Array ( [0] => 6693 ) +2024-12-30T13:55:56+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.6693 Array ( ) +2024-12-30T13:57:34+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.group.53 Array ( [0] => 53 ) +2024-12-30T13:57:40+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.group.53 Array ( ) +2024-12-30T14:00:46+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:00:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:00:58+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.769 Array ( ) +2024-12-30T14:01:06+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.6693 Array ( [0] => 6693 ) +2024-12-30T14:01:06+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.6693: 1 Array ( [0] => 6693 ) +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: Sending with mail() +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: Sendmail path: /usr/local/bin/mailpit sendmail -t --smtp-addr 127.0.0.1:1025 +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: Envelope sender: +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: To: "webmaster@conservatorio.udine.it" +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: Subject: Ultime azioni degli utenti +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: Headers: Date: Mon, 30 Dec 2024 14:01:56 +0000 +From: Conservatorio Statale di Musica Jacopo Tomadini - Udine +Message-ID: +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + + +2024-12-30T14:01:56+00:00 ERROR 172.21.0.6 mail Error in Mail API: Result: true +2024-12-30T14:01:56+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.6693 Array ( [0] => 6693 ) +2024-12-30T14:01:56+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.6693: 1 Array ( [0] => 6693 ) +2024-12-30T14:02:11+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.6693 Array ( ) +2024-12-30T14:02:18+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:02:18+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:02:22+00:00 CRITICAL 172.21.0.6 error Uncaught Throwable of type Joomla\CMS\Router\Exception\RouteNotFoundException thrown with message "Pagina non trovata". Stack trace: #0 [ROOT]/libraries/src/Application/SiteApplication.php(754): Joomla\CMS\Router\Router->parse(Object(Joomla\CMS\Uri\Uri), true) +#1 [ROOT]/libraries/src/Application/SiteApplication.php(244): Joomla\CMS\Application\SiteApplication->route() +#2 [ROOT]/libraries/src/Application/CMSApplication.php(306): Joomla\CMS\Application\SiteApplication->doExecute() +#3 [ROOT]/includes/app.php(58): Joomla\CMS\Application\CMSApplication->execute() +#4 [ROOT]/index.php(32): require_once('[ROOT]/i...') +#5 {main} +2024-12-30T14:02:32+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:02:32+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:02:55+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.769 Array ( ) +2024-12-30T14:03:56+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:03:56+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:04:11+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:04:11+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:09:28+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.325 Array ( ) +2024-12-30T14:10:39+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:10:39+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:11:24+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:11:25+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:12:28+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.769 Array ( ) +2024-12-30T14:29:36+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T14:29:36+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T14:29:51+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T14:32:39+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T14:32:39+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T14:33:16+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T14:37:25+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:37:26+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:38:11+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:38:29+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.431 Array ( [0] => 431 ) +2024-12-30T14:38:29+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.431: 1 Array ( [0] => 431 ) +2024-12-30T14:38:38+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.431 Array ( ) +2024-12-30T14:38:45+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:38:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:38:54+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:39:09+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.431 Array ( [0] => 431 ) +2024-12-30T14:39:10+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.431: 1 Array ( [0] => 431 ) +2024-12-30T14:40:23+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.431 Array ( ) +2024-12-30T14:40:30+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:40:30+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:40:38+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.577 Array ( [0] => 20 [1] => 577 ) +2024-12-30T14:40:39+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.577: 1 Array ( [0] => 20 [1] => 577 ) +2024-12-30T14:40:46+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.577 Array ( [0] => 20 ) +2024-12-30T14:40:50+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:40:51+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:41:03+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:41:07+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:41:07+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:41:16+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:42:26+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:42:45+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T14:43:15+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:43:24+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T14:45:13+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:45:32+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:46:13+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T14:47:29+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T14:47:29+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T14:48:10+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:03:50+00:00 INFO 172.21.0.6 controller Holding edit ID com_categories.edit.category.212 Array ( [0] => 154 [1] => 212 ) +2024-12-30T15:03:50+00:00 INFO 172.21.0.6 controller Checking edit ID com_categories.edit.category.212: 1 Array ( [0] => 154 [1] => 212 ) +2024-12-30T15:04:30+00:00 INFO 172.21.0.6 controller Releasing edit ID com_categories.edit.category.212 Array ( [0] => 154 ) +2024-12-30T15:04:47+00:00 CRITICAL 172.21.0.6 error Uncaught Throwable of type Joomla\CMS\Router\Exception\RouteNotFoundException thrown with message "Pagina non trovata". Stack trace: #0 [ROOT]/libraries/src/Application/SiteApplication.php(754): Joomla\CMS\Router\Router->parse(Object(Joomla\CMS\Uri\Uri), true) +#1 [ROOT]/libraries/src/Application/SiteApplication.php(244): Joomla\CMS\Application\SiteApplication->route() +#2 [ROOT]/libraries/src/Application/CMSApplication.php(306): Joomla\CMS\Application\SiteApplication->doExecute() +#3 [ROOT]/includes/app.php(58): Joomla\CMS\Application\CMSApplication->execute() +#4 [ROOT]/index.php(32): require_once('[ROOT]/i...') +#5 {main} +2024-12-30T15:05:33+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.178 Array ( [0] => 178 ) +2024-12-30T15:05:33+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.178: 1 Array ( [0] => 178 ) +2024-12-30T15:05:52+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.178 Array ( [0] => 178 ) +2024-12-30T15:05:52+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.178: 1 Array ( [0] => 178 ) +2024-12-30T15:06:13+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.178 Array ( ) +2024-12-30T15:06:35+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.293 Array ( [0] => 293 ) +2024-12-30T15:06:35+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.293: 1 Array ( [0] => 293 ) +2024-12-30T15:06:48+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.293 Array ( ) +2024-12-30T15:12:10+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T15:12:26+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T15:13:18+00:00 CRITICAL 172.21.0.6 error Uncaught Throwable of type Joomla\CMS\Component\Exception\MissingComponentException thrown with message "Componente non trovato". Stack trace: #0 [ROOT]/libraries/src/Application/SiteApplication.php(218): Joomla\CMS\Component\ComponentHelper::renderComponent(NULL) +#1 [ROOT]/libraries/src/Application/SiteApplication.php(261): Joomla\CMS\Application\SiteApplication->dispatch() +#2 [ROOT]/libraries/src/Application/CMSApplication.php(306): Joomla\CMS\Application\SiteApplication->doExecute() +#3 [ROOT]/includes/app.php(58): Joomla\CMS\Application\CMSApplication->execute() +#4 [ROOT]/index.php(32): require_once('[ROOT]/i...') +#5 {main} +2024-12-30T15:15:54+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:15:55+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:16:08+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:31:07+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:31:07+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:31:24+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:32:45+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:32:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:33:00+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:33:01+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:52:59+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:59:10+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:59:11+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:59:30+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:59:30+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T16:52:44+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T16:52:57+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3189 Array ( [0] => 3189 ) +2024-12-30T16:52:57+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3189: 1 Array ( [0] => 3189 ) +2024-12-30T16:53:35+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3189 Array ( ) +2024-12-30T16:53:37+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3189 Array ( [0] => 3189 ) +2024-12-30T16:53:38+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3189: 1 Array ( [0] => 3189 ) +2024-12-30T16:54:48+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3189 Array ( ) +2024-12-30T16:56:17+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3189 Array ( [0] => 3189 ) +2024-12-30T16:56:17+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3189: 1 Array ( [0] => 3189 ) +2024-12-30T16:56:31+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3189 Array ( ) +2024-12-30T16:56:57+00:00 INFO 172.21.0.6 updater Loading information from update site #3 with name "Accredited Joomla! Translations" and URL https://update.joomla.org/language/translationlist_5.xml took 0.10 seconds +2024-12-30T16:56:57+00:00 INFO 172.21.0.6 updater Loading information from update site #4 with name "Joomla! Update Component" and URL https://update.joomla.org/core/extensions/com_joomlaupdate.xml took 0.11 seconds +2024-12-30T16:56:58+00:00 INFO 172.21.0.6 updater Loading information from update site #216 with name "Regular Labs - Conditional Content" and URL https://download.regularlabs.com/updates.xml?e=conditionalcontent&type=.xml took 0.29 seconds +2024-12-30T16:56:58+00:00 INFO 172.21.0.6 updater Loading information from update site #220 with name "Regular Labs - Articles" and URL https://download.regularlabs.com/updates.xml?e=articlesfield&type=.xml took 0.26 seconds +2024-12-30T16:56:58+00:00 INFO 172.21.0.6 updater Loading information from update site #223 with name "COM_PHOCADOWNLOAD" and URL https://raw.githubusercontent.com/PhocaCz/PhocaDownload/master/manifest.xml took 0.10 seconds +2024-12-30T16:56:58+00:00 INFO 172.21.0.6 updater Loading information from update site #224 with name "COM_PHOCAGALLERY" and URL https://raw.githubusercontent.com/PhocaCz/PhocaGallery/master/manifest.xml took 0.20 seconds +2024-12-30T16:56:59+00:00 INFO 172.21.0.6 updater Loading information from update site #226 with name "Tabulizer.com" and URL http://www.tabulizer.com/update/tabulizer-update.xml took 1.25 seconds +2024-12-30T16:57:00+00:00 INFO 172.21.0.6 updater Loading information from update site #233 with name "Akeeba FEF" and URL http://cdn.akeebabackup.com/updates/fef.xml took 0.07 seconds +2024-12-30T16:57:00+00:00 INFO 172.21.0.6 updater Loading information from update site #238 with name "FOF 3.x" and URL http://cdn.akeebabackup.com/updates/fof3.xml took 0.05 seconds +2024-12-30T16:57:00+00:00 INFO 172.21.0.6 updater Loading information from update site #242 with name "JEM Update Site" and URL http://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.15 seconds +2024-12-30T16:57:00+00:00 INFO 172.21.0.6 updater Loading information from update site #243 with name "JL Content Fields Filter" and URL https://joomline.net/update.html?extension_id=5.xml took 0.53 seconds +2024-12-30T16:57:00+00:00 INFO 172.21.0.6 updater Loading information from update site #285 with name "Akeeba FEF" and URL http://cdn.akeeba.com/updates/fef.xml took 0.07 seconds +2024-12-30T16:57:01+00:00 INFO 172.21.0.6 updater Loading information from update site #313 with name "Regular Labs Library" and URL https://download.regularlabs.com/updates.xml?e=library&type=.xml took 0.17 seconds +2024-12-30T16:57:01+00:00 INFO 172.21.0.6 updater Loading information from update site #347 with name "COM_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMaps/master/manifest.xml took 0.20 seconds +2024-12-30T16:57:01+00:00 INFO 172.21.0.6 updater Loading information from update site #348 with name "PLG_CONTENT_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMapsPlugin/master/manifest.xml took 0.23 seconds +2024-12-30T16:57:01+00:00 INFO 172.21.0.6 updater Loading information from update site #398 with name "plg_captcha_hcaptcha" and URL https://data2site.com/updates/hcaptcha took 0.30 seconds +2024-12-30T16:57:02+00:00 INFO 172.21.0.6 updater Loading information from update site #404 with name "OSMap Free" and URL https://deploy.ostraining.com/client/update/free/stable/com_osmap took 0.32 seconds +2024-12-30T16:57:02+00:00 INFO 172.21.0.6 updater Loading information from update site #406 with name "Joomlashack Extension Support" and URL https://deploy.ostraining.com/client/update/free/stable/plg_system_osmylicensesmanager took 0.30 seconds +2024-12-30T16:57:02+00:00 INFO 172.21.0.6 updater Loading information from update site #407 with name "Joomlashack Framework" and URL https://deploy.ostraining.com/client/update/free/stable/lib_allediaframework took 0.24 seconds +2024-12-30T16:57:02+00:00 INFO 172.21.0.6 updater Loading information from update site #408 with name "Search Update Site" and URL https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml took 0.10 seconds +2024-12-30T16:57:03+00:00 INFO 172.21.0.6 updater Loading information from update site #409 with name "Regular Labs - Conditions" and URL https://download.regularlabs.com/updates.xml?e=conditions&type=.xml took 0.19 seconds +2024-12-30T16:57:03+00:00 INFO 172.21.0.6 updater Loading information from update site #410 with name "JEM Update Site" and URL https://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.19 seconds +2024-12-30T16:57:04+00:00 INFO 172.21.0.6 updater Loading information from update site #411 with name "Multilanguages CK Update" and URL https://update.joomlack.fr/multilanguagesck_light_update.xml took 0.77 seconds +2024-12-30T16:57:04+00:00 INFO 172.21.0.6 updater Loading information from update site #413 with name "Advanced Custom Fields" and URL https://www.tassos.gr/updates/advanced-custom-fields-pro.xml took 0.12 seconds +2024-12-30T16:57:12+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.16 Array ( [0] => 16 ) +2024-12-30T16:57:13+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.16: 1 Array ( [0] => 16 ) +2024-12-30T16:57:23+00:00 CRITICAL 172.21.0.6 error Uncaught Throwable of type Joomla\CMS\Router\Exception\RouteNotFoundException thrown with message "Pagina non trovata". Stack trace: #0 [ROOT]/libraries/src/Application/SiteApplication.php(754): Joomla\CMS\Router\Router->parse(Object(Joomla\CMS\Uri\Uri), true) +#1 [ROOT]/libraries/src/Application/SiteApplication.php(244): Joomla\CMS\Application\SiteApplication->route() +#2 [ROOT]/libraries/src/Application/CMSApplication.php(306): Joomla\CMS\Application\SiteApplication->doExecute() +#3 [ROOT]/includes/app.php(58): Joomla\CMS\Application\CMSApplication->execute() +#4 [ROOT]/index.php(32): require_once('[ROOT]/i...') +#5 {main} +2024-12-30T16:57:41+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.16 Array ( [0] => 16 ) +2024-12-30T16:57:41+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.16: 1 Array ( [0] => 16 ) +2024-12-30T18:10:25+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.16 Array ( ) +2024-12-31T07:24:40+00:00 WARNING 172.21.0.6 assets No asset found for com_modules.module.557, falling back to com_modules +2024-12-31T07:24:40+00:00 WARNING 172.21.0.6 assets No asset found for com_modules.module.557, falling back to com_modules +2024-12-31T07:46:06+00:00 INFO 172.21.0.6 task4 Esecuzione attività#04 'Delete Action Logs'. +2024-12-31T07:46:06+00:00 INFO 172.21.0.6 task4 Attività> Delete Logs after 15 days +2024-12-31T07:46:06+00:00 INFO 172.21.0.6 task4 Attività> Delete Logs end +2024-12-31T07:46:06+00:00 INFO 172.21.0.6 task4 Attività terminata con successo#04 in 0.02 (rete 0.02) secondi. +2024-12-31T07:51:07+00:00 INFO 172.21.0.6 task2 Esecuzione attività#02 'Session GC'. +2024-12-31T07:51:07+00:00 INFO 172.21.0.6 task2 Attività> SessionGC end +2024-12-31T07:51:07+00:00 INFO 172.21.0.6 task2 Attività terminata con successo#02 in 0.01 (rete 0.01) secondi. +2024-12-31T07:56:07+00:00 INFO 172.21.0.6 task3 Esecuzione attività#03 'Update Notification'. +2024-12-31T07:56:07+00:00 INFO 172.21.0.6 task3 Attività terminata con successo#03 in 0.53 (rete 0.53) secondi. +2024-12-31T08:11:53+00:00 WARNING 172.21.0.6 assets No asset found for com_modules.module.557, falling back to com_modules +2024-12-31T08:11:53+00:00 WARNING 172.21.0.6 assets No asset found for com_modules.module.557, falling back to com_modules +2024-12-31T08:12:08+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.2384 Array ( [0] => 2384 ) +2024-12-31T08:12:15+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.2384 Array ( ) +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 webauthn.system Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:21:58+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:21:58+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:22:28+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:22:29+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:22:50+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:22:50+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:23:14+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:23:15+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:37:51+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.556 Array ( ) +2024-12-31T08:38:01+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.176 Array ( [0] => 176 ) +2024-12-31T08:38:08+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.176 Array ( ) +2024-12-31T08:38:18+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.317 Array ( [0] => 317 ) +2024-12-31T08:38:43+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.317 Array ( [0] => 317 ) +2024-12-31T09:31:22+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.317 Array ( ) +2024-12-31T09:39:00+00:00 INFO 172.21.0.6 updater Loading information from update site #3 with name "Accredited Joomla! Translations" and URL https://update.joomla.org/language/translationlist_5.xml took 0.10 seconds +2024-12-31T09:39:00+00:00 INFO 172.21.0.6 updater Loading information from update site #4 with name "Joomla! Update Component" and URL https://update.joomla.org/core/extensions/com_joomlaupdate.xml took 0.09 seconds +2024-12-31T09:39:01+00:00 INFO 172.21.0.6 updater Loading information from update site #216 with name "Regular Labs - Conditional Content" and URL https://download.regularlabs.com/updates.xml?e=conditionalcontent&type=.xml took 0.31 seconds +2024-12-31T09:39:01+00:00 INFO 172.21.0.6 updater Loading information from update site #220 with name "Regular Labs - Articles" and URL https://download.regularlabs.com/updates.xml?e=articlesfield&type=.xml took 0.62 seconds +2024-12-31T09:39:02+00:00 INFO 172.21.0.6 updater Loading information from update site #223 with name "COM_PHOCADOWNLOAD" and URL https://raw.githubusercontent.com/PhocaCz/PhocaDownload/master/manifest.xml took 0.14 seconds +2024-12-31T09:39:02+00:00 INFO 172.21.0.6 updater Loading information from update site #224 with name "COM_PHOCAGALLERY" and URL https://raw.githubusercontent.com/PhocaCz/PhocaGallery/master/manifest.xml took 0.23 seconds +2024-12-31T09:39:02+00:00 INFO 172.21.0.6 updater Loading information from update site #226 with name "Tabulizer.com" and URL http://www.tabulizer.com/update/tabulizer-update.xml took 0.27 seconds +2024-12-31T09:39:02+00:00 INFO 172.21.0.6 updater Loading information from update site #233 with name "Akeeba FEF" and URL http://cdn.akeebabackup.com/updates/fef.xml took 0.08 seconds +2024-12-31T09:39:02+00:00 INFO 172.21.0.6 updater Loading information from update site #238 with name "FOF 3.x" and URL http://cdn.akeebabackup.com/updates/fof3.xml took 0.06 seconds +2024-12-31T09:39:02+00:00 INFO 172.21.0.6 updater Loading information from update site #242 with name "JEM Update Site" and URL http://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.15 seconds +2024-12-31T09:39:03+00:00 INFO 172.21.0.6 updater Loading information from update site #243 with name "JL Content Fields Filter" and URL https://joomline.net/update.html?extension_id=5.xml took 0.54 seconds +2024-12-31T09:39:03+00:00 INFO 172.21.0.6 updater Loading information from update site #285 with name "Akeeba FEF" and URL http://cdn.akeeba.com/updates/fef.xml took 0.09 seconds +2024-12-31T09:39:03+00:00 INFO 172.21.0.6 updater Loading information from update site #313 with name "Regular Labs Library" and URL https://download.regularlabs.com/updates.xml?e=library&type=.xml took 0.29 seconds +2024-12-31T09:39:04+00:00 INFO 172.21.0.6 updater Loading information from update site #347 with name "COM_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMaps/master/manifest.xml took 0.20 seconds +2024-12-31T09:39:04+00:00 INFO 172.21.0.6 updater Loading information from update site #348 with name "PLG_CONTENT_PHOCAMAPS" and URL https://raw.githubusercontent.com/PhocaCz/PhocaMapsPlugin/master/manifest.xml took 0.26 seconds +2024-12-31T09:39:04+00:00 INFO 172.21.0.6 updater Loading information from update site #398 with name "plg_captcha_hcaptcha" and URL https://data2site.com/updates/hcaptcha took 0.35 seconds +2024-12-31T09:39:05+00:00 INFO 172.21.0.6 updater Loading information from update site #404 with name "OSMap Free" and URL https://deploy.ostraining.com/client/update/free/stable/com_osmap took 0.31 seconds +2024-12-31T09:39:05+00:00 INFO 172.21.0.6 updater Loading information from update site #406 with name "Joomlashack Extension Support" and URL https://deploy.ostraining.com/client/update/free/stable/plg_system_osmylicensesmanager took 0.29 seconds +2024-12-31T09:39:05+00:00 INFO 172.21.0.6 updater Loading information from update site #407 with name "Joomlashack Framework" and URL https://deploy.ostraining.com/client/update/free/stable/lib_allediaframework took 0.28 seconds +2024-12-31T09:39:05+00:00 INFO 172.21.0.6 updater Loading information from update site #408 with name "Search Update Site" and URL https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml took 0.08 seconds +2024-12-31T09:39:05+00:00 INFO 172.21.0.6 updater Loading information from update site #409 with name "Regular Labs - Conditions" and URL https://download.regularlabs.com/updates.xml?e=conditions&type=.xml took 0.24 seconds +2024-12-31T09:39:06+00:00 INFO 172.21.0.6 updater Loading information from update site #410 with name "JEM Update Site" and URL https://www.joomlaeventmanager.net/updatecheck/update_pkg_jem.xml took 0.12 seconds +2024-12-31T09:39:07+00:00 INFO 172.21.0.6 updater Loading information from update site #411 with name "Multilanguages CK Update" and URL https://update.joomlack.fr/multilanguagesck_light_update.xml took 1.04 seconds +2024-12-31T09:39:07+00:00 INFO 172.21.0.6 updater Loading information from update site #413 with name "Advanced Custom Fields" and URL https://www.tassos.gr/updates/advanced-custom-fields-pro.xml took 0.13 seconds +2024-12-31T09:46:08+00:00 INFO 172.21.0.6 updater Loading information from update site #414 with name "com_highlights" and URL https://www.component-creator.com/index.php?task=builder.preupdatecheckhook&option=com_combuilder&component=NzY0NzgtMjEzOTAw took 0.70 seconds +2024-12-31T09:46:08+00:00 INFO 172.21.0.6 updater Loading information from update site #415 with name "com_highlights" and URL https://nocdn.component-creator.com/index.php?task=builder.preupdatecheckhook&option=com_combuilder&component=NzY0NzgtMjEzOTAw took 0.40 seconds +2024-12-31T09:46:15+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.4097 Array ( [0] => 4097 ) +2024-12-31T09:46:15+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.4097: 1 Array ( [0] => 4097 ) +2024-12-31T09:47:08+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.4097: 1 Array ( [0] => 4097 ) +2024-12-31T09:49:32+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.4097 Array ( ) +2024-12-31T09:56:46+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.4097 Array ( [0] => 4097 ) +2024-12-31T09:56:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.4097: 1 Array ( [0] => 4097 ) diff --git a/administrator/logs/jcontroller.log.php b/administrator/logs/jcontroller.log.php index 77c0e488..a802bcf4 100644 --- a/administrator/logs/jcontroller.log.php +++ b/administrator/logs/jcontroller.log.php @@ -41,3 +41,146 @@ 2024-12-21T11:13:38+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3159 Array ( [0] => 3142 [1] => 3159 ) 2024-12-21T11:13:38+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3159: 1 Array ( [0] => 3142 [1] => 3159 ) 2024-12-21T11:14:22+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3159 Array ( [0] => 3142 ) +2024-12-30T08:58:48+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.2530 Array ( [0] => 2530 ) +2024-12-30T08:59:03+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.2530 Array ( ) +2024-12-30T09:08:13+00:00 INFO 172.21.0.6 controller Holding edit ID com_categories.edit.category.154 Array ( [0] => 154 ) +2024-12-30T09:08:13+00:00 INFO 172.21.0.6 controller Checking edit ID com_categories.edit.category.154: 1 Array ( [0] => 154 ) +2024-12-30T09:08:22+00:00 INFO 172.21.0.6 controller Releasing edit ID com_categories.edit.category.154 Array ( ) +2024-12-30T09:25:03+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T09:25:11+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T09:25:15+00:00 INFO 172.21.0.6 controller Holding edit ID com_categories.edit.category.154 Array ( [0] => 154 ) +2024-12-30T09:25:45+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T09:34:51+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.325 Array ( ) +2024-12-30T09:35:53+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.325 Array ( [0] => 325 ) +2024-12-30T13:55:34+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.6693 Array ( [0] => 6693 ) +2024-12-30T13:55:34+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.6693: 1 Array ( [0] => 6693 ) +2024-12-30T13:55:56+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.6693 Array ( ) +2024-12-30T13:57:34+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.group.53 Array ( [0] => 53 ) +2024-12-30T13:57:40+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.group.53 Array ( ) +2024-12-30T14:00:46+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:00:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:00:58+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.769 Array ( ) +2024-12-30T14:01:06+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.6693 Array ( [0] => 6693 ) +2024-12-30T14:01:06+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.6693: 1 Array ( [0] => 6693 ) +2024-12-30T14:01:56+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.6693 Array ( [0] => 6693 ) +2024-12-30T14:01:56+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.6693: 1 Array ( [0] => 6693 ) +2024-12-30T14:02:11+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.6693 Array ( ) +2024-12-30T14:02:18+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:02:18+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:02:32+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:02:32+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:02:55+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.769 Array ( ) +2024-12-30T14:03:56+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:03:56+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:04:11+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:04:11+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:09:28+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.325 Array ( ) +2024-12-30T14:10:39+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:10:39+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:11:24+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.769 Array ( [0] => 769 ) +2024-12-30T14:11:25+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.769: 1 Array ( [0] => 769 ) +2024-12-30T14:12:28+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.769 Array ( ) +2024-12-30T14:29:36+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T14:29:36+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T14:29:51+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T14:32:39+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T14:32:39+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T14:33:16+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T14:37:25+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:37:26+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:38:11+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:38:29+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.431 Array ( [0] => 431 ) +2024-12-30T14:38:29+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.431: 1 Array ( [0] => 431 ) +2024-12-30T14:38:38+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.431 Array ( ) +2024-12-30T14:38:45+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:38:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:38:54+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:39:09+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.431 Array ( [0] => 431 ) +2024-12-30T14:39:10+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.431: 1 Array ( [0] => 431 ) +2024-12-30T14:40:23+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.431 Array ( ) +2024-12-30T14:40:30+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:40:30+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:40:38+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.577 Array ( [0] => 20 [1] => 577 ) +2024-12-30T14:40:39+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.577: 1 Array ( [0] => 20 [1] => 577 ) +2024-12-30T14:40:46+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.577 Array ( [0] => 20 ) +2024-12-30T14:40:50+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:40:51+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:41:03+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:41:07+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.20 Array ( [0] => 20 ) +2024-12-30T14:41:07+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.20: 1 Array ( [0] => 20 ) +2024-12-30T14:41:16+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.20 Array ( ) +2024-12-30T14:42:26+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:42:45+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T14:43:15+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:43:24+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T14:45:13+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:45:32+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T14:46:13+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T14:47:29+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T14:47:29+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T14:48:10+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:03:50+00:00 INFO 172.21.0.6 controller Holding edit ID com_categories.edit.category.212 Array ( [0] => 154 [1] => 212 ) +2024-12-30T15:03:50+00:00 INFO 172.21.0.6 controller Checking edit ID com_categories.edit.category.212: 1 Array ( [0] => 154 [1] => 212 ) +2024-12-30T15:04:30+00:00 INFO 172.21.0.6 controller Releasing edit ID com_categories.edit.category.212 Array ( [0] => 154 ) +2024-12-30T15:05:33+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.178 Array ( [0] => 178 ) +2024-12-30T15:05:33+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.178: 1 Array ( [0] => 178 ) +2024-12-30T15:05:52+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.178 Array ( [0] => 178 ) +2024-12-30T15:05:52+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.178: 1 Array ( [0] => 178 ) +2024-12-30T15:06:13+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.178 Array ( ) +2024-12-30T15:06:35+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.293 Array ( [0] => 293 ) +2024-12-30T15:06:35+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.293: 1 Array ( [0] => 293 ) +2024-12-30T15:06:48+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.293 Array ( ) +2024-12-30T15:12:10+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.327 Array ( [0] => 327 ) +2024-12-30T15:12:26+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.327 Array ( ) +2024-12-30T15:15:54+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:15:55+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:16:08+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:31:07+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:31:07+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:31:24+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:32:45+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:32:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:33:00+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:33:01+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:52:59+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T15:59:10+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:59:11+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T15:59:30+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.412 Array ( [0] => 412 ) +2024-12-30T15:59:30+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.412: 1 Array ( [0] => 412 ) +2024-12-30T16:52:44+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.412 Array ( ) +2024-12-30T16:52:57+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3189 Array ( [0] => 3189 ) +2024-12-30T16:52:57+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3189: 1 Array ( [0] => 3189 ) +2024-12-30T16:53:35+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3189 Array ( ) +2024-12-30T16:53:37+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3189 Array ( [0] => 3189 ) +2024-12-30T16:53:38+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3189: 1 Array ( [0] => 3189 ) +2024-12-30T16:54:48+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3189 Array ( ) +2024-12-30T16:56:17+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.3189 Array ( [0] => 3189 ) +2024-12-30T16:56:17+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.3189: 1 Array ( [0] => 3189 ) +2024-12-30T16:56:31+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.3189 Array ( ) +2024-12-30T16:57:12+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.16 Array ( [0] => 16 ) +2024-12-30T16:57:13+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.16: 1 Array ( [0] => 16 ) +2024-12-30T16:57:41+00:00 INFO 172.21.0.6 controller Holding edit ID com_fields.edit.field.16 Array ( [0] => 16 ) +2024-12-30T16:57:41+00:00 INFO 172.21.0.6 controller Checking edit ID com_fields.edit.field.16: 1 Array ( [0] => 16 ) +2024-12-30T18:10:25+00:00 INFO 172.21.0.6 controller Releasing edit ID com_fields.edit.field.16 Array ( ) +2024-12-31T08:12:08+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.2384 Array ( [0] => 2384 ) +2024-12-31T08:12:15+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.2384 Array ( ) +2024-12-31T08:21:58+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:21:58+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:22:28+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:22:29+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:22:50+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:22:50+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:23:14+00:00 INFO 172.21.0.6 controller Holding edit ID com_modules.edit.module.556 Array ( [0] => 556 ) +2024-12-31T08:23:15+00:00 INFO 172.21.0.6 controller Checking edit ID com_modules.edit.module.556: 1 Array ( [0] => 556 ) +2024-12-31T08:37:51+00:00 INFO 172.21.0.6 controller Releasing edit ID com_modules.edit.module.556 Array ( ) +2024-12-31T08:38:01+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.176 Array ( [0] => 176 ) +2024-12-31T08:38:08+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.176 Array ( ) +2024-12-31T08:38:18+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.317 Array ( [0] => 317 ) +2024-12-31T08:38:43+00:00 INFO 172.21.0.6 controller Holding edit ID com_menus.edit.item.317 Array ( [0] => 317 ) +2024-12-31T09:31:22+00:00 INFO 172.21.0.6 controller Releasing edit ID com_menus.edit.item.317 Array ( ) +2024-12-31T09:46:15+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.4097 Array ( [0] => 4097 ) +2024-12-31T09:46:15+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.4097: 1 Array ( [0] => 4097 ) +2024-12-31T09:47:08+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.4097: 1 Array ( [0] => 4097 ) +2024-12-31T09:49:32+00:00 INFO 172.21.0.6 controller Releasing edit ID com_content.edit.article.4097 Array ( ) +2024-12-31T09:56:46+00:00 INFO 172.21.0.6 controller Holding edit ID com_content.edit.article.4097 Array ( [0] => 4097 ) +2024-12-31T09:56:46+00:00 INFO 172.21.0.6 controller Checking edit ID com_content.edit.article.4097: 1 Array ( [0] => 4097 ) diff --git a/administrator/logs/joomla_scheduler.php b/administrator/logs/joomla_scheduler.php index 58c3195c..dbd9a049 100644 --- a/administrator/logs/joomla_scheduler.php +++ b/administrator/logs/joomla_scheduler.php @@ -5,3 +5,21 @@ #Fields: date time priority message 2024-12-21 06:28:06 INFO Attività terminata con successo#01 in 0.03 (rete 0.03) secondi. +2024-12-30 07:45:47 INFO Esecuzione attività#04 'Delete Action Logs'. +2024-12-30 07:45:47 INFO Attività> Delete Logs after 15 days +2024-12-30 07:45:47 INFO Attività> Delete Logs end +2024-12-30 07:45:47 INFO Attività terminata con successo#04 in 0.01 (rete 0.02) secondi. +2024-12-30 07:45:53 INFO Esecuzione attività#02 'Session GC'. +2024-12-30 07:45:53 INFO Attività> SessionGC end +2024-12-30 07:45:53 INFO Attività terminata con successo#02 in 0.01 (rete 0.01) secondi. +2024-12-30 07:46:05 INFO Esecuzione attività#03 'Update Notification'. +2024-12-30 07:46:06 INFO Attività terminata con successo#03 in 0.78 (rete 0.78) secondi. +2024-12-31 07:46:06 INFO Esecuzione attività#04 'Delete Action Logs'. +2024-12-31 07:46:06 INFO Attività> Delete Logs after 15 days +2024-12-31 07:46:06 INFO Attività> Delete Logs end +2024-12-31 07:46:06 INFO Attività terminata con successo#04 in 0.02 (rete 0.02) secondi. +2024-12-31 07:51:07 INFO Esecuzione attività#02 'Session GC'. +2024-12-31 07:51:07 INFO Attività> SessionGC end +2024-12-31 07:51:07 INFO Attività terminata con successo#02 in 0.01 (rete 0.01) secondi. +2024-12-31 07:56:07 INFO Esecuzione attività#03 'Update Notification'. +2024-12-31 07:56:07 INFO Attività terminata con successo#03 in 0.53 (rete 0.53) secondi. diff --git a/administrator/logs/webauthn_system.php b/administrator/logs/webauthn_system.php index b4b0cf00..5dbe5133 100644 --- a/administrator/logs/webauthn_system.php +++ b/administrator/logs/webauthn_system.php @@ -221,3 +221,15 @@ 2024-12-21T09:31:42+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page 2024-12-21T09:31:42+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page 2024-12-21T09:31:42+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:35+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-30T08:39:39+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page +2024-12-31T08:12:47+00:00 DEBUG 172.21.0.6 Injecting WebAuthn Passwordless Login fields in user profile edit page diff --git a/components/com_highlights/forms/highlightform.xml b/components/com_highlights/forms/highlightform.xml new file mode 100644 index 00000000..2aed80db --- /dev/null +++ b/components/com_highlights/forms/highlightform.xml @@ -0,0 +1,28 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/components/com_highlights/forms/index.html b/components/com_highlights/forms/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/components/com_highlights/forms/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/com_highlights/src/Controller/DisplayController.php b/components/com_highlights/src/Controller/DisplayController.php new file mode 100644 index 00000000..56c92559 --- /dev/null +++ b/components/com_highlights/src/Controller/DisplayController.php @@ -0,0 +1,64 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Factory\MVCFactoryInterface; + +/** + * Display Component Controller + * + * @since 1.0.0 + */ +class DisplayController extends \Joomla\CMS\MVC\Controller\BaseController +{ + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * Recognized key values include 'name', 'default_task', 'model_path', and + * 'view_path' (this list is not meant to be comprehensive). + * @param MVCFactoryInterface $factory The factory. + * @param CMSApplication $app The JApplication for the dispatcher + * @param Input $input Input + * + * @since 1.0.0 + */ + public function __construct($config = array(), MVCFactoryInterface $factory = null, $app = null, $input = null) + { + parent::__construct($config, $factory, $app, $input); + } + + /** + * Method to display a view. + * + * @param boolean $cachable If true, the view output will be cached. + * @param boolean $urlparams An array of safe URL parameters and their variable types, for valid values see {@link InputFilter::clean()}. + * + * @return \Joomla\CMS\MVC\Controller\BaseController This object to support chaining. + * + * @since 1.0.0 + */ + public function display($cachable = false, $urlparams = false) + { + + $view = $this->input->getCmd('view', 'highlights'); + $view = $view == "featured" ? 'highlights' : $view; + $this->input->set('view', $view); + + + parent::display($cachable, $urlparams); + return $this; + } +} diff --git a/components/com_highlights/src/Controller/HighlightController.php b/components/com_highlights/src/Controller/HighlightController.php new file mode 100644 index 00000000..80877a41 --- /dev/null +++ b/components/com_highlights/src/Controller/HighlightController.php @@ -0,0 +1,220 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Controller; + +\defined('_JEXEC') or die; + +use \Joomla\CMS\Application\SiteApplication; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Multilanguage; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\MVC\Controller\BaseController; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Uri\Uri; +use \Joomla\Utilities\ArrayHelper; + +/** + * Highlight class. + * + * @since 1.6.0 + */ +class HighlightController extends BaseController +{ + /** + * Method to check out an item for editing and redirect to the edit form. + * + * @return void + * + * @since 1.0.0 + * + * @throws Exception + */ + public function edit() + { + // Get the previous edit id (if any) and the current edit id. + $previousId = (int) $this->app->getUserState('com_highlights.edit.highlight.id'); + $editId = $this->input->getInt('id', 0); + + // Set the user id for the user to edit in the session. + $this->app->setUserState('com_highlights.edit.highlight.id', $editId); + + // Get the model. + $model = $this->getModel('Highlight', 'Site'); + + // Check out the item + if ($editId) + { + $model->checkout($editId); + } + + // Check in the previous user. + if ($previousId && $previousId !== $editId) + { + $model->checkin($previousId); + } + + // Redirect to the edit screen. + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlightform&layout=edit', false)); + } + + /** + * Method to save data + * + * @return void + * + * @throws Exception + * @since 1.0.0 + */ + public function publish() + { + // Checking if the user can remove object + $user = $this->app->getIdentity(); + + if ($user->authorise('core.edit', 'com_highlights') || $user->authorise('core.edit.state', 'com_highlights')) + { + $model = $this->getModel('Highlight', 'Site'); + + // Get the user data. + $id = $this->input->getInt('id'); + $state = $this->input->getInt('state'); + + // Attempt to save the data. + $return = $model->publish($id, $state); + + // Check for errors. + if ($return === false) + { + $this->setMessage(Text::sprintf('Save failed: %s', $model->getError()), 'warning'); + } + + // Clear the profile id from the session. + $this->app->setUserState('com_highlights.edit.highlight.id', null); + + // Flush the data from the session. + $this->app->setUserState('com_highlights.edit.highlight.data', null); + + // Redirect to the list screen. + $this->setMessage(Text::_('COM_HIGHLIGHTS_ITEM_SAVED_SUCCESSFULLY')); + $menu = Factory::getApplication()->getMenu(); + $item = $menu->getActive(); + + if (!$item) + { + // If there isn't any menu item active, redirect to list view + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlights', false)); + } + else + { + $this->setRedirect(Route::_('index.php?Itemid='. $item->id, false)); + } + } + else + { + throw new \Exception(500); + } + } + + /** + * Check in record + * + * @return boolean True on success + * + * @since 1.0.0 + */ + public function checkin() + { + // Check for request forgeries. + $this->checkToken('GET'); + + $id = $this->input->getInt('id', 0); + $model = $this->getModel(); + $item = $model->getItem($id); + + // Checking if the user can remove object + $user = $this->app->getIdentity(); + + if ($user->authorise('core.manage', 'com_highlights') || $item->checked_out == $user->id) { + + $return = $model->checkin($id); + + if ($return === false) + { + // Checkin failed. + $message = Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()); + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlight' . '&id=' . $id, false), $message, 'error'); + return false; + } + else + { + // Checkin succeeded. + $message = Text::_('COM_HIGHLIGHTS_CHECKEDIN_SUCCESSFULLY'); + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlight' . '&id=' . $id, false), $message); + return true; + } + } + else + { + throw new \Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + } + } + + /** + * Remove data + * + * @return void + * + * @throws Exception + */ + public function remove() + { + // Checking if the user can remove object + $user = $this->app->getIdentity(); + + if ($user->authorise('core.delete', 'com_highlights')) + { + $model = $this->getModel('Highlight', 'Site'); + + // Get the user data. + $id = $this->input->getInt('id', 0); + + // Attempt to save the data. + $return = $model->delete($id); + + // Check for errors. + if ($return === false) + { + $this->setMessage(Text::sprintf('Delete failed', $model->getError()), 'warning'); + } + else + { + // Check in the profile. + if ($return) + { + $model->checkin($return); + } + + $this->app->setUserState('com_highlights.edit.highlight.id', null); + $this->app->setUserState('com_highlights.edit.highlight.data', null); + + $this->app->enqueueMessage(Text::_('COM_HIGHLIGHTS_ITEM_DELETED_SUCCESSFULLY'), 'success'); + $this->app->redirect(Route::_('index.php?option=com_highlights&view=highlights', false)); + } + + // Redirect to the list screen. + $menu = Factory::getApplication()->getMenu(); + $item = $menu->getActive(); + $this->setRedirect(Route::_($item->link, false)); + } + else + { + throw new \Exception(500); + } + } +} diff --git a/components/com_highlights/src/Controller/HighlightformController.php b/components/com_highlights/src/Controller/HighlightformController.php new file mode 100644 index 00000000..c51f108a --- /dev/null +++ b/components/com_highlights/src/Controller/HighlightformController.php @@ -0,0 +1,273 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Application\SiteApplication; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Multilanguage; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; +use Joomla\Utilities\ArrayHelper; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; + +/** + * Highlight class. + * + * @since 1.0.0 + */ +class HighlightformController extends FormController +{ + /** + * Method to check out an item for editing and redirect to the edit form. + * + * @return void + * + * @since 1.0.0 + * + * @throws Exception + */ + public function edit($key = NULL, $urlVar = NULL) + { + // Get the previous edit id (if any) and the current edit id. + $previousId = (int) $this->app->getUserState('com_highlights.edit.highlight.id'); + $editId = $this->input->getInt('id', 0); + + // Set the user id for the user to edit in the session. + $this->app->setUserState('com_highlights.edit.highlight.id', $editId); + + // Get the model. + $model = $this->getModel('Highlightform', 'Site'); + + // Check out the item + if ($editId) + { + $model->checkout($editId); + } + + // Check in the previous user. + if ($previousId) + { + $model->checkin($previousId); + } + + // Redirect to the edit screen. + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlightform&layout=edit', false)); + } + + /** + * Method to save data. + * + * @return void + * + * @throws Exception + * @since 1.0.0 + */ + public function save($key = NULL, $urlVar = NULL) + { + // Check for request forgeries. + $this->checkToken(); + + // Initialise variables. + $model = $this->getModel('Highlightform', 'Site'); + + // Get the user data. + $data = $this->input->get('jform', array(), 'array'); + + // Validate the posted data. + $form = $model->getForm(); + + if (!$form) + { + throw new \Exception($model->getError(), 500); + } + + // Send an object which can be modified through the plugin event + $objData = (object) $data; + $this->app->triggerEvent( + 'onContentNormaliseRequestData', + array($this->option . '.' . $this->context, $objData, $form) + ); + + $data = (array) $objData; + + // Validate the posted data. + $data = $model->validate($form, $data); + + // Check for errors. + if ($data === false) + { + // Get the validation messages. + $errors = $model->getErrors(); + + // Push up to three validation messages out to the user. + for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) + { + if ($errors[$i] instanceof \Exception) + { + $this->app->enqueueMessage($errors[$i]->getMessage(), 'warning'); + } + else + { + $this->app->enqueueMessage($errors[$i], 'warning'); + } + } + + $jform = $this->input->get('jform', array(), 'ARRAY'); + + // Save the data in the session. + $this->app->setUserState('com_highlights.edit.highlight.data', $jform); + + // Redirect back to the edit screen. + $id = (int) $this->app->getUserState('com_highlights.edit.highlight.id'); + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlightform&layout=edit&id=' . $id, false)); + + $this->redirect(); + } + + // Attempt to save the data. + $return = $model->save($data); + + // Check for errors. + if ($return === false) + { + // Save the data in the session. + $this->app->setUserState('com_highlights.edit.highlight.data', $data); + + // Redirect back to the edit screen. + $id = (int) $this->app->getUserState('com_highlights.edit.highlight.id'); + $this->setMessage(Text::sprintf('Save failed', $model->getError()), 'warning'); + $this->setRedirect(Route::_('index.php?option=com_highlights&view=highlightform&layout=edit&id=' . $id, false)); + $this->redirect(); + } + + // Check in the profile. + if ($return) + { + $model->checkin($return); + } + + // Clear the profile id from the session. + $this->app->setUserState('com_highlights.edit.highlight.id', null); + + // Redirect to the list screen. + if (!empty($return)) + { + $this->setMessage(Text::_('COM_HIGHLIGHTS_ITEM_SAVED_SUCCESSFULLY')); + } + + $menu = Factory::getApplication()->getMenu(); + $item = $menu->getActive(); + $url = (empty($item->link) ? 'index.php?option=com_highlights&view=highlights' : $item->link); + $this->setRedirect(Route::_($url, false)); + + // Flush the data from the session. + $this->app->setUserState('com_highlights.edit.highlight.data', null); + + // Invoke the postSave method to allow for the child class to access the model. + $this->postSaveHook($model, $data); + + } + + /** + * Method to abort current operation + * + * @return void + * + * @throws Exception + */ + public function cancel($key = NULL) + { + + // Get the current edit id. + $editId = (int) $this->app->getUserState('com_highlights.edit.highlight.id'); + + // Get the model. + $model = $this->getModel('Highlightform', 'Site'); + + // Check in the item + if ($editId) + { + $model->checkin($editId); + } + + $menu = Factory::getApplication()->getMenu(); + $item = $menu->getActive(); + $url = (empty($item->link) ? 'index.php?option=com_highlights&view=highlights' : $item->link); + $this->setRedirect(Route::_($url, false)); + } + + /** + * Method to remove data + * + * @return void + * + * @throws Exception + * + * @since 1.0.0 + */ + public function remove() + { + $model = $this->getModel('Highlightform', 'Site'); + $pk = $this->input->getInt('id'); + + // Attempt to save the data + try + { + // Check in before delete + $return = $model->checkin($return); + // Clear id from the session. + $this->app->setUserState('com_highlights.edit.highlight.id', null); + + $menu = $this->app->getMenu(); + $item = $menu->getActive(); + $url = (empty($item->link) ? 'index.php?option=com_highlights&view=highlights' : $item->link); + + if($return) + { + $model->delete($pk); + $this->setMessage(Text::_('COM_HIGHLIGHTS_ITEM_DELETED_SUCCESSFULLY')); + } + else + { + $this->setMessage(Text::_('COM_HIGHLIGHTS_ITEM_DELETED_UNSUCCESSFULLY'), 'warning'); + } + + + $this->setRedirect(Route::_($url, false)); + // Flush the data from the session. + $this->app->setUserState('com_highlights.edit.highlight.data', null); + } + catch (\Exception $e) + { + $errorType = ($e->getCode() == '404') ? 'error' : 'warning'; + $this->setMessage($e->getMessage(), $errorType); + $this->setRedirect('index.php?option=com_highlights&view=highlights'); + } + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param BaseDatabaseModel $model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 1.6 + */ + protected function postSaveHook(BaseDatabaseModel $model, $validData = array()) + { + } +} diff --git a/components/com_highlights/src/Controller/HighlightsController.php b/components/com_highlights/src/Controller/HighlightsController.php new file mode 100644 index 00000000..d7f42ea0 --- /dev/null +++ b/components/com_highlights/src/Controller/HighlightsController.php @@ -0,0 +1,45 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Application\SiteApplication; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Multilanguage; +use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; +use Joomla\Utilities\ArrayHelper; + +/** + * Highlights class. + * + * @since 1.0.0 + */ +class HighlightsController extends FormController +{ + /** + * Proxy for getModel. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional + * @param array $config Configuration array for model. Optional + * + * @return object The model + * + * @since 1.0.0 + */ + public function getModel($name = 'Highlights', $prefix = 'Site', $config = array()) + { + return parent::getModel($name, $prefix, array('ignore_request' => true)); + } +} diff --git a/components/com_highlights/src/Dispatcher/Dispatcher.php b/components/com_highlights/src/Dispatcher/Dispatcher.php new file mode 100644 index 00000000..8fdf9845 --- /dev/null +++ b/components/com_highlights/src/Dispatcher/Dispatcher.php @@ -0,0 +1,35 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Dispatcher; + +defined('JPATH_PLATFORM') or die; + +use Joomla\CMS\Dispatcher\ComponentDispatcher; +use Joomla\CMS\Language\Text; + +/** + * ComponentDispatcher class for Com_Highlights + * + * @since 1.0.0 + */ +class Dispatcher extends ComponentDispatcher +{ + /** + * Dispatch a controller task. Redirecting the user if appropriate. + * + * @return void + * + * @since 1.0.0 + */ + public function dispatch() + { + parent::dispatch(); + } +} diff --git a/components/com_highlights/src/Field/CreatedbyField.php b/components/com_highlights/src/Field/CreatedbyField.php new file mode 100644 index 00000000..c01bc00e --- /dev/null +++ b/components/com_highlights/src/Field/CreatedbyField.php @@ -0,0 +1,67 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\User\UserFactoryInterface; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class CreatedbyField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'createdby'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + + // Load user + $user_id = $this->value; + + if ($user_id) + { + $container = \Joomla\CMS\Factory::getContainer(); + $userFactory = $container->get(UserFactoryInterface::class); + $user = $userFactory->loadUserById($user_id); + } + else + { + $user = Factory::getApplication()->getIdentity(); + $html[] = ''; + } + + if (!$this->hidden) + { + $html[] = "
" . $user->name . " (" . $user->username . ")
"; + } + + return implode($html); + } +} diff --git a/components/com_highlights/src/Field/ForeignkeyField.php b/components/com_highlights/src/Field/ForeignkeyField.php new file mode 100644 index 00000000..ad641a63 --- /dev/null +++ b/components/com_highlights/src/Field/ForeignkeyField.php @@ -0,0 +1,296 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\Field\ListField; + +/** + * Supports a value from an external table + * + * @since 1.0.0 + */ +class ForeignKeyField extends ListField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'foreignkey'; + + protected $layout = 'joomla.form.field.list-fancy-select'; + + /** + * The translate. + * + * @var boolean + * @since 1.0.0 + */ + protected $translate = true; + + protected $header = false; + + private $input_type; + + private $table; + + private $key_field; + + private $value_field; + + private $option_key_field; + + private $option_value_field; + + private $condition; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function processQuery() + { + // Type of input the field shows + $this->input_type = $this->getAttribute('input_type'); + + // Database Table + $this->table = $this->getAttribute('table'); + + // The field that the field will save on the database + $this->key_field = (string) $this->getAttribute('key_field'); + + // The column that the field shows in the input + $this->value_field = (string) $this->getAttribute('value_field'); + + // The option field that the field will save on the database + $this->option_key_field = (string) $this->getAttribute('option_key_field'); + + // The option value that the field shows in the input + $this->option_value_field = (string) $this->getAttribute('option_value_field'); + + // Flag to identify if the fk_value is multiple + $this->value_multiple = (int) $this->getAttribute('value_multiple', 0); + + $this->required = (string) $this->getAttribute('required', 0); + + // Flag to identify if the fk_value hides the trashed items + $this->hideTrashed = (int) $this->getAttribute('hide_trashed', 0); + + // Flag to identify if the fk_value hides the unpublished items + $this->hideUnpublished = (int) $this->getAttribute('hide_unpublished', 0); + + // Flag to identify if the fk_value hides the published items + $this->hidePublished = (int) $this->getAttribute('hide_published', 0); + + // Flag to identify if the fk_value hides the archived items + $this->hideArchived = (int) $this->getAttribute('hide_archived', 0); + + // Flag to identify if the fk has default order + $this->fk_ordering = (string) $this->getAttribute('fk_ordering'); + + // The where SQL for foreignkey + $this->condition = (string) $this->getAttribute('condition'); + + // Flag for translate options + $this->translate = (bool) $this->getAttribute('translate'); + + // Initialize variables. + $html = ''; + $fk_value = ''; + + // Load all the field options + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + // Support for multiple fields on fk_values + if ($this->value_multiple == 1) + { + // Get the fields for multiple value + $this->value_fields = (string) $this->getAttribute('value_field_multiple'); + $this->value_fields = explode(',', $this->value_fields); + $this->separator = (string) $this->getAttribute('separator'); + + $fk_value = ' CONCAT('; + + foreach ($this->value_fields as $field) + { + $fk_value .= $db->quoteName($field) . ', \'' . $this->separator . '\', '; + } + + $fk_value = substr($fk_value, 0, -(strlen($this->separator) + 6)); + $fk_value .= ') AS ' . $db->quoteName($this->value_field); + } + else + { + $fk_value = $db->quoteName($this->value_field); + } + + $query + ->select( + array( + $db->quoteName($this->key_field), + $fk_value + ) + ) + ->from($this->table); + + if ($this->hideTrashed) + { + $query->where($db->quoteName('state') . ' != -2'); + } + + if ($this->hideUnpublished) + { + $query->where($db->quoteName('state') . ' != 0'); + } + + if ($this->hidePublished) + { + $query->where($db->quoteName('state') . ' != 1'); + } + + if ($this->hideArchived) + { + $query->where($db->quoteName('state') . ' != 2'); + } + + if ($this->fk_ordering) + { + $query->order($this->fk_ordering); + } + + if($this->condition) + { + $query->where($this->condition); + } + + + + return $query; + } + + /** + * Method to get the field input for a foreignkey field. + * + * @return string The field input. + * + * @since 1.0.0 + */ + protected function getInput() + { + $data = $this->getLayoutData(); + + if (!\is_array($this->value) && !empty($this->value)) + { + if (\is_object($this->value)) + { + $this->value = get_object_vars($this->value); + } + + // String in format 2,5,4 + if (\is_string($this->value)) + { + $this->value = explode(',', $this->value); + } + + // Integer is given + if (\is_int($this->value)) + { + $this->value = array($this->value); + } + + $data['value'] = $this->value; + } + + $data['options'] = $this->getOptions(); + + return $this->getRenderer($this->layout)->render($data); + } + + /** + * Method to get the field options. + * + * @return array The field option objects. + * + * @since 1.0.0 + */ + protected function getOptions() + { + $options = array(); + $db = Factory::getContainer()->get('DatabaseDriver'); + try + { + $db->setQuery($this->processQuery()); + $results = $db->loadObjectList(); + } + catch (ExecutionFailureException $e) + { + Factory::getApplication()->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error'); + } + + // Add header. + if (!empty($this->header)) + { + $options[] = (object) ["value" => '', "text" => Text::_($this->header)]; + } + + if(!empty($this->option_value_field) || !empty($this->option_key_field)) + { + $options[] = (object) ["value" => $this->option_key_field, "text" => Text::_($this->option_value_field)]; + } + + // Build the field options. + if (!empty($results)) + { + foreach ($results as $item) + { + $options[] = (object) [ + "value" => $item->{$this->key_field}, + "text" => $this->translate == true ? Text::_($item->{$this->value_field}) : $item->{$this->value_field} + ]; + } + } + + // Merge any additional options in the XML definition. + $options = array_merge(parent::getOptions(), $options); + + return $options; + } + + /** + * Wrapper method for getting attributes from the form element + * + * @param string $attr_name Attribute name + * @param mixed $default Optional value to return if attribute not found + * + * @return mixed The value of the attribute if it exists, null otherwise + */ + public function getAttribute($attr_name, $default = null) + { + if (!empty($this->element[$attr_name])) + { + return $this->element[$attr_name]; + } + else + { + return $default; + } + } +} diff --git a/components/com_highlights/src/Field/ModifiedbyField.php b/components/com_highlights/src/Field/ModifiedbyField.php new file mode 100644 index 00000000..f8f6956a --- /dev/null +++ b/components/com_highlights/src/Field/ModifiedbyField.php @@ -0,0 +1,53 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Form\FormField; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class ModifiedbyField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'modifiedby'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + $user = Factory::getApplication()->getIdentity(); + $html[] = ''; + + if (!$this->hidden) + { + $html[] = "
" . $user->name . " (" . $user->username . ")
"; + } + + return implode($html); + } +} diff --git a/components/com_highlights/src/Field/NestedparentField.php b/components/com_highlights/src/Field/NestedparentField.php new file mode 100644 index 00000000..c83bb190 --- /dev/null +++ b/components/com_highlights/src/Field/NestedparentField.php @@ -0,0 +1,83 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +defined('JPATH_BASE') or die; + +use Joomla\CMS\Helper\UserGroupsHelper; +use \Joomla\CMS\Factory; +use Joomla\CMS\Form\Field\ListField; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class NestedparentField extends ListField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'nestedparent'; + + /** + * Method to get the field options. + * + * @return array The field option objects. + * + * @since 1.0.0 + */ + protected function getOptions() + { + $options = array(); + $table = $this->getAttribute('table'); + + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true) + ->select('DISTINCT(a.id) AS value, a.title AS text, a.level, a.lft') + ->from($table . ' AS a'); + + + // Prevent parenting to children of this item. + if ($id = $this->form->getValue('id')) + { + $query->join('LEFT', $db->quoteName($table) . ' AS p ON p.id = ' . (int) $id) + ->where('NOT(a.lft >= p.lft AND a.rgt <= p.rgt)'); + } + + $query->order('a.lft ASC'); + + // Get the options. + $db->setQuery($query); + + try + { + $options = $db->loadObjectList(); + } + catch (\RuntimeException $e) + { + \JError::raiseWarning(500, $e->getMessage()); + } + + // Pad the option text with spaces using depth level as a multiplier. + for ($i = 0, $n = count($options); $i < $n; $i++) + { + $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->text; + } + + // Merge any additional options in the XML definition. + $options = array_merge(parent::getOptions(), $options); + + return $options; + } +} diff --git a/components/com_highlights/src/Field/SubmitField.php b/components/com_highlights/src/Field/SubmitField.php new file mode 100644 index 00000000..806ac3b8 --- /dev/null +++ b/components/com_highlights/src/Field/SubmitField.php @@ -0,0 +1,49 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +// Check to ensure this file is included in Joomla! +defined('_JEXEC') or die('Restricted access'); + +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\FormField; + +/** + * Class SubmitField + * + * @since 1.0.0 + */ +class SubmitField extends FormField +{ + protected $type = 'submit'; + + protected $value; + + protected $for; + + /** + * Get a form field markup for the input + * + * @return string + */ + public function getInput() + { + $this->value = $this->getAttribute('value'); + + return ''; + } +} diff --git a/components/com_highlights/src/Field/TimecreatedField.php b/components/com_highlights/src/Field/TimecreatedField.php new file mode 100644 index 00000000..c6b1abda --- /dev/null +++ b/components/com_highlights/src/Field/TimecreatedField.php @@ -0,0 +1,65 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\Date\Date; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class TimecreatedField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'timecreated'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + + $time_created = $this->value; + + if (!strtotime($time_created)) + { + $time_created = Factory::getDate()->toSql(); + $html[] = ''; + } + + $hidden = (boolean) $this->element['hidden']; + + if ($hidden == null || !$hidden) + { + $jdate = new Date($time_created); + $pretty_date = $jdate->format(Text::_('DATE_FORMAT_LC2')); + $html[] = "
" . $pretty_date . "
"; + } + + return implode($html); + } +} diff --git a/components/com_highlights/src/Field/TimeupdatedField.php b/components/com_highlights/src/Field/TimeupdatedField.php new file mode 100644 index 00000000..7f3774cb --- /dev/null +++ b/components/com_highlights/src/Field/TimeupdatedField.php @@ -0,0 +1,68 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Field; + +defined('JPATH_BASE') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\FormField; +use \Joomla\CMS\Date\Date; + +/** + * Supports an HTML select list of categories + * + * @since 1.0.0 + */ +class TimeupdatedField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 1.0.0 + */ + protected $type = 'timeupdated'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.0.0 + */ + protected function getInput() + { + // Initialize variables. + $html = array(); + + $old_time_updated = $this->value; + $hidden = (boolean) $this->element['hidden']; + + if ($hidden == null || !$hidden) + { + if (!strtotime($old_time_updated)) + { + $html[] = '-'; + } + else + { + $jdate = new Date($old_time_updated); + $pretty_date = $jdate->format(Text::_('DATE_FORMAT_LC2')); + $html[] = "
" . $pretty_date . "
"; + } + } + + $time_updated = Factory::getDate()->toSql(); + $html[] = ''; + + return implode($html); + } +} diff --git a/components/com_highlights/src/Field/index.html b/components/com_highlights/src/Field/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/components/com_highlights/src/Field/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/com_highlights/src/Helper/HighlightsHelper.php b/components/com_highlights/src/Helper/HighlightsHelper.php new file mode 100644 index 00000000..7db45f3b --- /dev/null +++ b/components/com_highlights/src/Helper/HighlightsHelper.php @@ -0,0 +1,71 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Helper; + +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\MVC\Model\BaseDatabaseModel; + +/** + * Class HighlightsFrontendHelper + * + * @since 1.0.0 + */ +class HighlightsHelper +{ + + + /** + * Gets the files attached to an item + * + * @param int $pk The item's id + * + * @param string $table The table's name + * + * @param string $field The field's name + * + * @return array The files + */ + public static function getFiles($pk, $table, $field) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + $query + ->select($field) + ->from($table) + ->where('id = ' . (int) $pk); + + $db->setQuery($query); + + return explode(',', $db->loadResult()); + } + + /** + * Gets the edit permission for an user + * + * @param mixed $item The item + * + * @return bool + */ + public static function canUserEdit($item) + { + $permission = false; + $user = Factory::getApplication()->getIdentity(); + + if ($user->authorise('core.edit', 'com_highlights') || (isset($item->created_by) && $user->authorise('core.edit.own', 'com_highlights') && $item->created_by == $user->id) || $user->authorise('core.create', 'com_highlights')) + { + $permission = true; + } + + return $permission; + } +} diff --git a/components/com_highlights/src/Helper/index.html b/components/com_highlights/src/Helper/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/components/com_highlights/src/Helper/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/com_highlights/src/Model/HighlightModel.php b/components/com_highlights/src/Model/HighlightModel.php new file mode 100644 index 00000000..feaf4fb4 --- /dev/null +++ b/components/com_highlights/src/Model/HighlightModel.php @@ -0,0 +1,351 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\Utilities\ArrayHelper; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Table\Table; +use \Joomla\CMS\MVC\Model\ItemModel; +use \Joomla\CMS\Helper\TagsHelper; +use \Joomla\CMS\Object\CMSObject; +use \Joomla\CMS\User\UserFactoryInterface; +use \Pcrt\Component\Highlights\Site\Helper\HighlightsHelper; + +/** + * Highlights model. + * + * @since 1.0.0 + */ +class HighlightModel extends ItemModel +{ + public $_item; + + + + + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @return void + * + * @since 1.0.0 + * + * @throws Exception + */ + protected function populateState() + { + $app = Factory::getApplication('com_highlights'); + $user = $app->getIdentity(); + + // Check published state + if ((!$user->authorise('core.edit.state', 'com_highlights')) && (!$user->authorise('core.edit', 'com_highlights'))) + { + $this->setState('filter.published', 1); + $this->setState('filter.archived', 2); + } + + // Load state from the request userState on edit or from the passed variable on default + if (Factory::getApplication()->input->get('layout') == 'edit') + { + $id = Factory::getApplication()->getUserState('com_highlights.edit.highlight.id'); + } + else + { + $id = Factory::getApplication()->input->get('id'); + Factory::getApplication()->setUserState('com_highlights.edit.highlight.id', $id); + } + + $this->setState('highlight.id', $id); + + // Load the parameters. + $params = $app->getParams(); + $params_array = $params->toArray(); + + if (isset($params_array['item_id'])) + { + $this->setState('highlight.id', $params_array['item_id']); + } + + $this->setState('params', $params); + } + + /** + * Method to get an object. + * + * @param integer $id The id of the object to get. + * + * @return mixed Object on success, false on failure. + * + * @throws Exception + */ + public function getItem($id = null) + { + if ($this->_item === null) + { + $this->_item = false; + + if (empty($id)) + { + $id = $this->getState('highlight.id'); + } + + // Get a level row instance. + $table = $this->getTable(); + + // Attempt to load the row. + if ($table && $table->load($id)) + { + + + // Check published state. + if ($published = $this->getState('filter.published')) + { + if (isset($table->state) && $table->state != $published) + { + throw new \Exception(Text::_('COM_HIGHLIGHTS_ITEM_NOT_LOADED'), 403); + } + } + + // Convert the Table to a clean CMSObject. + $properties = $table->getProperties(1); + $this->_item = ArrayHelper::toObject($properties, CMSObject::class); + + + } + + if (empty($this->_item)) + { + throw new \Exception(Text::_('COM_HIGHLIGHTS_ITEM_NOT_LOADED'), 404); + } + } + + + + $container = \Joomla\CMS\Factory::getContainer(); + + $userFactory = $container->get(UserFactoryInterface::class); + + if (isset($this->_item->created_by)) + { + $user = $userFactory->loadUserById($this->_item->created_by); + $this->_item->created_by_name = $user->name; + } + + $container = \Joomla\CMS\Factory::getContainer(); + + $userFactory = $container->get(UserFactoryInterface::class); + + if (isset($this->_item->modified_by)) + { + $user = $userFactory->loadUserById($this->_item->modified_by); + $this->_item->modified_by_name = $user->name; + } + + if (isset($this->_item->etichetta) && $this->_item->etichetta != '') + { + if (is_object($this->_item->etichetta)) + { + $this->_item->etichetta = ArrayHelper::fromObject($this->_item->etichetta); + } + + $values = (is_array($this->_item->etichetta)) ? $this->_item->etichetta : explode(',',$this->_item->etichetta); + + $textValue = array(); + + foreach ($values as $value) + { + $db = $this->getDbo(); + $query = $db->getQuery(true); + + $query + ->select('`#__highlights_etichetta_4129035`.`nome`') + ->from($db->quoteName('#__highlights_etichetta', '#__highlights_etichetta_4129035')) + ->where($db->quoteName('nome') . ' = ' . $db->quote($value)); + + $db->setQuery($query); + $results = $db->loadObject(); + + if ($results) + { + $textValue[] = $results->nome; + } + } + + $this->_item->etichetta = !empty($textValue) ? implode(', ', $textValue) : $this->_item->etichetta; + + } + + return $this->_item; + } + + + + /** + * Get an instance of Table class + * + * @param string $type Name of the Table class to get an instance of. + * @param string $prefix Prefix for the table class name. Optional. + * @param array $config Array of configuration values for the Table object. Optional. + * + * @return Table|bool Table if success, false on failure. + */ + public function getTable($type = 'Highlight', $prefix = 'Administrator', $config = array()) + { + return parent::getTable($type, $prefix, $config); + } + + /** + * Get the id of an item by alias + * @param string $alias Item alias + * + * @return mixed + * + * @deprecated No replacement + */ + public function getItemIdByAlias($alias) + { + $table = $this->getTable(); + $properties = $table->getProperties(); + $result = null; + $aliasKey = null; + if (method_exists($this, 'getAliasFieldNameByView')) + { + $aliasKey = $this->getAliasFieldNameByView('highlight'); + } + + + if (key_exists('alias', $properties)) + { + $table->load(array('alias' => $alias)); + $result = $table->id; + } + elseif (isset($aliasKey) && key_exists($aliasKey, $properties)) + { + $table->load(array($aliasKey => $alias)); + $result = $table->id; + } + + return $result; + + } + + /** + * Method to check in an item. + * + * @param integer $id The id of the row to check out. + * + * @return boolean True on success, false on failure. + * + * @since 1.0.0 + */ + public function checkin($id = null) + { + // Get the id. + $id = (!empty($id)) ? $id : (int) $this->getState('highlight.id'); + + if ($id) + { + // Initialise the table + $table = $this->getTable(); + + // Attempt to check the row in. + if (method_exists($table, 'checkin')) + { + if (!$table->checkin($id)) + { + return false; + } + } + } + + return true; + + } + + /** + * Method to check out an item for editing. + * + * @param integer $id The id of the row to check out. + * + * @return boolean True on success, false on failure. + * + * @since 1.0.0 + */ + public function checkout($id = null) + { + // Get the user id. + $id = (!empty($id)) ? $id : (int) $this->getState('highlight.id'); + + + if ($id) + { + // Initialise the table + $table = $this->getTable(); + + // Get the current user object. + $user = Factory::getApplication()->getIdentity(); + + // Attempt to check the row out. + if (method_exists($table, 'checkout')) + { + if (!$table->checkout($user->get('id'), $id)) + { + return false; + } + } + } + + return true; + + } + + /** + * Publish the element + * + * @param int $id Item id + * @param int $state Publish state + * + * @return boolean + */ + public function publish($id, $state) + { + $table = $this->getTable(); + + $table->load($id); + $table->state = $state; + + return $table->store(); + + } + + /** + * Method to delete an item + * + * @param int $id Element id + * + * @return bool + */ + public function delete($id) + { + $table = $this->getTable(); + + + return $table->delete($id); + + } + + +} diff --git a/components/com_highlights/src/Model/HighlightformModel.php b/components/com_highlights/src/Model/HighlightformModel.php new file mode 100644 index 00000000..8a6c2467 --- /dev/null +++ b/components/com_highlights/src/Model/HighlightformModel.php @@ -0,0 +1,415 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\Utilities\ArrayHelper; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Table\Table; +use \Joomla\CMS\MVC\Model\FormModel; +use \Joomla\CMS\Object\CMSObject; +use \Joomla\CMS\Helper\TagsHelper; + +/** + * Highlights model. + * + * @since 1.0.0 + */ +class HighlightformModel extends FormModel +{ + private $item = null; + + + + + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @return void + * + * @since 1.0.0 + * + * @throws Exception + */ + protected function populateState() + { + $app = Factory::getApplication('com_highlights'); + + // Load state from the request userState on edit or from the passed variable on default + if (Factory::getApplication()->input->get('layout') == 'edit') + { + $id = Factory::getApplication()->getUserState('com_highlights.edit.highlight.id'); + } + else + { + $id = Factory::getApplication()->input->get('id'); + Factory::getApplication()->setUserState('com_highlights.edit.highlight.id', $id); + } + + $this->setState('highlight.id', $id); + + // Load the parameters. + $params = $app->getParams(); + $params_array = $params->toArray(); + + if (isset($params_array['item_id'])) + { + $this->setState('highlight.id', $params_array['item_id']); + } + + $this->setState('params', $params); + } + + /** + * Method to get an ojbect. + * + * @param integer $id The id of the object to get. + * + * @return Object|boolean Object on success, false on failure. + * + * @throws Exception + */ + public function getItem($id = null) + { + if ($this->item === null) + { + $this->item = false; + + if (empty($id)) + { + $id = $this->getState('highlight.id'); + } + + // Get a level row instance. + $table = $this->getTable(); + $properties = $table->getProperties(); + $this->item = ArrayHelper::toObject($properties, CMSObject::class); + + if ($table !== false && $table->load($id) && !empty($table->id)) + { + $user = Factory::getApplication()->getIdentity(); + $id = $table->id; + + + $canEdit = $user->authorise('core.edit', 'com_highlights') || $user->authorise('core.create', 'com_highlights'); + + if (!$canEdit && $user->authorise('core.edit.own', 'com_highlights')) + { + $canEdit = $user->id == $table->created_by; + } + + if (!$canEdit) + { + throw new \Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + } + + // Check published state. + if ($published = $this->getState('filter.published')) + { + if (isset($table->state) && $table->state != $published) + { + return $this->item; + } + } + + // Convert the Table to a clean CMSObject. + $properties = $table->getProperties(1); + $this->item = ArrayHelper::toObject($properties, CMSObject::class); + + + + } + } + + return $this->item; + } + + /** + * Method to get the table + * + * @param string $type Name of the Table class + * @param string $prefix Optional prefix for the table class name + * @param array $config Optional configuration array for Table object + * + * @return Table|boolean Table if found, boolean false on failure + */ + public function getTable($type = 'Highlight', $prefix = 'Administrator', $config = array()) + { + return parent::getTable($type, $prefix, $config); + } + + /** + * Get an item by alias + * + * @param string $alias Alias string + * + * @return int Element id + */ + public function getItemIdByAlias($alias) + { + $table = $this->getTable(); + $properties = $table->getProperties(); + + if (!in_array('alias', $properties)) + { + return null; + } + + $table->load(array('alias' => $alias)); + $id = $table->id; + + + return $id; + + } + + /** + * Method to check in an item. + * + * @param integer $id The id of the row to check out. + * + * @return boolean True on success, false on failure. + * + * @since 1.0.0 + */ + public function checkin($id = null) + { + // Get the id. + $id = (!empty($id)) ? $id : (int) $this->getState('highlight.id'); + + if ($id) + { + // Initialise the table + $table = $this->getTable(); + + // Attempt to check the row in. + if (method_exists($table, 'checkin')) + { + if (!$table->checkin($id)) + { + return false; + } + } + } + + return true; + + } + + /** + * Method to check out an item for editing. + * + * @param integer $id The id of the row to check out. + * + * @return boolean True on success, false on failure. + * + * @since 1.0.0 + */ + public function checkout($id = null) + { + // Get the user id. + $id = (!empty($id)) ? $id : (int) $this->getState('highlight.id'); + + if ($id) + { + // Initialise the table + $table = $this->getTable(); + + // Get the current user object. + $user = Factory::getApplication()->getIdentity(); + + // Attempt to check the row out. + if (method_exists($table, 'checkout')) + { + if (!$table->checkout($user->get('id'), $id)) + { + return false; + } + } + } + + return true; + + } + + /** + * Method to get the profile form. + * + * The base form is loaded from XML + * + * @param array $data An optional array of data for the form to interogate. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * + * @return Form A Form object on success, false on failure + * + * @since 1.0.0 + */ + public function getForm($data = array(), $loadData = true) + { + // Get the form. + $form = $this->loadForm('com_highlights.highlight', 'highlightform', array( + 'control' => 'jform', + 'load_data' => $loadData + ) + ); + + if (empty($form)) + { + return false; + } + + return $form; + } + + /** + * Method to get the data that should be injected in the form. + * + * @return array The default data is an empty array. + * @since 1.0.0 + */ + protected function loadFormData() + { + $data = Factory::getApplication()->getUserState('com_highlights.edit.highlight.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + } + + if ($data) + { + + + return $data; + } + + return array(); + } + + /** + * Method to save the form data. + * + * @param array $data The form data + * + * @return bool + * + * @throws Exception + * @since 1.0.0 + */ + public function save($data) + { + $id = (!empty($data['id'])) ? $data['id'] : (int) $this->getState('highlight.id'); + $state = (!empty($data['state'])) ? 1 : 0; + $user = Factory::getApplication()->getIdentity(); + + + if ($id) + { + // Check the user can edit this item + $authorised = $user->authorise('core.edit', 'com_highlights') || $authorised = $user->authorise('core.edit.own', 'com_highlights'); + } + else + { + // Check the user can create new items in this section + $authorised = $user->authorise('core.create', 'com_highlights'); + } + + if ($authorised !== true) + { + throw new \Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + } + + $table = $this->getTable(); + + if(!empty($id)) + { + $table->load($id); + } + + + + try{ + if ($table->save($data) === true) + { + return $table->id; + } + else + { + Factory::getApplication()->enqueueMessage($table->getError(), 'error'); + return false; + } + }catch(\Exception $e) + { + Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); + return false; + } + + } + + /** + * Method to delete data + * + * @param int $pk Item primary key + * + * @return int The id of the deleted item + * + * @throws Exception + * + * @since 1.0.0 + */ + public function delete($id) + { + $user = Factory::getApplication()->getIdentity(); + + + if (empty($id)) + { + $id = (int) $this->getState('highlight.id'); + } + + if ($id == 0 || $this->getItem($id) == null) + { + throw new \Exception(Text::_('COM_HIGHLIGHTS_ITEM_DOESNT_EXIST'), 404); + } + + if ($user->authorise('core.delete', 'com_highlights') !== true) + { + throw new \Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); + } + + $table = $this->getTable(); + + if ($table->delete($id) !== true) + { + throw new \Exception(Text::_('JERROR_FAILED'), 501); + } + + return $id; + + } + + /** + * Check if data can be saved + * + * @return bool + */ + public function getCanSave() + { + $table = $this->getTable(); + + return $table !== false; + } + +} diff --git a/components/com_highlights/src/Model/HighlightsModel.php b/components/com_highlights/src/Model/HighlightsModel.php new file mode 100644 index 00000000..288b8605 --- /dev/null +++ b/components/com_highlights/src/Model/HighlightsModel.php @@ -0,0 +1,288 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Model; +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\MVC\Model\ListModel; +use \Joomla\Component\Fields\Administrator\Helper\FieldsHelper; +use \Joomla\CMS\Helper\TagsHelper; +use \Joomla\CMS\Layout\FileLayout; +use \Joomla\Database\ParameterType; +use \Joomla\Utilities\ArrayHelper; +use \Pcrt\Component\Highlights\Site\Helper\HighlightsHelper; + + +/** + * Methods supporting a list of Highlights records. + * + * @since 1.0.0 + */ +class HighlightsModel extends ListModel +{ + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * + * @see JController + * @since 1.0.0 + */ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'id', 'a.id', + 'state', 'a.state', + 'ordering', 'a.ordering', + 'created_by', 'a.created_by', + 'modified_by', 'a.modified_by', + 'etichetta', 'a.etichetta', + 'titolo', 'a.titolo', + 'sottotitolo', 'a.sottotitolo', + 'descrizione', 'a.descrizione', + 'lingua', 'a.lingua', + 'link_pulsante', 'a.link_pulsante', + 'testo_pulsante', 'a.testo_pulsante', + 'data', 'a.data', + 'immagine_main', 'a.immagine_main', + 'immagine_secondaria', 'a.immagine_secondaria', + 'data_inizio_pubblicazione', 'a.data_inizio_pubblicazione', + 'data_fine_pubblicazione', 'a.data_fine_pubblicazione', + ); + } + + parent::__construct($config); + } + + + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering Elements order + * @param string $direction Order direction + * + * @return void + * + * @throws Exception + * + * @since 1.0.0 + */ + protected function populateState($ordering = null, $direction = null) + { + // List state information. + parent::populateState('a.id', 'ASC'); + + $app = Factory::getApplication(); + $list = $app->getUserState($this->context . '.list'); + + $value = $app->getUserState($this->context . '.list.limit', $app->get('list_limit', 25)); + $list['limit'] = $value; + + $this->setState('list.limit', $value); + + $value = $app->input->get('limitstart', 0, 'uint'); + $this->setState('list.start', $value); + + $ordering = $this->getUserStateFromRequest($this->context .'.filter_order', 'filter_order', 'a.id'); + $direction = strtoupper($this->getUserStateFromRequest($this->context .'.filter_order_Dir', 'filter_order_Dir', 'ASC')); + + if(!empty($ordering) || !empty($direction)) + { + $list['fullordering'] = $ordering . ' ' . $direction; + } + + $app->setUserState($this->context . '.list', $list); + + + + $context = $this->getUserStateFromRequest($this->context.'.filter.search', 'filter_search'); + $this->setState('filter.search', $context); + + // Split context into component and optional section + if (!empty($context)) + { + $parts = FieldsHelper::extract($context); + + if ($parts) + { + $this->setState('filter.component', $parts[0]); + $this->setState('filter.section', $parts[1]); + } + } + } + + /** + * Build an SQL query to load the list data. + * + * @return DatabaseQuery + * + * @since 1.0.0 + */ + protected function getListQuery() + { + // Create a new query object. + $db = $this->getDbo(); + $query = $db->getQuery(true); + + // Select the required fields from the table. + $query->select( + $this->getState( + 'list.select', 'DISTINCT a.*' + ) + ); + + $query->from('`#__highlights_` AS a'); + + // Join over the users for the checked out user. + $query->select('uc.name AS uEditor'); + $query->join('LEFT', '#__users AS uc ON uc.id=a.checked_out'); + + // Join over the created by field 'created_by' + $query->join('LEFT', '#__users AS created_by ON created_by.id = a.created_by'); + + // Join over the created by field 'modified_by' + $query->join('LEFT', '#__users AS modified_by ON modified_by.id = a.modified_by'); + // Join over the foreign key 'etichetta' + $query->select('`#__highlights_etichetta_4129035`.`nome` AS etichette_fk_value_4129035'); + $query->join('LEFT', '#__highlights_etichetta AS #__highlights_etichetta_4129035 ON #__highlights_etichetta_4129035.`nome` = a.`etichetta`'); + + if (!Factory::getApplication()->getIdentity()->authorise('core.edit', 'com_highlights')) + { + $query->where('a.state = 1'); + } + else + { + $query->where('(a.state IN (0, 1))'); + } + + // Filter by search in title + $search = $this->getState('filter.search'); + + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->Quote('%' . $db->escape($search, true) . '%'); + } + } + + + + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'ASC'); + + if ($orderCol && $orderDirn) + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get an array of data items + * + * @return mixed An array of data on success, false on failure. + */ + public function getItems() + { + $items = parent::getItems(); + + foreach ($items as $item) + { + + if (isset($item->etichetta)) + { + + $values = explode(',', $item->etichetta); + $textValue = array(); + + foreach ($values as $value) + { + $db = $this->getDbo(); + $query = $db->getQuery(true); + $query + ->select('`#__highlights_etichetta_4129035`.`nome`') + ->from($db->quoteName('#__highlights_etichetta', '#__highlights_etichetta_4129035')) + ->where($db->quoteName('#__highlights_etichetta_4129035.nome') . ' = '. $db->quote($db->escape($value))); + + $db->setQuery($query); + $results = $db->loadObject(); + + if ($results) + { + $textValue[] = $results->nome; + } + } + + $item->etichetta = !empty($textValue) ? implode(', ', $textValue) : $item->etichetta; + } + + } + + return $items; + } + + /** + * Overrides the default function to check Date fields format, identified by + * "_dateformat" suffix, and erases the field if it's not correct. + * + * @return void + */ + protected function loadFormData() + { + $app = Factory::getApplication(); + $filters = $app->getUserState($this->context . '.filter', array()); + $error_dateformat = false; + + foreach ($filters as $key => $value) + { + if (strpos($key, '_dateformat') && !empty($value) && $this->isValidDate($value) == null) + { + $filters[$key] = ''; + $error_dateformat = true; + } + } + + if ($error_dateformat) + { + $app->enqueueMessage(Text::_("COM_HIGHLIGHTS_SEARCH_FILTER_DATE_FORMAT"), "warning"); + $app->setUserState($this->context . '.filter', $filters); + } + + return parent::loadFormData(); + } + + /** + * Checks if a given date is valid and in a specified format (YYYY-MM-DD) + * + * @param string $date Date to be checked + * + * @return bool + */ + private function isValidDate($date) + { + $date = str_replace('/', '-', $date); + return (date_create($date)) ? Factory::getDate($date)->format("Y-m-d") : null; + } +} diff --git a/components/com_highlights/src/Service/Category.php b/components/com_highlights/src/Service/Category.php new file mode 100644 index 00000000..72d40bf3 --- /dev/null +++ b/components/com_highlights/src/Service/Category.php @@ -0,0 +1,21 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Service; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\Categories\Categories; +/** + * Content Component Category Tree + * + * @since 1.0.0 + */ + diff --git a/components/com_highlights/src/Service/Router.php b/components/com_highlights/src/Service/Router.php new file mode 100644 index 00000000..1177aedb --- /dev/null +++ b/components/com_highlights/src/Service/Router.php @@ -0,0 +1,151 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\Service; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Component\Router\RouterViewConfiguration; +use Joomla\CMS\Component\Router\RouterView; +use Joomla\CMS\Component\Router\Rules\StandardRules; +use Joomla\CMS\Component\Router\Rules\NomenuRules; +use Joomla\CMS\Component\Router\Rules\MenuRules; +use Joomla\CMS\Factory; +use Joomla\CMS\Categories\Categories; +use Joomla\CMS\Application\SiteApplication; +use Joomla\CMS\Categories\CategoryFactoryInterface; +use Joomla\CMS\Categories\CategoryInterface; +use Joomla\Database\DatabaseInterface; +use Joomla\CMS\Menu\AbstractMenu; +use Joomla\CMS\Component\ComponentHelper; + +/** + * Class HighlightsRouter + * + */ +class Router extends RouterView +{ + private $noIDs; + /** + * The category factory + * + * @var CategoryFactoryInterface + * + * @since 1.0.0 + */ + private $categoryFactory; + + /** + * The category cache + * + * @var array + * + * @since 1.0.0 + */ + private $categoryCache = []; + + public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFactoryInterface $categoryFactory, DatabaseInterface $db) + { + $params = ComponentHelper::getParams('com_highlights'); + $this->noIDs = (bool) $params->get('sef_ids'); + $this->categoryFactory = $categoryFactory; + + + $highlights = new RouterViewConfiguration('highlights'); + $this->registerView($highlights); + $ccHighlight = new RouterViewConfiguration('highlight'); + $ccHighlight->setKey('id')->setParent($highlights); + $this->registerView($ccHighlight); + $highlightform = new RouterViewConfiguration('highlightform'); + $highlightform->setKey('id'); + $this->registerView($highlightform); + + parent::__construct($app, $menu); + + $this->attachRule(new MenuRules($this)); + $this->attachRule(new StandardRules($this)); + $this->attachRule(new NomenuRules($this)); + } + + + + /** + * Method to get the segment(s) for an highlight + * + * @param string $id ID of the highlight to retrieve the segments for + * @param array $query The request that is built right now + * + * @return array|string The segments of this item + */ + public function getHighlightSegment($id, $query) + { + return array((int) $id => $id); + } + /** + * Method to get the segment(s) for an highlightform + * + * @param string $id ID of the highlightform to retrieve the segments for + * @param array $query The request that is built right now + * + * @return array|string The segments of this item + */ + public function getHighlightformSegment($id, $query) + { + return $this->getHighlightSegment($id, $query); + } + + + /** + * Method to get the segment(s) for an highlight + * + * @param string $segment Segment of the highlight to retrieve the ID for + * @param array $query The request that is parsed right now + * + * @return mixed The id of this item or false + */ + public function getHighlightId($segment, $query) + { + return (int) $segment; + } + /** + * Method to get the segment(s) for an highlightform + * + * @param string $segment Segment of the highlightform to retrieve the ID for + * @param array $query The request that is parsed right now + * + * @return mixed The id of this item or false + */ + public function getHighlightformId($segment, $query) + { + return $this->getHighlightId($segment, $query); + } + + /** + * Method to get categories from cache + * + * @param array $options The options for retrieving categories + * + * @return CategoryInterface The object containing categories + * + * @since 1.0.0 + */ + private function getCategories(array $options = []): CategoryInterface + { + $key = serialize($options); + + if (!isset($this->categoryCache[$key])) + { + $this->categoryCache[$key] = $this->categoryFactory->createCategory($options); + } + + return $this->categoryCache[$key]; + } +} diff --git a/components/com_highlights/src/View/Highlight/HtmlView.php b/components/com_highlights/src/View/Highlight/HtmlView.php new file mode 100644 index 00000000..feeee8cb --- /dev/null +++ b/components/com_highlights/src/View/Highlight/HtmlView.php @@ -0,0 +1,140 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\View\Highlight; +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; + +/** + * View class for a list of Highlights. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $state; + + protected $item; + + protected $form; + + protected $params; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $app = Factory::getApplication(); + $user = $app->getIdentity(); + + $this->state = $this->get('State'); + $this->item = $this->get('Item'); + $this->params = $app->getParams('com_highlights'); + + if (!empty($this->item)) + { + $this->form = $this->get('Form'); + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + + + + if ($this->_layout == 'edit') + { + $authorised = $user->authorise('core.create', 'com_highlights'); + + if ($authorised !== true) + { + throw new \Exception(Text::_('JERROR_ALERTNOAUTHOR')); + } + } + + $this->_prepareDocument(); + + parent::display($tpl); + } + + /** + * Prepares the document + * + * @return void + * + * @throws Exception + */ + protected function _prepareDocument() + { + $app = Factory::getApplication(); + $menus = $app->getMenu(); + $title = null; + + // Because the application sets a default page title, + // We need to get it from the menu item itself + $menu = $menus->getActive(); + + if ($menu) + { + $this->params->def('page_heading', $this->params->get('page_title', $menu->title)); + } + else + { + $this->params->def('page_heading', Text::_('COM_HIGHLIGHTS_DEFAULT_PAGE_TITLE')); + } + + $title = $this->params->get('page_title', ''); + + if (empty($title)) + { + $title = $app->get('sitename'); + } + elseif ($app->get('sitename_pagetitles', 0) == 1) + { + $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title); + } + elseif ($app->get('sitename_pagetitles', 0) == 2) + { + $title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename')); + } + + $this->document->setTitle($title); + + if ($this->params->get('menu-meta_description')) + { + $this->document->setDescription($this->params->get('menu-meta_description')); + } + + if ($this->params->get('menu-meta_keywords')) + { + $this->document->setMetadata('keywords', $this->params->get('menu-meta_keywords')); + } + + if ($this->params->get('robots')) + { + $this->document->setMetadata('robots', $this->params->get('robots')); + } + + + } +} diff --git a/components/com_highlights/src/View/Highlightform/HtmlView.php b/components/com_highlights/src/View/Highlightform/HtmlView.php new file mode 100644 index 00000000..0a7eb5e3 --- /dev/null +++ b/components/com_highlights/src/View/Highlightform/HtmlView.php @@ -0,0 +1,129 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\View\Highlightform; +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; + +/** + * View class for a list of Highlights. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $state; + + protected $item; + + protected $form; + + protected $params; + + protected $canSave; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $app = Factory::getApplication(); + $user = $app->getIdentity(); + + $this->state = $this->get('State'); + $this->item = $this->get('Item'); + $this->params = $app->getParams('com_highlights'); + $this->canSave = $this->get('CanSave'); + $this->form = $this->get('Form'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + + + + $this->_prepareDocument(); + + parent::display($tpl); + } + + /** + * Prepares the document + * + * @return void + * + * @throws Exception + */ + protected function _prepareDocument() + { + $app = Factory::getApplication(); + $menus = $app->getMenu(); + $title = null; + + // Because the application sets a default page title, + // we need to get it from the menu item itself + $menu = $menus->getActive(); + + if ($menu) + { + $this->params->def('page_heading', $this->params->get('page_title', $menu->title)); + } + else + { + $this->params->def('page_heading', Text::_('COM_HIGHLIGHTS_DEFAULT_PAGE_TITLE')); + } + + $title = $this->params->get('page_title', ''); + + if (empty($title)) + { + $title = $app->get('sitename'); + } + elseif ($app->get('sitename_pagetitles', 0) == 1) + { + $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title); + } + elseif ($app->get('sitename_pagetitles', 0) == 2) + { + $title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename')); + } + + $this->document->setTitle($title); + + if ($this->params->get('menu-meta_description')) + { + $this->document->setDescription($this->params->get('menu-meta_description')); + } + + if ($this->params->get('menu-meta_keywords')) + { + $this->document->setMetadata('keywords', $this->params->get('menu-meta_keywords')); + } + + if ($this->params->get('robots')) + { + $this->document->setMetadata('robots', $this->params->get('robots')); + } + + + } +} diff --git a/components/com_highlights/src/View/Highlights/HtmlView.php b/components/com_highlights/src/View/Highlights/HtmlView.php new file mode 100644 index 00000000..4539c042 --- /dev/null +++ b/components/com_highlights/src/View/Highlights/HtmlView.php @@ -0,0 +1,135 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Component\Highlights\Site\View\Highlights; +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; + +/** + * View class for a list of Highlights. + * + * @since 1.0.0 + */ +class HtmlView extends BaseHtmlView +{ + protected $items; + + protected $pagination; + + protected $state; + + protected $params; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $app = Factory::getApplication(); + + $this->state = $this->get('State'); + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->params = $app->getParams('com_highlights'); + + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + + $this->_prepareDocument(); + parent::display($tpl); + } + + /** + * Prepares the document + * + * @return void + * + * @throws Exception + */ + protected function _prepareDocument() + { + $app = Factory::getApplication(); + $menus = $app->getMenu(); + $title = null; + + // Because the application sets a default page title, + // we need to get it from the menu item itself + $menu = $menus->getActive(); + + if ($menu) + { + $this->params->def('page_heading', $this->params->get('page_title', $menu->title)); + } + else + { + $this->params->def('page_heading', Text::_('COM_HIGHLIGHTS_DEFAULT_PAGE_TITLE')); + } + + $title = $this->params->get('page_title', ''); + + if (empty($title)) + { + $title = $app->get('sitename'); + } + elseif ($app->get('sitename_pagetitles', 0) == 1) + { + $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title); + } + elseif ($app->get('sitename_pagetitles', 0) == 2) + { + $title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename')); + } + + $this->document->setTitle($title); + + if ($this->params->get('menu-meta_description')) + { + $this->document->setDescription($this->params->get('menu-meta_description')); + } + + if ($this->params->get('menu-meta_keywords')) + { + $this->document->setMetadata('keywords', $this->params->get('menu-meta_keywords')); + } + + if ($this->params->get('robots')) + { + $this->document->setMetadata('robots', $this->params->get('robots')); + } + + + } + + /** + * Check if state is set + * + * @param mixed $state State + * + * @return bool + */ + public function getState($state) + { + return isset($this->state->{$state}) ? $this->state->{$state} : false; + } +} diff --git a/components/com_highlights/tmpl/highlight/default.php b/components/com_highlights/tmpl/highlight/default.php new file mode 100644 index 00000000..8ae7efc0 --- /dev/null +++ b/components/com_highlights/tmpl/highlight/default.php @@ -0,0 +1,147 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Session\Session; +use Joomla\Utilities\ArrayHelper; + +$canEdit = Factory::getApplication()->getIdentity()->authorise('core.edit', 'com_highlights'); + +if (!$canEdit && Factory::getApplication()->getIdentity()->authorise('core.edit.own', 'com_highlights')) +{ + $canEdit = Factory::getApplication()->getIdentity()->id == $this->item->created_by; +} +?> + +
+params->get('show_page_heading')) : ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
item->etichetta; ?>
item->titolo; ?>
item->sottotitolo; ?>
item->descrizione); ?>
item->lingua; ?>
item->link_pulsante; ?>
item->testo_pulsante; ?>
item->data; + echo $date > 0 ? HTMLHelper::_('date', $date, Text::_('DATE_FORMAT_LC4')) : '-'; + ?> + +
item->immagine_main; ?>
item->immagine_secondaria; ?>
item->data_inizio_pubblicazione; + echo $date > 0 ? HTMLHelper::_('date', $date, Text::_('DATE_FORMAT_LC4')) : '-'; + ?> + +
item->data_fine_pubblicazione; + echo $date > 0 ? HTMLHelper::_('date', $date, Text::_('DATE_FORMAT_LC4')) : '-'; + ?> + +
+ +
+ +getIdentity()->authorise('core.manage', 'com_highlights.' . $this->item->id) || $this->item->checked_out == Factory::getApplication()->getIdentity()->id; ?> + item->checked_out == 0): ?> + + + item->checked_out > 0) : ?> + + + + +getIdentity()->authorise('core.delete','com_highlights.highlight.'.$this->item->id)) : ?> + + + + + + Text::_('COM_HIGHLIGHTS_DELETE_ITEM'), + 'height' => '50%', + 'width' => '20%', + + 'modalWidth' => '50', + 'bodyHeight' => '100', + 'footer' => '' . Text::_('COM_HIGHLIGHTS_DELETE_ITEM') .'' + ), + Text::sprintf('COM_HIGHLIGHTS_DELETE_CONFIRM', $this->item->id) + ); ?> + + \ No newline at end of file diff --git a/components/com_highlights/tmpl/highlight/default.xml b/components/com_highlights/tmpl/highlight/default.xml new file mode 100644 index 00000000..7c91098c --- /dev/null +++ b/components/com_highlights/tmpl/highlight/default.xml @@ -0,0 +1,24 @@ + + + + + + + + +
+ + +
+
+
diff --git a/components/com_highlights/tmpl/highlight/index.html b/components/com_highlights/tmpl/highlight/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/components/com_highlights/tmpl/highlight/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/com_highlights/tmpl/highlightform/default.php b/components/com_highlights/tmpl/highlightform/default.php new file mode 100644 index 00000000..158bcb8f --- /dev/null +++ b/components/com_highlights/tmpl/highlightform/default.php @@ -0,0 +1,117 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; +use \Pcrt\Component\Highlights\Site\Helper\HighlightsHelper; + +$wa = $this->document->getWebAssetManager(); +$wa->useScript('keepalive') + ->useScript('form.validate'); +HTMLHelper::_('bootstrap.tooltip'); + +// Load admin language file +$lang = Factory::getLanguage(); +$lang->load('com_highlights', JPATH_SITE); + +$user = Factory::getApplication()->getIdentity(); +$canEdit = HighlightsHelper::canUserEdit($this->item, $user); + + +?> + +
+ +params->get('show_page_heading')) : ?> + + + +

+ +

+ + item->id)): ?> +

item->id); ?>

+ +

+ + +
+ + + + + + form->getInput('created_by'); ?> + form->getInput('modified_by'); ?> + 'highlight')); ?> + + form->renderField('etichetta'); ?> + + form->renderField('titolo'); ?> + + form->renderField('sottotitolo'); ?> + + form->renderField('descrizione'); ?> + + form->renderField('lingua'); ?> + + form->renderField('link_pulsante'); ?> + + form->renderField('testo_pulsante'); ?> + + form->renderField('data'); ?> + + + + form->renderField('immagine_main'); ?> + + form->renderField('immagine_secondaria'); ?> + + + + form->renderField('data_inizio_pubblicazione'); ?> + + form->renderField('data_fine_pubblicazione'); ?> + + +
+
+ + canSave): ?> + + + + + + +
+
+ + + + +
+ +
diff --git a/components/com_highlights/tmpl/highlightform/default.xml b/components/com_highlights/tmpl/highlightform/default.xml new file mode 100644 index 00000000..e3675102 --- /dev/null +++ b/components/com_highlights/tmpl/highlightform/default.xml @@ -0,0 +1,24 @@ + + + + + + + + +
+ + +
+
+
diff --git a/components/com_highlights/tmpl/highlightform/index.html b/components/com_highlights/tmpl/highlightform/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/components/com_highlights/tmpl/highlightform/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/com_highlights/tmpl/highlights/default.php b/components/com_highlights/tmpl/highlights/default.php new file mode 100644 index 00000000..2f5ec677 --- /dev/null +++ b/components/com_highlights/tmpl/highlights/default.php @@ -0,0 +1,167 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Layout\LayoutHelper; +use \Joomla\CMS\Session\Session; +use \Joomla\CMS\User\UserFactoryInterface; + +HTMLHelper::_('bootstrap.tooltip'); +HTMLHelper::_('behavior.multiselect'); +HTMLHelper::_('formbehavior.chosen', 'select'); + +$user = Factory::getApplication()->getIdentity(); +$userId = $user->get('id'); +$listOrder = $this->state->get('list.ordering'); +$listDirn = $this->state->get('list.direction'); +$canCreate = $user->authorise('core.create', 'com_highlights') && file_exists(JPATH_COMPONENT . DIRECTORY_SEPARATOR . 'forms' . DIRECTORY_SEPARATOR . 'highlightform.xml'); +$canEdit = $user->authorise('core.edit', 'com_highlights') && file_exists(JPATH_COMPONENT . DIRECTORY_SEPARATOR . 'forms' . DIRECTORY_SEPARATOR . 'highlightform.xml'); +$canCheckin = $user->authorise('core.manage', 'com_highlights'); +$canChange = $user->authorise('core.edit.state', 'com_highlights'); +$canDelete = $user->authorise('core.delete', 'com_highlights'); + +// Import CSS +$wa = $this->document->getWebAssetManager(); +$wa->useStyle('com_highlights.list'); +?> + +params->get('show_page_heading')) : ?> + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + items as $i => $item) : ?> + authorise('core.edit', 'com_highlights'); ?> + authorise('core.edit.own', 'com_highlights')): ?> + getIdentity()->id == $item->created_by; ?> + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ id; ?> + + + + state == 1): ?> + + + + + + + getIdentity()->authorise('core.manage', 'com_highlights.' . $item->id) || $item->checked_out == Factory::getApplication()->getIdentity()->id; ?> + checked_out > 0) : ?> + + uEditor, $item->checked_out_time, 'highlight.', false); ?> + + + escape($item->titolo); ?> + + etichetta; ?> + + getIdentity()->authorise('core.manage', 'com_highlights.' . $item->id) || $item->checked_out == Factory::getApplication()->getIdentity()->id; ?> + + checked_out == 0): ?> + + + + + +
+
+ + + + + + + + + + +
+ +addInlineScript(" + jQuery(document).ready(function () { + jQuery('.delete-button').click(deleteItem); + }); + + function deleteItem() { + + if (!confirm(\"" . Text::_('COM_HIGHLIGHTS_DELETE_MESSAGE') . "\")) { + return false; + } + } + ", [], [], ["jquery"]); + } +?> \ No newline at end of file diff --git a/components/com_highlights/tmpl/highlights/default.xml b/components/com_highlights/tmpl/highlights/default.xml new file mode 100644 index 00000000..73f04ef1 --- /dev/null +++ b/components/com_highlights/tmpl/highlights/default.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/com_highlights/tmpl/index.html b/components/com_highlights/tmpl/index.html new file mode 100644 index 00000000..42682b47 --- /dev/null +++ b/components/com_highlights/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/db-latest.sql.zip b/db-latest.sql.zip index 9c17ceed..6469b9ad 100644 Binary files a/db-latest.sql.zip and b/db-latest.sql.zip differ diff --git a/language/en-GB/com_highlights.ini b/language/en-GB/com_highlights.ini new file mode 100644 index 00000000..33ec4076 --- /dev/null +++ b/language/en-GB/com_highlights.ini @@ -0,0 +1,126 @@ +COM_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_ITEM_SAVED_SUCCESSFULLY = "Item saved successfully" +COM_HIGHLIGHTS_ITEM_DELETED_SUCCESSFULLY = "Item deleted successfully" +COM_HIGHLIGHTS_ITEM_DELETED_UNSUCCESSFULLY = "Could not delete item" +COM_HIGHLIGHTS_ITEM_DOESNT_EXIST = "Item does not exist" +COM_HIGHLIGHTS_ITEM_NOT_LOADED = "Could not load the item" +COM_HIGHLIGHTS_VIEW_FILE = "[View File]" +COM_HIGHLIGHTS_ADD_ITEM = "Add" +COM_HIGHLIGHTS_EDIT_ITEM = "Edit" +COM_HIGHLIGHTS_DELETE_ITEM = "Delete" +COM_HIGHLIGHTS_DELETE_MESSAGE = "Are you sure that you want delete this item?" +COM_HIGHLIGHTS_DELETE_CONFIRM = "You're about to delete the item #%s. Are you sure?" +COM_HIGHLIGHTS_PUBLISH_ITEM = "Publish" +COM_HIGHLIGHTS_UNPUBLISH_ITEM = "Unpublish" +COM_HIGHLIGHTS_NO_ITEMS = "There are no items in the list" +COM_HIGHLIGHTS_SEARCH_TOOLS = "Search Tools" +COM_HIGHLIGHTS_SEARCH_TOOLS_DESC = "" +COM_HIGHLIGHTS_SEARCH_FILTER_SUBMIT = "Search" +COM_HIGHLIGHTS_SEARCH_FILTER_CLEAR = "Clear" +COM_HIGHLIGHTS_SEARCH_FILTER_DATE_FORMAT = "Dates should be in this format: \"YYYY-MM-DD\". Some filter fields have been ignored." + +COM_HIGHLIGHTS_ERROR_MESSAGE_NOT_AUTHORISED = "You don't have permission to access this. Please contact a website administrator if this is incorrect." + +COM_HIGHLIGHTS_ADD_ITEM_TITLE = "Add" +COM_HIGHLIGHTS_EDIT_ITEM_TITLE = "Edit %s" + +COM_HIGHLIGHTS_ACTION_PERMISSIONS_LABEL = "Permissions" +COM_HIGHLIGHTS_VALIDATION_FORM_FAILED = "Invalid form" +COM_HIGHLIGHTS_CHECKEDIN_SUCCESSFULLY = "Item successfully checked in" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHTS="Highlights" +COM_HIGHLIGHTS_HIGHLIGHTS_ID="ID" +COM_HIGHLIGHTS_ID_DESC="ID Descending" +COM_HIGHLIGHTS_ID_ASC="ID Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_STATE="State" +COM_HIGHLIGHTS_STATE_DESC="State Descending" +COM_HIGHLIGHTS_STATE_ASC="State Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_ORDERING="Order" +COM_HIGHLIGHTS_ORDERING_DESC="Order Descending" +COM_HIGHLIGHTS_ORDERING_ASC="Order Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT="N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT_TIME="N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CREATED_BY="Created by" +COM_HIGHLIGHTS_HIGHLIGHTS_MODIFIED_BY="Modified by" +COM_HIGHLIGHTS_HIGHLIGHTS_ETICHETTA="Etichetta" +COM_HIGHLIGHTS_ETICHETTA_DESC="Etichetta Descending" +COM_HIGHLIGHTS_ETICHETTA_ASC="Etichetta Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_TITOLO="Titolo" +COM_HIGHLIGHTS_TITOLO_DESC="Titolo Descending" +COM_HIGHLIGHTS_TITOLO_ASC="Titolo Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_SOTTOTITOLO="Sottotitolo" +COM_HIGHLIGHTS_HIGHLIGHTS_DESCRIZIONE="Descrizione" +COM_HIGHLIGHTS_HIGHLIGHTS_LINGUA="Lingua" +COM_HIGHLIGHTS_HIGHLIGHTS_LINK_PULSANTE="Link Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_TESTO_PULSANTE="Testo Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA="Data" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_MAIN="Immagine Main" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_SECONDARIA="Immagine Secondaria" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE="Data Inizio Pubblicazione" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE="Data Fine Pubblicazione" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHT="Item" +COM_HIGHLIGHTS_LEGEND_HIGHLIGHT="Item" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ID="ID" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ID="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_STATE="State" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_STATE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ORDERING="Order" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ORDERING="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT="N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT_TIME="N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT_TIME="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CREATED_BY="Created by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CREATED_BY="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_MODIFIED_BY="Modified by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_MODIFIED_BY="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ETICHETTA="Etichetta" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ETICHETTA="" +COM_HIGHLIGHTS_TAB_HIGHLIGHT="Highlight" +COM_HIGHLIGHTS_FIELDSET_HIGHLIGHT="Highlight" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TITOLO="Titolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TITOLO="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_SOTTOTITOLO="Sottotitolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_SOTTOTITOLO="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DESCRIZIONE="Descrizione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DESCRIZIONE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINGUA="Lingua" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINGUA="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINK_PULSANTE="Link Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINK_PULSANTE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TESTO_PULSANTE="Testo Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TESTO_PULSANTE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA="Data" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_MAIN="Immagine Main" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_MAIN="" +COM_HIGHLIGHTS_TAB_IMMAGINI="Immagini" +COM_HIGHLIGHTS_FIELDSET_IMMAGINI="Immagini" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_SECONDARIA="Immagine Secondaria" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_SECONDARIA="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE="Data Inizio Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE="" +COM_HIGHLIGHTS_TAB_PUBBLICAZIONE="Pubblicazione" +COM_HIGHLIGHTS_FIELDSET_PUBBLICAZIONE="Pubblicazione" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE="Data Fine Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE="" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS="Highlights" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS_DESC="Show a list of Highlights" +COM_HIGHLIGHTS_HIGHLIGHTS_ACTIONS="Actions" +COM_HIGHLIGHTS_HIGHLIGHTS_EDIT="Edit" +COM_HIGHLIGHTS_HIGHLIGHTS_DELETE="Delete" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT="Single Highlight" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT_DESC="Show a specific Highlight" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT="HighlightForm" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT_DESC="Show a form to add or edit a Highlight" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ID="ID" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_STATE="State" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ORDERING="Order" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT="N/A" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT_TIME="N/A" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CREATED_BY="Created by" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_MODIFIED_BY="Modified by" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_NOME="Nome" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_LINGUA="Lingua" + diff --git a/language/en-GB/mod_highlights.ini b/language/en-GB/mod_highlights.ini new file mode 100644 index 00000000..e0c6ca84 --- /dev/null +++ b/language/en-GB/mod_highlights.ini @@ -0,0 +1,59 @@ +MOD_HIGHLIGHTS_NAME="Module - Highlights" +MOD_HIGHLIGHTS_DESCRIPTION="Module for showing content of Highlights" +MOD_HIGHLIGHTS_CUSTOM_CONTENT="Custom Content" +MOD_HIGHLIGHTS_LIST="List" +MOD_HIGHLIGHTS_ITEM="Item" +MOD_HIGHLIGHTS_DIDATTICA="Didattica" +MOD_HIGHLIGHTS_SLIDE="Slide" +MOD_HIGHLIGHTS_COUNTDOWN="Countdown" + +MOD_HIGHLIGHTS_CONTENT_TYPE_TAB_LBL="Content type" +MOD_HIGHLIGHTS_CONTENT_TYPE_LBL="Content type" +MOD_HIGHLIGHTS_CONTENT_TYPE_DESC="Type of content the module should show" +MOD_HIGHLIGHTS_CUSTOM_CONTENT_TAB_LBL="Custom content" +MOD_HIGHLIGHTS_CUSTOM_CONTENT_TAB_DESC="Add you own HTML code to show it as a module" +MOD_HIGHLIGHTS_HTML_LBL="HTML Code" +MOD_HIGHLIGHTS_HTML_DESC="Write your own HTML code to show it as a module" +MOD_HIGHLIGHTS_LIST_CONTENT_TAB_LBL="List Content" +MOD_HIGHLIGHTS_LIST_CONTENT_TAB_DESC="Create a list of items showing the selected field content." +MOD_HIGHLIGHTS_TABLE_LBL="Table" +MOD_HIGHLIGHTS_TABLE_DESC="Table name" +MOD_HIGHLIGHTS_FIELD_LBL="Field" +MOD_HIGHLIGHTS_FIELD_DESC="Which field you want to show" +MOD_HIGHLIGHTS_OFFSET_LBL="Offset" +MOD_HIGHLIGHTS_OFFSET_DESC="How many items you want to skip" +MOD_HIGHLIGHTS_LIMIT_LBL="Limit" +MOD_HIGHLIGHTS_LIMIT_DESC="How many records you want to show" +MOD_HIGHLIGHTS_ITEM_CONTENT_TAB="Item content" +MOD_HIGHLIGHTS_ITEM_CONTENT_TAB_DESC="Show a specific item from one of your component table" +MOD_HIGHLIGHTS_ITEM_ID_LBL="Item ID" +MOD_HIGHLIGHTS_ITEM_ID_DESC="The ID of the item you want to show" +MOD_HIGHLIGHTS_CONTENT_TYPE_TAB_DESC="Configure which type of content you want to show" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__ID="ID" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__STATE="State" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__ORDERING="Order" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__CHECKED_OUT="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__CHECKED_OUT_TIME="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__CREATED_BY="Created by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__MODIFIED_BY="Modified by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__ETICHETTA="Etichetta" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__TITOLO="Titolo" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__SOTTOTITOLO="Sottotitolo" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DESCRIZIONE="Descrizione" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__LINGUA="Lingua" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__LINK_PULSANTE="Link Pulsante" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__TESTO_PULSANTE="Testo Pulsante" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DATA="Data" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__IMMAGINE_MAIN="Immagine Main" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__IMMAGINE_SECONDARIA="Immagine Secondaria" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DATA_INIZIO_PUBBLICAZIONE="Data Inizio Pubblicazione" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DATA_FINE_PUBBLICAZIONE="Data Fine Pubblicazione" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_ID="ID" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_STATE="State" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_ORDERING="Order" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_CHECKED_OUT="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_CHECKED_OUT_TIME="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_CREATED_BY="Created by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_MODIFIED_BY="Modified by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_NOME="Nome" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_LINGUA="Lingua" diff --git a/language/en-GB/mod_highlights.sys.ini b/language/en-GB/mod_highlights.sys.ini new file mode 100644 index 00000000..5c8f0cdb --- /dev/null +++ b/language/en-GB/mod_highlights.sys.ini @@ -0,0 +1,2 @@ +MOD_HIGHLIGHTS_NAME="Module - Highlights" +MOD_HIGHLIGHTS_DESCRIPTION="Module for showing content of Highlights" \ No newline at end of file diff --git a/language/it-IT/com_highlights.ini b/language/it-IT/com_highlights.ini new file mode 100644 index 00000000..33ec4076 --- /dev/null +++ b/language/it-IT/com_highlights.ini @@ -0,0 +1,126 @@ +COM_HIGHLIGHTS = "Highlights" +COM_HIGHLIGHTS_ITEM_SAVED_SUCCESSFULLY = "Item saved successfully" +COM_HIGHLIGHTS_ITEM_DELETED_SUCCESSFULLY = "Item deleted successfully" +COM_HIGHLIGHTS_ITEM_DELETED_UNSUCCESSFULLY = "Could not delete item" +COM_HIGHLIGHTS_ITEM_DOESNT_EXIST = "Item does not exist" +COM_HIGHLIGHTS_ITEM_NOT_LOADED = "Could not load the item" +COM_HIGHLIGHTS_VIEW_FILE = "[View File]" +COM_HIGHLIGHTS_ADD_ITEM = "Add" +COM_HIGHLIGHTS_EDIT_ITEM = "Edit" +COM_HIGHLIGHTS_DELETE_ITEM = "Delete" +COM_HIGHLIGHTS_DELETE_MESSAGE = "Are you sure that you want delete this item?" +COM_HIGHLIGHTS_DELETE_CONFIRM = "You're about to delete the item #%s. Are you sure?" +COM_HIGHLIGHTS_PUBLISH_ITEM = "Publish" +COM_HIGHLIGHTS_UNPUBLISH_ITEM = "Unpublish" +COM_HIGHLIGHTS_NO_ITEMS = "There are no items in the list" +COM_HIGHLIGHTS_SEARCH_TOOLS = "Search Tools" +COM_HIGHLIGHTS_SEARCH_TOOLS_DESC = "" +COM_HIGHLIGHTS_SEARCH_FILTER_SUBMIT = "Search" +COM_HIGHLIGHTS_SEARCH_FILTER_CLEAR = "Clear" +COM_HIGHLIGHTS_SEARCH_FILTER_DATE_FORMAT = "Dates should be in this format: \"YYYY-MM-DD\". Some filter fields have been ignored." + +COM_HIGHLIGHTS_ERROR_MESSAGE_NOT_AUTHORISED = "You don't have permission to access this. Please contact a website administrator if this is incorrect." + +COM_HIGHLIGHTS_ADD_ITEM_TITLE = "Add" +COM_HIGHLIGHTS_EDIT_ITEM_TITLE = "Edit %s" + +COM_HIGHLIGHTS_ACTION_PERMISSIONS_LABEL = "Permissions" +COM_HIGHLIGHTS_VALIDATION_FORM_FAILED = "Invalid form" +COM_HIGHLIGHTS_CHECKEDIN_SUCCESSFULLY = "Item successfully checked in" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHTS="Highlights" +COM_HIGHLIGHTS_HIGHLIGHTS_ID="ID" +COM_HIGHLIGHTS_ID_DESC="ID Descending" +COM_HIGHLIGHTS_ID_ASC="ID Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_STATE="State" +COM_HIGHLIGHTS_STATE_DESC="State Descending" +COM_HIGHLIGHTS_STATE_ASC="State Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_ORDERING="Order" +COM_HIGHLIGHTS_ORDERING_DESC="Order Descending" +COM_HIGHLIGHTS_ORDERING_ASC="Order Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT="N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CHECKED_OUT_TIME="N/A" +COM_HIGHLIGHTS_HIGHLIGHTS_CREATED_BY="Created by" +COM_HIGHLIGHTS_HIGHLIGHTS_MODIFIED_BY="Modified by" +COM_HIGHLIGHTS_HIGHLIGHTS_ETICHETTA="Etichetta" +COM_HIGHLIGHTS_ETICHETTA_DESC="Etichetta Descending" +COM_HIGHLIGHTS_ETICHETTA_ASC="Etichetta Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_TITOLO="Titolo" +COM_HIGHLIGHTS_TITOLO_DESC="Titolo Descending" +COM_HIGHLIGHTS_TITOLO_ASC="Titolo Ascending" +COM_HIGHLIGHTS_HIGHLIGHTS_SOTTOTITOLO="Sottotitolo" +COM_HIGHLIGHTS_HIGHLIGHTS_DESCRIZIONE="Descrizione" +COM_HIGHLIGHTS_HIGHLIGHTS_LINGUA="Lingua" +COM_HIGHLIGHTS_HIGHLIGHTS_LINK_PULSANTE="Link Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_TESTO_PULSANTE="Testo Pulsante" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA="Data" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_MAIN="Immagine Main" +COM_HIGHLIGHTS_HIGHLIGHTS_IMMAGINE_SECONDARIA="Immagine Secondaria" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_INIZIO_PUBBLICAZIONE="Data Inizio Pubblicazione" +COM_HIGHLIGHTS_HIGHLIGHTS_DATA_FINE_PUBBLICAZIONE="Data Fine Pubblicazione" + +COM_HIGHLIGHTS_TITLE_HIGHLIGHT="Item" +COM_HIGHLIGHTS_LEGEND_HIGHLIGHT="Item" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ID="ID" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ID="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_STATE="State" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_STATE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ORDERING="Order" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ORDERING="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT="N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CHECKED_OUT_TIME="N/A" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CHECKED_OUT_TIME="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_CREATED_BY="Created by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_CREATED_BY="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_MODIFIED_BY="Modified by" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_MODIFIED_BY="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_ETICHETTA="Etichetta" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_ETICHETTA="" +COM_HIGHLIGHTS_TAB_HIGHLIGHT="Highlight" +COM_HIGHLIGHTS_FIELDSET_HIGHLIGHT="Highlight" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TITOLO="Titolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TITOLO="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_SOTTOTITOLO="Sottotitolo" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_SOTTOTITOLO="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DESCRIZIONE="Descrizione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DESCRIZIONE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINGUA="Lingua" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINGUA="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_LINK_PULSANTE="Link Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_LINK_PULSANTE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_TESTO_PULSANTE="Testo Pulsante" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_TESTO_PULSANTE="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA="Data" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_MAIN="Immagine Main" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_MAIN="" +COM_HIGHLIGHTS_TAB_IMMAGINI="Immagini" +COM_HIGHLIGHTS_FIELDSET_IMMAGINI="Immagini" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_IMMAGINE_SECONDARIA="Immagine Secondaria" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_IMMAGINE_SECONDARIA="" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE="Data Inizio Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_INIZIO_PUBBLICAZIONE="" +COM_HIGHLIGHTS_TAB_PUBBLICAZIONE="Pubblicazione" +COM_HIGHLIGHTS_FIELDSET_PUBBLICAZIONE="Pubblicazione" +COM_HIGHLIGHTS_FORM_LBL_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE="Data Fine Pubblicazione" +COM_HIGHLIGHTS_FORM_DESC_HIGHLIGHT_DATA_FINE_PUBBLICAZIONE="" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS="Highlights" +COM_HIGHLIGHTS_TITLE_LIST_VIEW_HIGHLIGHTS_DESC="Show a list of Highlights" +COM_HIGHLIGHTS_HIGHLIGHTS_ACTIONS="Actions" +COM_HIGHLIGHTS_HIGHLIGHTS_EDIT="Edit" +COM_HIGHLIGHTS_HIGHLIGHTS_DELETE="Delete" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT="Single Highlight" +COM_HIGHLIGHTS_TITLE_ITEM_VIEW_HIGHLIGHT_DESC="Show a specific Highlight" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT="HighlightForm" +COM_HIGHLIGHTS_TITLE_FORM_VIEW_HIGHLIGHT_DESC="Show a form to add or edit a Highlight" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ID="ID" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_STATE="State" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_ORDERING="Order" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT="N/A" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CHECKED_OUT_TIME="N/A" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_CREATED_BY="Created by" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_MODIFIED_BY="Modified by" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_NOME="Nome" +COM_HIGHLIGHTS_FORM_LBL_ETICHETTA_LINGUA="Lingua" + diff --git a/language/it-IT/mod_highlights.ini b/language/it-IT/mod_highlights.ini new file mode 100644 index 00000000..e8896fec --- /dev/null +++ b/language/it-IT/mod_highlights.ini @@ -0,0 +1,57 @@ +MOD_HIGHLIGHTS_NAME="Module - Highlights" +MOD_HIGHLIGHTS_DESCRIPTION="Module for showing content of Highlights" +MOD_HIGHLIGHTS_CUSTOM_CONTENT="Custom Content" +MOD_HIGHLIGHTS_LIST="List" +MOD_HIGHLIGHTS_ITEM="Item" +MOD_HIGHLIGHTS_DIDATTICA="Didattica" + +MOD_HIGHLIGHTS_CONTENT_TYPE_TAB_LBL="Content type" +MOD_HIGHLIGHTS_CONTENT_TYPE_LBL="Content type" +MOD_HIGHLIGHTS_CONTENT_TYPE_DESC="Type of content the module should show" +MOD_HIGHLIGHTS_CUSTOM_CONTENT_TAB_LBL="Custom content" +MOD_HIGHLIGHTS_CUSTOM_CONTENT_TAB_DESC="Add you own HTML code to show it as a module" +MOD_HIGHLIGHTS_HTML_LBL="HTML Code" +MOD_HIGHLIGHTS_HTML_DESC="Write your own HTML code to show it as a module" +MOD_HIGHLIGHTS_LIST_CONTENT_TAB_LBL="List Content" +MOD_HIGHLIGHTS_LIST_CONTENT_TAB_DESC="Create a list of items showing the selected field content." +MOD_HIGHLIGHTS_TABLE_LBL="Table" +MOD_HIGHLIGHTS_TABLE_DESC="Table name" +MOD_HIGHLIGHTS_FIELD_LBL="Field" +MOD_HIGHLIGHTS_FIELD_DESC="Which field you want to show" +MOD_HIGHLIGHTS_OFFSET_LBL="Offset" +MOD_HIGHLIGHTS_OFFSET_DESC="How many items you want to skip" +MOD_HIGHLIGHTS_LIMIT_LBL="Limit" +MOD_HIGHLIGHTS_LIMIT_DESC="How many records you want to show" +MOD_HIGHLIGHTS_ITEM_CONTENT_TAB="Item content" +MOD_HIGHLIGHTS_ITEM_CONTENT_TAB_DESC="Show a specific item from one of your component table" +MOD_HIGHLIGHTS_ITEM_ID_LBL="Item ID" +MOD_HIGHLIGHTS_ITEM_ID_DESC="The ID of the item you want to show" +MOD_HIGHLIGHTS_CONTENT_TYPE_TAB_DESC="Configure which type of content you want to show" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__ID="ID" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__STATE="State" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__ORDERING="Order" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__CHECKED_OUT="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__CHECKED_OUT_TIME="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__CREATED_BY="Created by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__MODIFIED_BY="Modified by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__ETICHETTA="Etichetta" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__TITOLO="Titolo" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__SOTTOTITOLO="Sottotitolo" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DESCRIZIONE="Descrizione" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__LINGUA="Lingua" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__LINK_PULSANTE="Link Pulsante" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__TESTO_PULSANTE="Testo Pulsante" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DATA="Data" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__IMMAGINE_MAIN="Immagine Main" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__IMMAGINE_SECONDARIA="Immagine Secondaria" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DATA_INIZIO_PUBBLICAZIONE="Data Inizio Pubblicazione" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS__DATA_FINE_PUBBLICAZIONE="Data Fine Pubblicazione" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_ID="ID" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_STATE="State" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_ORDERING="Order" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_CHECKED_OUT="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_CHECKED_OUT_TIME="N/A" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_CREATED_BY="Created by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_MODIFIED_BY="Modified by" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_NOME="Nome" +MOD_HIGHLIGHTS_HEADER_FIELD_HIGHLIGHTS_ETICHETTA_LINGUA="Lingua" diff --git a/language/it-IT/mod_highlights.sys.ini b/language/it-IT/mod_highlights.sys.ini new file mode 100644 index 00000000..5c8f0cdb --- /dev/null +++ b/language/it-IT/mod_highlights.sys.ini @@ -0,0 +1,2 @@ +MOD_HIGHLIGHTS_NAME="Module - Highlights" +MOD_HIGHLIGHTS_DESCRIPTION="Module for showing content of Highlights" \ No newline at end of file diff --git a/media/com_highlights/css/admin.css b/media/com_highlights/css/admin.css new file mode 100644 index 00000000..41e2e7bc --- /dev/null +++ b/media/com_highlights/css/admin.css @@ -0,0 +1,11 @@ +.jcc-color-box { + float: left; + width: 15px; + height: 15px; + margin-right: 5px; + border: 1px solid rgba(0, 0, 0, .2); +} + +.other-filters{ + padding: 0 14px; +} \ No newline at end of file diff --git a/media/com_highlights/css/list.css b/media/com_highlights/css/list.css new file mode 100644 index 00000000..d4f8bf21 --- /dev/null +++ b/media/com_highlights/css/list.css @@ -0,0 +1,7 @@ +/** + * @version CVS: 1.0.0 + * @package com_highlights + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + * @author Eddy Prosperi + */ diff --git a/media/com_highlights/joomla.asset.json b/media/com_highlights/joomla.asset.json new file mode 100644 index 00000000..3a4f3766 --- /dev/null +++ b/media/com_highlights/joomla.asset.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json", + "name": "com_highlights", + "version": "CVS: 1.0.0", + "license": "GNU General Public License versione 2 o successiva; vedi LICENSE.txt", + "assets": [ + { + "name": "com_highlights.admin", + "type": "style", + "uri": "com_highlights/admin.css" + }, + { + "name": "com_highlights.admin", + "type": "script", + "uri": "com_highlights/admin.js", + "dependencies": [ + "jquery" + ], + "attributes": { + "defer": true + } + }, + { + "name": "com_highlights.list", + "type": "style", + "uri": "com_highlights/list.css" + } + ] +} diff --git a/media/com_highlights/js/admin.js b/media/com_highlights/js/admin.js new file mode 100644 index 00000000..115e0fb8 --- /dev/null +++ b/media/com_highlights/js/admin.js @@ -0,0 +1,32 @@ +window.Joomla = window.Joomla || {}; + +(function (window, Joomla) { + Joomla.toggleField = function (id, task, field) { + + var f = document.adminForm, i = 0, cbx, cb = f[ id ]; + + if (!cb) return false; + + while (true) { + cbx = f[ 'cb' + i ]; + + if (!cbx) break; + + cbx.checked = false; + i++; + } + + var inputField = document.createElement('input'); + + inputField.type = 'hidden'; + inputField.name = 'field'; + inputField.value = field; + f.appendChild(inputField); + + cb.checked = true; + f.boxchecked.value = 1; + Joomla.submitform(task); + + return false; + }; +})(window, Joomla); \ No newline at end of file diff --git a/media/mod_highlights/css/style.css b/media/mod_highlights/css/style.css new file mode 100644 index 00000000..825d8433 --- /dev/null +++ b/media/mod_highlights/css/style.css @@ -0,0 +1,26 @@ +/** + * @version CVS: 1.0.0 + * @package com_highlights + * @subpackage mod_highlights + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + * @author Eddy Prosperi + */ + +.jcc-table { + width: 100%; + margin-bottom: 18px; +} + +.jcc-table tr{ + border: none; +} + +.jcc-table td, .jcc-table th{ + padding: 8px; + line-height: 18px; + text-align: left; + vertical-align: top; + border: none; + border-top: 1px solid #ddd; +} \ No newline at end of file diff --git a/media/mod_highlights/joomla.asset.json b/media/mod_highlights/joomla.asset.json new file mode 100644 index 00000000..982312c4 --- /dev/null +++ b/media/mod_highlights/joomla.asset.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json", + "name": "mod_highlights", + "version": "CVS: 1.0.0", + "license": "GNU General Public License versione 2 o successiva; vedi LICENSE.txt", + "assets": [ + { + "name": "mod_highlights.style", + "type": "style", + "uri": "mod_highlights/style.css" + }, + { + "name": "mod_highlights.script", + "type": "script", + "uri": "mod_highlights/script.js" + } + ] +} diff --git a/media/mod_highlights/js/script.js b/media/mod_highlights/js/script.js new file mode 100644 index 00000000..62b87bd4 --- /dev/null +++ b/media/mod_highlights/js/script.js @@ -0,0 +1,10 @@ +/** + * @version CVS: 1.0.0 + * @package com_highlights + * @subpackage mod_highlights + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + * @author Eddy Prosperi + */ + + diff --git a/media/plg_fields_acfaddress/img/marker.png b/media/plg_fields_acfaddress/img/marker.png new file mode 100644 index 00000000..e134b6c5 Binary files /dev/null and b/media/plg_fields_acfaddress/img/marker.png differ diff --git a/media/plg_fields_acfarticles/css/style.css b/media/plg_fields_acfarticles/css/style.css new file mode 100644 index 00000000..da9d23d4 --- /dev/null +++ b/media/plg_fields_acfarticles/css/style.css @@ -0,0 +1,3 @@ +.acfarticles-field-wrapper .acfarticle-item{--title-color: #333;--title-hover-color: #000;--gap: 4px;--color: #000;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;gap:var(--gap);color:var(--color)}.acfarticles-field-wrapper .acfarticle-item--content--title{text-decoration:none;color:var(--title-color);line-height:1.3;font-size:20px}.acfarticles-field-wrapper .acfarticle-item--content--title:hover{text-decoration:underline;color:var(--title-hover-color)}.acfarticles-field-wrapper.layout-grid{display:grid;grid-template-columns:repeat(var(--columns), 1fr);grid-gap:var(--gap)}.acfarticles-field-wrapper.layout-grid.layout-styleb .acfarticle-item{--image-width: 200px;--gap: 16px;--direction: column;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:var(--direction);flex-direction:var(--direction)}@media only screen and (min-width: 576px){.acfarticles-field-wrapper.layout-grid.layout-styleb .acfarticle-item{--direction: row}.acfarticles-field-wrapper.layout-grid.layout-styleb .acfarticle-item--image{width:var(--image-width)}}.acfarticles-field-wrapper.layout-grid .acfarticle-item{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.acfarticles-field-wrapper.layout-grid .acfarticle-item--content--title{font-weight:bold}.acfarticles-field-wrapper.layout-grid .acfarticle-item--content{--content-gap: 4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;gap:var(--content-gap)}.acfarticles-field-wrapper.layout-grid .acfarticle-item--content--excerpt{line-height:1.5} + + diff --git a/media/plg_fields_acfarticles/img/alist.svg b/media/plg_fields_acfarticles/img/alist.svg new file mode 100644 index 00000000..15525b7d --- /dev/null +++ b/media/plg_fields_acfarticles/img/alist.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfarticles/img/astylea.svg b/media/plg_fields_acfarticles/img/astylea.svg new file mode 100644 index 00000000..78372084 --- /dev/null +++ b/media/plg_fields_acfarticles/img/astylea.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfarticles/img/astyleb.svg b/media/plg_fields_acfarticles/img/astyleb.svg new file mode 100644 index 00000000..ee3133d3 --- /dev/null +++ b/media/plg_fields_acfarticles/img/astyleb.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfarticles/img/custom.svg b/media/plg_fields_acfarticles/img/custom.svg new file mode 100644 index 00000000..5ba91d53 --- /dev/null +++ b/media/plg_fields_acfarticles/img/custom.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/media/plg_fields_acfcountdown/img/1.svg b/media/plg_fields_acfcountdown/img/1.svg new file mode 100644 index 00000000..ed37cf8f --- /dev/null +++ b/media/plg_fields_acfcountdown/img/1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/2.svg b/media/plg_fields_acfcountdown/img/2.svg new file mode 100644 index 00000000..53a2f664 --- /dev/null +++ b/media/plg_fields_acfcountdown/img/2.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/3.svg b/media/plg_fields_acfcountdown/img/3.svg new file mode 100644 index 00000000..29f6ecb1 --- /dev/null +++ b/media/plg_fields_acfcountdown/img/3.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/4.svg b/media/plg_fields_acfcountdown/img/4.svg new file mode 100644 index 00000000..c42f60a7 --- /dev/null +++ b/media/plg_fields_acfcountdown/img/4.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/5.svg b/media/plg_fields_acfcountdown/img/5.svg new file mode 100644 index 00000000..8aed9eda --- /dev/null +++ b/media/plg_fields_acfcountdown/img/5.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/6.svg b/media/plg_fields_acfcountdown/img/6.svg new file mode 100644 index 00000000..d6f07305 --- /dev/null +++ b/media/plg_fields_acfcountdown/img/6.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/7.svg b/media/plg_fields_acfcountdown/img/7.svg new file mode 100644 index 00000000..237d73bf --- /dev/null +++ b/media/plg_fields_acfcountdown/img/7.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfcountdown/img/8.svg b/media/plg_fields_acfcountdown/img/8.svg new file mode 100644 index 00000000..250c4d5e --- /dev/null +++ b/media/plg_fields_acfcountdown/img/8.svg @@ -0,0 +1,3 @@ + + + diff --git a/media/plg_fields_acfcountdown/js/countdown.js b/media/plg_fields_acfcountdown/js/countdown.js new file mode 100644 index 00000000..04325875 --- /dev/null +++ b/media/plg_fields_acfcountdown/js/countdown.js @@ -0,0 +1,2 @@ +var ACF_Countdown_Editor=function(){function e(){window.ACFCountdownPresetsData&&this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("change",function(e){this.onClick(e)}.bind(this))},t.onClick=function(e){var t=e.target.closest(".acf-countdown-preset-selector");if(t){var i=t.querySelector('input[name="jform[fieldparams][preset]"]:checked');i&&(this.reset(),this.apply(i.value))}},t.reset=function(){var e={"[border][style]":{type:"list",value:"none"},"[border][width]":{type:"number",value:""},"[border][color]":{type:"text",value:""},separator:{type:"nrtoggle",value:!1},double_zeroes_format:{type:"nrtoggle",value:!0},days:{type:"nrtoggle",value:!0},days_label:{type:"text",value:Joomla.Text._("NR_DAYS")},hours:{type:"nrtoggle",value:!0},hours_label:{type:"text",value:Joomla.Text._("NR_HOURS")},minutes:{type:"nrtoggle",value:!0},minutes_label:{type:"text",value:Joomla.Text._("NR_MINUTES")},seconds:{type:"nrtoggle",value:!0},seconds_label:{type:"text",value:Joomla.Text._("NR_SECONDS")},item_size:{responsive:!0,type:"number",value:""},gap:{responsive:!0,type:"number",value:{desktop:10,tablet:"",mobile:""}},digits_gap:{responsive:!0,type:"number",value:{desktop:0,tablet:"",mobile:""}},digits_wrapper_custom_width:{type:"nrtoggle",value:!1},digits_wrapper_min_width:{type:"number",value:""},digits_custom_width:{type:"nrtoggle",value:!1},digits_min_width:{type:"number",value:""},unit_label_margin_top:{type:"number",value:5},style:{type:"list",value:"none"},item_background_color:{type:"text",value:""},digits_wrapper_background_color:{type:"text",value:""},digit_background_color:{type:"text",value:""},digits_wrapper_padding:{responsive:!0,dimensions:!0,type:"number",value:""},digits_padding:{responsive:!0,dimensions:!0,type:"number",value:""},digits_wrapper_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},digits_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},item_padding:{responsive:!0,dimensions:!0,type:"number",value:""},item_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},digit_text_color:{type:"text",value:""},digits_font_weight:{type:"list",value:400},unit_label_text_color:{type:"text",value:""},label_font_weight:{type:"list",value:400}};new TF_Fields_Values_Applier(e).applyData()},t.apply=function(e){new TF_Fields_Values_Applier(window.ACFCountdownPresetsData,e).applyItemData()},e}();document.addEventListener("DOMContentLoaded",function(e){new ACF_Countdown_Editor}); + diff --git a/media/plg_fields_acfcountdown/js/previewer.js b/media/plg_fields_acfcountdown/js/previewer.js new file mode 100644 index 00000000..8f9fc81b --- /dev/null +++ b/media/plg_fields_acfcountdown/js/previewer.js @@ -0,0 +1,2 @@ +function _inheritsLoose(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}var ACF_Countdown_Previewer=function(e){function t(){return e.call(this,"Countdown")||this}return _inheritsLoose(t,e),t}(ACF_Field_Previewer);document.addEventListener("DOMContentLoaded",function(){new ACF_Countdown_Previewer}); + diff --git a/media/plg_fields_acffaq/img/icons/arrow.svg b/media/plg_fields_acffaq/img/icons/arrow.svg new file mode 100644 index 00000000..9efaf310 --- /dev/null +++ b/media/plg_fields_acffaq/img/icons/arrow.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/media/plg_fields_acffaq/img/icons/circle_arrow.svg b/media/plg_fields_acffaq/img/icons/circle_arrow.svg new file mode 100644 index 00000000..7d1fb7cd --- /dev/null +++ b/media/plg_fields_acffaq/img/icons/circle_arrow.svg @@ -0,0 +1,4 @@ + + + + diff --git a/media/plg_fields_acffaq/img/icons/circle_plus_minus.svg b/media/plg_fields_acffaq/img/icons/circle_plus_minus.svg new file mode 100644 index 00000000..75ed1d59 --- /dev/null +++ b/media/plg_fields_acffaq/img/icons/circle_plus_minus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/media/plg_fields_acffaq/img/icons/plus_minus.svg b/media/plg_fields_acffaq/img/icons/plus_minus.svg new file mode 100644 index 00000000..36855bde --- /dev/null +++ b/media/plg_fields_acffaq/img/icons/plus_minus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/media/plg_fields_acffaq/img/index.php b/media/plg_fields_acffaq/img/index.php new file mode 100644 index 00000000..e69de29b diff --git a/media/plg_fields_acffaq/img/templates/1.png b/media/plg_fields_acffaq/img/templates/1.png new file mode 100644 index 00000000..1ea715b0 Binary files /dev/null and b/media/plg_fields_acffaq/img/templates/1.png differ diff --git a/media/plg_fields_acffaq/img/templates/2.png b/media/plg_fields_acffaq/img/templates/2.png new file mode 100644 index 00000000..0b2ec581 Binary files /dev/null and b/media/plg_fields_acffaq/img/templates/2.png differ diff --git a/media/plg_fields_acffaq/img/templates/3.png b/media/plg_fields_acffaq/img/templates/3.png new file mode 100644 index 00000000..1d6370c5 Binary files /dev/null and b/media/plg_fields_acffaq/img/templates/3.png differ diff --git a/media/plg_fields_acffaq/img/templates/4.png b/media/plg_fields_acffaq/img/templates/4.png new file mode 100644 index 00000000..1fca5e40 Binary files /dev/null and b/media/plg_fields_acffaq/img/templates/4.png differ diff --git a/media/plg_fields_acffaq/js/countdown.js b/media/plg_fields_acffaq/js/countdown.js new file mode 100644 index 00000000..04325875 --- /dev/null +++ b/media/plg_fields_acffaq/js/countdown.js @@ -0,0 +1,2 @@ +var ACF_Countdown_Editor=function(){function e(){window.ACFCountdownPresetsData&&this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("change",function(e){this.onClick(e)}.bind(this))},t.onClick=function(e){var t=e.target.closest(".acf-countdown-preset-selector");if(t){var i=t.querySelector('input[name="jform[fieldparams][preset]"]:checked');i&&(this.reset(),this.apply(i.value))}},t.reset=function(){var e={"[border][style]":{type:"list",value:"none"},"[border][width]":{type:"number",value:""},"[border][color]":{type:"text",value:""},separator:{type:"nrtoggle",value:!1},double_zeroes_format:{type:"nrtoggle",value:!0},days:{type:"nrtoggle",value:!0},days_label:{type:"text",value:Joomla.Text._("NR_DAYS")},hours:{type:"nrtoggle",value:!0},hours_label:{type:"text",value:Joomla.Text._("NR_HOURS")},minutes:{type:"nrtoggle",value:!0},minutes_label:{type:"text",value:Joomla.Text._("NR_MINUTES")},seconds:{type:"nrtoggle",value:!0},seconds_label:{type:"text",value:Joomla.Text._("NR_SECONDS")},item_size:{responsive:!0,type:"number",value:""},gap:{responsive:!0,type:"number",value:{desktop:10,tablet:"",mobile:""}},digits_gap:{responsive:!0,type:"number",value:{desktop:0,tablet:"",mobile:""}},digits_wrapper_custom_width:{type:"nrtoggle",value:!1},digits_wrapper_min_width:{type:"number",value:""},digits_custom_width:{type:"nrtoggle",value:!1},digits_min_width:{type:"number",value:""},unit_label_margin_top:{type:"number",value:5},style:{type:"list",value:"none"},item_background_color:{type:"text",value:""},digits_wrapper_background_color:{type:"text",value:""},digit_background_color:{type:"text",value:""},digits_wrapper_padding:{responsive:!0,dimensions:!0,type:"number",value:""},digits_padding:{responsive:!0,dimensions:!0,type:"number",value:""},digits_wrapper_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},digits_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},item_padding:{responsive:!0,dimensions:!0,type:"number",value:""},item_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},digit_text_color:{type:"text",value:""},digits_font_weight:{type:"list",value:400},unit_label_text_color:{type:"text",value:""},label_font_weight:{type:"list",value:400}};new TF_Fields_Values_Applier(e).applyData()},t.apply=function(e){new TF_Fields_Values_Applier(window.ACFCountdownPresetsData,e).applyItemData()},e}();document.addEventListener("DOMContentLoaded",function(e){new ACF_Countdown_Editor}); + diff --git a/media/plg_fields_acffaq/js/faq.js b/media/plg_fields_acffaq/js/faq.js new file mode 100644 index 00000000..52e80d62 --- /dev/null +++ b/media/plg_fields_acffaq/js/faq.js @@ -0,0 +1,2 @@ +var ACF_FAQ_Editor=function(){function e(){window.ACFFAQPresetsData&&this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("change",function(e){this.onClick(e)}.bind(this))},t.onClick=function(e){var t=e.target.closest(".acf-faq-template-selector");if(t){var n=t.querySelector('input[name="jform[fieldparams][template]"]:checked');n&&(this.reset(),this.apply(n.value))}},t.reset=function(){new TF_Fields_Values_Applier({separator:{type:"nrtoggle",value:!0},keep_one_question_open:{type:"nrtoggle",value:!0},columns:{type:"number",value:""},item_gap:{responsive:!0,type:"number",value:{desktop:20,tablet:"",mobile:""}},column_gap:{responsive:!0,type:"number",value:{desktop:20,tablet:"",mobile:""}},background_color:{type:"text",value:""},item_padding:{responsive:!0,dimensions:!0,type:"number",value:""},item_border_radius:{responsive:!0,dimensions:!0,border_radius:!0,type:"number",value:""},question_text_color:{type:"text",value:""},question_font_size:{responsive:!0,type:"number",value:{desktop:16,tablet:"",mobile:""}},answer_text_color:{type:"text",value:""},answer_font_size:{responsive:!0,type:"number",value:{desktop:16,tablet:"",mobile:""}},icon_position:{type:"radio",value:"right"},icon:{type:"radio",value:"arrow"},initial_state:{type:"list",value:"first-open"}}).applyData()},t.apply=function(e){new TF_Fields_Values_Applier(window.ACFFAQPresetsData,e).applyItemData()},e}();document.addEventListener("DOMContentLoaded",function(e){new ACF_FAQ_Editor}); + diff --git a/media/plg_fields_acffaq/js/previewer.js b/media/plg_fields_acffaq/js/previewer.js new file mode 100644 index 00000000..1dadd549 --- /dev/null +++ b/media/plg_fields_acffaq/js/previewer.js @@ -0,0 +1,2 @@ +function _inheritsLoose(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}var ACF_FAQ_Previewer=function(e){function t(){return e.call(this,"FAQ")||this}return _inheritsLoose(t,e),t}(ACF_Field_Previewer);document.addEventListener("DOMContentLoaded",function(){new ACF_FAQ_Previewer}); + diff --git a/media/plg_fields_acfgallery/img/grid.svg b/media/plg_fields_acfgallery/img/grid.svg new file mode 100644 index 00000000..eb41136c --- /dev/null +++ b/media/plg_fields_acfgallery/img/grid.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/media/plg_fields_acfgallery/img/masonry.svg b/media/plg_fields_acfgallery/img/masonry.svg new file mode 100644 index 00000000..6025eed0 --- /dev/null +++ b/media/plg_fields_acfgallery/img/masonry.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/media/plg_fields_acfgallery/img/slideshow.svg b/media/plg_fields_acfgallery/img/slideshow.svg new file mode 100644 index 00000000..7dbd0700 --- /dev/null +++ b/media/plg_fields_acfgallery/img/slideshow.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfgallery/img/zjustified.svg b/media/plg_fields_acfgallery/img/zjustified.svg new file mode 100644 index 00000000..26aea2b3 --- /dev/null +++ b/media/plg_fields_acfgallery/img/zjustified.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/media/plg_fields_acfgallery/js/acfgallery.js b/media/plg_fields_acfgallery/js/acfgallery.js new file mode 100644 index 00000000..d3fd5d2a --- /dev/null +++ b/media/plg_fields_acfgallery/js/acfgallery.js @@ -0,0 +1,2 @@ +var ACF_Gallery_Init=function(){function t(){"undefined"!=typeof Dropzone&&this.init()}var e=t.prototype;return e.init=function(){var e=this;document.querySelectorAll("joomla-field-subform").forEach(function(t){t.addEventListener("subform-row-add",function(t){e.setup(e.getElements(t.detail.row),t.detail.row),e.initInstances(e.getElements(t.detail.row))})})},e.setup=function(t,e){var r=this;t.forEach(function(t){r.initMediaLibraryModal(t),r.updateAttributes(t,e)})},e.initInstances=function(t){t.forEach(function(t){new TF_Gallery_Manager(t).init()})},e.updateAttributes=function(t,e){var r=e.dataset.group.replace("row","");t.setAttribute("class",t.getAttribute("class").replace("__rowX__","__row"+r+"__"));var o=t.querySelector(".tf-gallery-browse-item-button");o.setAttribute("data-bs-target",o.getAttribute("data-bs-target").replace("__rowX__","__row"+r+"__"));var a=t.querySelector(".media_uploader_file");a.setAttribute("id",a.id.replace("__rowX__","__row"+r+"__"));var n=t.querySelector(".tf-gallery-dz");n.setAttribute("data-inputname",n.getAttribute("data-inputname").replace("[rowX]","[row"+r+"]"));var i=t.querySelector(".modal");i.setAttribute("id",i.id.replace("__rowX__","__row"+r+"__"));var u=t.querySelector("template.previewTemplate"),l=u.content.querySelector('input[type="checkbox"]');l.setAttribute("id",l.id.replace("[rowX]","[row"+r+"]"));var c=u.content.querySelector("label");c.setAttribute("for",c.getAttribute("for").replace("[rowX]","[row"+r+"]"));var d=u.content.querySelector("textarea");d.setAttribute("name",d.getAttribute("name").replace("[rowX]","[row"+r+"]"));var s=u.content.querySelector("input.item-source");s&&s.setAttribute("name",s.getAttribute("name").replace("[rowX]","[row"+r+"]"));var b=u.content.querySelector("input.item-thumbnail");b.setAttribute("name",b.getAttribute("name").replace("[rowX]","[row"+r+"]"));var p=u.content.querySelector("input.item-caption");p.setAttribute("name",p.getAttribute("name").replace("[rowX]","[row"+r+"]"));var _=u.content.querySelector("input.item-tags");_.setAttribute("name",_.getAttribute("name").replace("[rowX]","[row"+r+"]"))},e.initMediaLibraryModal=function(t){var e=t.querySelector(":scope > .modal"),r=Joomla.getOptions("bootstrap.modal")["#"+e.id],o={backdrop:!r.backdrop||r.backdrop,keyboard:!r.keyboard||r.keyboard,focus:!r.focus||r.focus};Joomla.initialiseModal(e,o)},e.getElements=function(t){return void 0===t&&(t=""),(t=""==t?document:t).querySelectorAll(".tf-gallery-manager")},t}();document.addEventListener("DOMContentLoaded",function(t){new ACF_Gallery_Init}); + diff --git a/media/plg_fields_acfmap/img/marker.png b/media/plg_fields_acfmap/img/marker.png new file mode 100644 index 00000000..e134b6c5 Binary files /dev/null and b/media/plg_fields_acfmap/img/marker.png differ diff --git a/media/plg_fields_acfpaypal/css/acf-paypal-admin.css b/media/plg_fields_acfpaypal/css/acf-paypal-admin.css new file mode 100644 index 00000000..fd8cc125 --- /dev/null +++ b/media/plg_fields_acfpaypal/css/acf-paypal-admin.css @@ -0,0 +1,3 @@ +.acf-paypal-styles-wrapper .controls{margin-left:0 !important} + + diff --git a/media/plg_fields_acfpaypal/img/btn_checkout_blue.png b/media/plg_fields_acfpaypal/img/btn_checkout_blue.png new file mode 100644 index 00000000..2760e72e Binary files /dev/null and b/media/plg_fields_acfpaypal/img/btn_checkout_blue.png differ diff --git a/media/plg_fields_acfpaypal/img/btn_checkout_grey.png b/media/plg_fields_acfpaypal/img/btn_checkout_grey.png new file mode 100644 index 00000000..562a33c1 Binary files /dev/null and b/media/plg_fields_acfpaypal/img/btn_checkout_grey.png differ diff --git a/media/plg_fields_acfpaypal/img/btn_checkout_yellow.png b/media/plg_fields_acfpaypal/img/btn_checkout_yellow.png new file mode 100644 index 00000000..dca56c13 Binary files /dev/null and b/media/plg_fields_acfpaypal/img/btn_checkout_yellow.png differ diff --git a/media/plg_fields_acfpaypal/img/btn_paypal_blue.png b/media/plg_fields_acfpaypal/img/btn_paypal_blue.png new file mode 100644 index 00000000..ceec5b29 Binary files /dev/null and b/media/plg_fields_acfpaypal/img/btn_paypal_blue.png differ diff --git a/media/plg_fields_acfpaypal/img/btn_paypal_grey.png b/media/plg_fields_acfpaypal/img/btn_paypal_grey.png new file mode 100644 index 00000000..14613cc2 Binary files /dev/null and b/media/plg_fields_acfpaypal/img/btn_paypal_grey.png differ diff --git a/media/plg_fields_acfpaypal/img/btn_paypal_yellow.png b/media/plg_fields_acfpaypal/img/btn_paypal_yellow.png new file mode 100644 index 00000000..3d0c3aa1 Binary files /dev/null and b/media/plg_fields_acfpaypal/img/btn_paypal_yellow.png differ diff --git a/media/plg_fields_acfpaypal/img/logo.png b/media/plg_fields_acfpaypal/img/logo.png new file mode 100644 index 00000000..aab877ab Binary files /dev/null and b/media/plg_fields_acfpaypal/img/logo.png differ diff --git a/media/plg_fields_acfpaypal/img/logo_cc.png b/media/plg_fields_acfpaypal/img/logo_cc.png new file mode 100644 index 00000000..15cbfc1b Binary files /dev/null and b/media/plg_fields_acfpaypal/img/logo_cc.png differ diff --git a/media/plg_fields_acfpaypal/img/paypal_b.png b/media/plg_fields_acfpaypal/img/paypal_b.png new file mode 100644 index 00000000..132989bc Binary files /dev/null and b/media/plg_fields_acfpaypal/img/paypal_b.png differ diff --git a/media/plg_fields_acfprogressbar/css/style.css b/media/plg_fields_acfprogressbar/css/style.css new file mode 100644 index 00000000..a4558fa0 --- /dev/null +++ b/media/plg_fields_acfprogressbar/css/style.css @@ -0,0 +1,3 @@ +.acf_progressbar_wrapper{-webkit-box-sizing:border-box;box-sizing:border-box}.acf_progressbar_wrapper,.acf_progressbar_wrapper *{-webkit-box-sizing:inherit;box-sizing:inherit}.acf_progressbar_wrapper .acf_progressbar{position:relative;background-color:#efeeee}.acf_progressbar_wrapper .acf_progressbar.acf_progressbar_shadow{-webkit-box-shadow:inset 0px 1px 3px rgba(0,0,0,0.15);box-shadow:inset 0px 1px 3px rgba(0,0,0,0.15)}.acf_progressbar_wrapper .acf_progressbar.acf_progressbar_rounded{border-radius:8px}.acf_progressbar_wrapper .acf_progressbar .acf_bar{height:100%}.acf_progressbar_wrapper .acf_progressbar .acf_bar.acf_progressbar_stripes,.acf_progressbar_wrapper .acf_progressbar .acf_bar.acf_progressbar_animated{background:linear-gradient(-45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:35px 35px}.acf_progressbar_wrapper .acf_progressbar .acf_bar.acf_progressbar_rounded{border-radius:8px}.acf_progressbar_wrapper .acf_progressbar .acf_bar.acf_progressbar_animated{-webkit-animation:progressBarAnimated 2s linear infinite;animation:progressBarAnimated 2s linear infinite}@-webkit-keyframes progressBarAnimated{0%{background-position:0 0}100%{background-position:35px 35px}}@keyframes progressBarAnimated{0%{background-position:0 0}100%{background-position:35px 35px}}.acf_progressbar_wrapper .acf_progressbar_label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#fff;font-size:13px;font-family:Arial;padding:0 10px 0 10px;height:100%;white-space:nowrap}.acf_progressbar_wrapper .acf_progressbar_label.acf_left{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.acf_progressbar_wrapper .acf_progressbar_label.acf_right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end} + + diff --git a/media/plg_fields_acftelephone/js/script.js b/media/plg_fields_acftelephone/js/script.js new file mode 100644 index 00000000..dae0c3f7 --- /dev/null +++ b/media/plg_fields_acftelephone/js/script.js @@ -0,0 +1,2 @@ +function initACFTelephoneInputMask(){if("undefined"!=typeof Inputmask)for(var e=document.querySelectorAll(".acf-input-mask"),t=0;tdiv:nth-child(2){margin-top:15px}.acfupload .dz-message{color:#999;border:2px dashed currentColor;padding:25px 10px;text-align:center;border-radius:3px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-transition:all 80ms ease;transition:all 80ms ease}.acfupload .dz-message>*{margin:5px}.acfupload .dz-message:hover>*{opacity:1}.acfupload .dz-message .acfupload-browse{color:inherit;width:auto;border-radius:5px;padding:4px 7px;background:transparent;border:solid 1px currentColor;color:inherit}.acfupload.dz-clickable .dz-message{cursor:pointer}.acfupload.dz-drag-hover .dz-message{background-color:#c7ecc7 !important;color:#5d5c5c}.acfupload.dz-drag-hover .dz-message>*{opacity:1}.cfup-status{width:8px;height:8px;border-radius:100%;background-color:#999;text-indent:-100000000px;-ms-flex-negative:0;flex-shrink:0}.dz-processing .cfup-status{background-color:#4b7ac3}.dz-success .cfup-status{background-color:#4bc380}.dz-error .cfup-status{background-color:#f44660}.acfupload-items{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.cfup-file{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;gap:6px;line-height:1;position:relative;height:38px;-webkit-transition:all 80ms ease;transition:all 80ms ease}.cfup-file:before,.cfup-file:after{opacity:0;content:" ";width:10px;height:100%;background:#f7f7f7;position:absolute;left:-10px;top:0;border-radius:5px 0 0 5px;-webkit-transition:all 80ms ease;transition:all 80ms ease}.cfup-file:after{left:auto;right:-10px;border-radius:0 5px 5px 0}.cfup-file .cfup-controls{position:absolute;right:-8px;display:none;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.cfup-file .cfup-controls svg{fill:currentColor}.cfup-file .cfup-controls a{margin:0;display:-webkit-box;display:-ms-flexbox;display:flex;padding:0 4px;height:100%;text-decoration:none !important;color:inherit}.cfup-file .cfup-controls a:hover{opacity:1}.cfup-file .cfup-controls a:hover svg{fill:#2a69b8}.cfup-file .cfup-controls a.acf_upload_delete:hover svg{fill:#f44660}.cfup-file .cfup-controls a[target="_blank"]::before{content:none}.cfup-file:hover{background:#f7f7f7}.cfup-file:hover:before,.cfup-file:hover:after{opacity:1}.cfup-file:hover .cfup-size{display:none !important}.cfup-file:hover .cfup-controls{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.cfup-file .cfup-right{white-space:nowrap;height:100%}.cfup-file .cfup-right .cfup-size{opacity:.7;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:100%;gap:2px}.cfup-file .cfup-right .cfup-size strong{font-weight:normal}.cfup-file .cfup-details{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;gap:4px;overflow:hidden}.cfup-file .cfup-details .cfup-name{white-space:nowrap;overflow:hidden;width:100%;text-overflow:ellipsis}.cfup-file .cfup-progress{display:none;width:100%;height:2px;background-color:#ccc;position:absolute;width:100%;left:0;bottom:0}.cfup-file .cfup-progress .dz-upload{background-color:#4b7ac3;height:100%;width:0;display:block;border-radius:5px 0 0 5px;-webkit-transition:all 500ms linear;transition:all 500ms linear}.cfup-file.dz-processing .cfup-progress{display:block}.cfup-file .cfup-error{display:none;color:#f44660;font-size:.8em}.cfup-file.dz-error .cfup-controls .acfupload-edit-item,.cfup-file.dz-error .cfup-controls .upload-link{display:none}.cfup-file.dz-error .cfup-error{display:block}.cfup-file.dz-complete .cfup-progress{display:none} + + diff --git a/media/plg_fields_acfupload/js/acfupload.js b/media/plg_fields_acfupload/js/acfupload.js new file mode 100644 index 00000000..dcc83543 --- /dev/null +++ b/media/plg_fields_acfupload/js/acfupload.js @@ -0,0 +1,2 @@ +window.NodeList&&!NodeList.prototype.forEach&&(NodeList.prototype.forEach=Array.prototype.forEach),Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),Element.prototype.closest||(Element.prototype.closest=function(e){var t=this;do{if(t.matches(e))return t;t=t.parentElement||t.parentNode}while(null!==t&&1===t.nodeType);return null});var ACF_Upload_Instance=function(){function e(e){this.elem=e,this.item_id=1,this.token=Joomla.getOptions("csrf.token"),this.upload_url="index.php?option=com_ajax&format=raw&plugin=ACFUpload&group=fields",this.init()}var t=e.prototype;return t.init=function(){var e=this.elem.previousElementSibling,t=e.innerHTML;if("1"!=this.elem.getAttribute("data-disabled")){e.parentElement.removeChild(e);var i=parseFloat(this.elem.getAttribute("data-maxfilesize"));i=i||null;var o=parseInt(this.elem.getAttribute("data-maxfiles")),n=1!=(o=o||null),r=!(!this.elem.previousElementSibling||"INPUT"!=this.elem.previousElementSibling.nodeName),a=this;new Dropzone(this.elem,{url:this.elem.dataset.baseurl+this.upload_url,previewTemplate:t,maxFilesize:i,uploadMultiple:n,maxFiles:o,acceptedFiles:this.elem.getAttribute("data-acceptedfiles"),autoProcessQueue:!0,parallelUploads:1,filesizeBase:1e3,createImageThumbnails:!1,previewsContainer:this.elem.querySelector(".acfupload-items"),timeout:0,dictFallbackMessage:Joomla.JText._("ACF_UPLOAD_FALLBACK_MESSAGE"),dictFileTooBig:Joomla.JText._("ACF_UPLOAD_FILETOOBIG"),dictInvalidFileType:Joomla.JText._("ACF_UPLOAD_INVALID_FILE"),dictResponseError:Joomla.JText._("ACF_UPLOAD_RESPONSE_ERROR"),dictCancelUpload:Joomla.JText._("ACF_UPLOAD_CANCEL_UPLOAD"),dictCancelUploadConfirmation:Joomla.JText._("ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION"),dictRemoveFile:Joomla.JText._("ACF_UPLOAD_REMOVE_FILE"),dictMaxFilesExceeded:Joomla.JText._("ACF_UPLOAD_MAX_FILES_EXCEEDED"),dictRemoveFileConfirmation:Joomla.JText._("ACF_UPLOAD_REMOVE_FILE_CONFIRM"),init:function(){var e=this;"undefined"!=typeof Sortable&&n&&new Sortable(a.elem.querySelector(".acfupload-items"),{animation:150,handle:".cfup-file",onStart:function(){e.element.setAttribute("data-sorting",!0)},onEnd:function(){e.element.setAttribute("data-sorting",!1)}}),this.on("addedfile",function(t){null==t.upload&&(a.createHiddenInput(t.previewTemplate,t.path,t.title,t.description),t.error?(t.previewTemplate.classList.add("dz-error"),el_error=t.previewTemplate.querySelector(".cfup-error"),el_error.innerHTML=t.error,el_error.style.display="block"):(t.previewTemplate.querySelectorAll(".upload-link").forEach(function(e){return e.setAttribute("href",t.url)}),t.previewTemplate.dataset.file=t.encoded));var e=t.name.split(".");e&&t.previewTemplate.classList.add("cfup-"+e.pop())});var t=a.elem.dataset.value;if(t){t=JSON.parse(t);for(var i=0;i'),this.element.appendChild(e));var l=e.getElementsByTagName("span")[0];return l&&(null!=l.textContent?l.textContent=this.options.dictFallbackMessage:null!=l.innerText&&(l.innerText=this.options.dictFallbackMessage)),this.element.appendChild(this.getFallbackForm())},resize:function(e,t,n,i){var r={srcX:0,srcY:0,srcWidth:e.width,srcHeight:e.height},o=e.width/e.height;null==t&&null==n?(t=r.srcWidth,n=r.srcHeight):null==t?t=n*o:null==n&&(n=t/o);var a=(t=Math.min(t,r.srcWidth))/(n=Math.min(n,r.srcHeight));if(r.srcWidth>t||r.srcHeight>n)if("crop"===i)a\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n
\n',drop:function(){return this.element.classList.remove("dz-drag-hover")},dragstart:function(){},dragend:function(){return this.element.classList.remove("dz-drag-hover")},dragenter:function(){return this.element.classList.add("dz-drag-hover")},dragover:function(){return this.element.classList.add("dz-drag-hover")},dragleave:function(){return this.element.classList.remove("dz-drag-hover")},paste:function(){},reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(t){var n=this;if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer){t.previewElement=C.createElement(this.options.previewTemplate.trim()),t.previewTemplate=t.previewElement,this.previewsContainer.appendChild(t.previewElement);var e=!0,i=!1,r=void 0;try{for(var o,a=t.previewElement.querySelectorAll("[data-dz-name]")[Symbol.iterator]();!(e=(o=a.next()).done);e=!0){var l=o.value;l.textContent=t.name}}catch(e){i=!0,r=e}finally{try{e||null==a.return||a.return()}finally{if(i)throw r}}var s=!0,u=!1,c=void 0;try{for(var d,p=t.previewElement.querySelectorAll("[data-dz-size]")[Symbol.iterator]();!(s=(d=p.next()).done);s=!0)(l=d.value).innerHTML=this.filesize(t.size)}catch(e){u=!0,c=e}finally{try{s||null==p.return||p.return()}finally{if(u)throw c}}this.options.addRemoveLinks&&(t._removeLink=C.createElement(''.concat(this.options.dictRemoveFile,"")),t.previewElement.appendChild(t._removeLink));var h=function(e){return e.preventDefault(),e.stopPropagation(),t.status===C.UPLOADING?C.confirm(n.options.dictCancelUploadConfirmation,function(){return n.removeFile(t)}):n.options.dictRemoveFileConfirmation?C.confirm(n.options.dictRemoveFileConfirmation,function(){return n.removeFile(t)}):n.removeFile(t)},f=!0,v=!1,m=void 0;try{for(var y,g=t.previewElement.querySelectorAll("[data-dz-remove]")[Symbol.iterator]();!(f=(y=g.next()).done);f=!0){y.value.addEventListener("click",h)}}catch(e){v=!0,m=e}finally{try{f||null==g.return||g.return()}finally{if(v)throw m}}}},removedfile:function(e){return null!=e.previewElement&&null!=e.previewElement.parentNode&&e.previewElement.parentNode.removeChild(e.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(e,t){if(e.previewElement){e.previewElement.classList.remove("dz-file-preview");var n=!0,i=!1,r=void 0;try{for(var o,a=e.previewElement.querySelectorAll("[data-dz-thumbnail]")[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var l=o.value;l.alt=e.name,l.src=t}}catch(e){i=!0,r=e}finally{try{n||null==a.return||a.return()}finally{if(i)throw r}}return setTimeout(function(){return e.previewElement.classList.add("dz-image-preview")},1)}},error:function(e,t){if(e.previewElement){e.previewElement.classList.add("dz-error"),"String"!=typeof t&&t.error&&(t=t.error);var n=!0,i=!1,r=void 0;try{for(var o,a=e.previewElement.querySelectorAll("[data-dz-errormessage]")[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){o.value.textContent=t}}catch(e){i=!0,r=e}finally{try{n||null==a.return||a.return()}finally{if(i)throw r}}}},errormultiple:function(){},processing:function(e){if(e.previewElement&&(e.previewElement.classList.add("dz-processing"),e._removeLink))return e._removeLink.innerHTML=this.options.dictCancelUpload},processingmultiple:function(){},uploadprogress:function(e,t){if(e.previewElement){var n=!0,i=!1,r=void 0;try{for(var o,a=e.previewElement.querySelectorAll("[data-dz-uploadprogress]")[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var l=o.value;"PROGRESS"===l.nodeName?l.value=t:l.style.width="".concat(t,"%")}}catch(e){i=!0,r=e}finally{try{n||null==a.return||a.return()}finally{if(i)throw r}}}},totaluploadprogress:function(){},sending:function(){},sendingmultiple:function(){},success:function(e){if(e.previewElement)return e.previewElement.classList.add("dz-success")},successmultiple:function(){},canceled:function(e){return this.emit("error",e,this.options.dictUploadCanceled)},canceledmultiple:function(){},complete:function(e){if(e._removeLink&&(e._removeLink.innerHTML=this.options.dictRemoveFile),e.previewElement)return e.previewElement.classList.add("dz-complete")},completemultiple:function(){},maxfilesexceeded:function(){},maxfilesreached:function(){},queuecomplete:function(){},addedfiles:function(){}},this.prototype._thumbnailQueue=[],this.prototype._processingThumbnail=!1}},{key:"extend",value:function(e){for(var t=arguments.length,n=new Array(1"))),this.clickableElements.length){!function l(){return s.hiddenFileInput&&s.hiddenFileInput.parentNode.removeChild(s.hiddenFileInput),s.hiddenFileInput=document.createElement("input"),s.hiddenFileInput.setAttribute("type","file"),(null===s.options.maxFiles||1")),n+='');var i=C.createElement(n);return"FORM"!==this.element.tagName?(t=C.createElement('
'))).appendChild(i):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=t?t:i}},{key:"getExistingFallback",value:function(){for(var e=function(e){var t=!0,n=!1,i=void 0;try{for(var r,o=e[Symbol.iterator]();!(t=(r=o.next()).done);t=!0){var a=r.value;if(/(^| )fallback($| )/.test(a.className))return a}}catch(e){n=!0,i=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw i}}},t=0,n=["div","form"];t".concat(t," ").concat(this.options.dictFileSizeUnits[n])}},{key:"_updateMaxFilesReachedClass",value:function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")}},{key:"drop",value:function(e){if(e.dataTransfer){this.emit("drop",e);for(var t=[],n=0;n1024*this.options.maxFilesize*1024?t(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(e.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):C.isValidFile(e,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(t(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",e)):this.options.accept.call(this,e,t):t(this.options.dictInvalidFileType)}},{key:"addFile",value:function(t){var n=this;t.upload={uuid:C.uuidv4(),progress:0,total:t.size,bytesSent:0,filename:this._renameFile(t)},this.files.push(t),t.status=C.ADDED,this.emit("addedfile",t),this._enqueueThumbnail(t),this.accept(t,function(e){e?(t.accepted=!1,n._errorProcessing([t],e)):(t.accepted=!0,n.options.autoQueue&&n.enqueueFile(t)),n._updateMaxFilesReachedClass()})}},{key:"enqueueFiles",value:function(e){var t=!0,n=!1,i=void 0;try{for(var r,o=e[Symbol.iterator]();!(t=(r=o.next()).done);t=!0){var a=r.value;this.enqueueFile(a)}}catch(e){n=!0,i=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw i}}return null}},{key:"enqueueFile",value:function(e){var t=this;if(e.status!==C.ADDED||!0!==e.accepted)throw new Error("This file can't be queued because it has already been processed or was rejected.");if(e.status=C.QUEUED,this.options.autoProcessQueue)return setTimeout(function(){return t.processQueue()},0)}},{key:"_enqueueThumbnail",value:function(e){var t=this;if(this.options.createImageThumbnails&&e.type.match(/image.*/)&&e.size<=1024*this.options.maxThumbnailFilesize*1024)return this._thumbnailQueue.push(e),setTimeout(function(){return t._processThumbnailQueue()},0)}},{key:"_processThumbnailQueue",value:function(){var t=this;if(!this._processingThumbnail&&0!==this._thumbnailQueue.length){this._processingThumbnail=!0;var n=this._thumbnailQueue.shift();return this.createThumbnail(n,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,!0,function(e){return t.emit("thumbnail",n,e),t._processingThumbnail=!1,t._processThumbnailQueue()})}}},{key:"removeFile",value:function(e){if(e.status===C.UPLOADING&&this.cancelUpload(e),this.files=without(this.files,e),this.emit("removedfile",e),0===this.files.length)return this.emit("reset")}},{key:"removeAllFiles",value:function(e){null==e&&(e=!1);var t=!0,n=!1,i=void 0;try{for(var r,o=this.files.slice()[Symbol.iterator]();!(t=(r=o.next()).done);t=!0){var a=r.value;a.status===C.UPLOADING&&!e||this.removeFile(a)}}catch(e){n=!0,i=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw i}}return null}},{key:"resizeImage",value:function(r,e,t,n,o){var a=this;return this.createThumbnail(r,e,t,n,!0,function(e,t){if(null==t)return o(r);var n=a.options.resizeMimeType;null==n&&(n=r.type);var i=t.toDataURL(n,a.options.resizeQuality);return"image/jpeg"!==n&&"image/jpg"!==n||(i=ExifRestore.restore(r.dataURL,i)),o(C.dataURItoBlob(i))})}},{key:"createThumbnail",value:function(e,t,n,i,r,o){var a=this,l=new FileReader;l.onload=function(){e.dataURL=l.result,"image/svg+xml"!==e.type?a.createThumbnailFromUrl(e,t,n,i,r,o):null!=o&&o(l.result)},l.readAsDataURL(e)}},{key:"displayExistingFile",value:function(t,e,n,i,r){var o=this,a=!(4u.options.chunkSize),s[0].upload.totalChunkCount=Math.ceil(t.size/u.options.chunkSize)}if(s[0].upload.chunked){var r=s[0],o=e[0];r.upload.chunks=[];var i=function(){for(var e=0;void 0!==r.upload.chunks[e];)e++;if(!(e>=r.upload.totalChunkCount)){0;var t=e*u.options.chunkSize,n=Math.min(t+u.options.chunkSize,r.size),i={name:u._getParamName(0),data:o.webkitSlice?o.webkitSlice(t,n):o.slice(t,n),filename:r.upload.filename,chunkIndex:e};r.upload.chunks[e]={file:r,index:e,dataBlock:i,status:C.UPLOADING,progress:0,retries:0},u._uploadData(s,[i])}};if(r.upload.finishedChunkUpload=function(e){var t=!0;e.status=C.SUCCESS,e.dataBlock=null,e.xhr=null;for(var n=0;n>1}var s=l/t;return 0==s?1:s},drawImageIOSFix=function(e,t,n,i,r,o,a,l,s,u){var c=detectVerticalSquash(t);return e.drawImage(t,n,i,r,o,a,l,s,u/c)},ExifRestore=function(){function e(){_classCallCheck(this,e)}return _createClass(e,null,[{key:"initClass",value:function(){this.KEY_STR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}},{key:"encode64",value:function(e){for(var t="",n=void 0,i=void 0,r="",o=void 0,a=void 0,l=void 0,s="",u=0;o=(n=e[u++])>>2,a=(3&n)<<4|(i=e[u++])>>4,l=(15&i)<<2|(r=e[u++])>>6,s=63&r,isNaN(i)?l=s=64:isNaN(r)&&(s=64),t=t+this.KEY_STR.charAt(o)+this.KEY_STR.charAt(a)+this.KEY_STR.charAt(l)+this.KEY_STR.charAt(s),n=i=r="",o=a=l=s="",ue.length)break}return n}},{key:"decode64",value:function(e){var t=void 0,n=void 0,i="",r=void 0,o=void 0,a="",l=0,s=[];for(/[^A-Za-z0-9\+\/\=]/g.exec(e)&&console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");t=this.KEY_STR.indexOf(e.charAt(l++))<<2|(r=this.KEY_STR.indexOf(e.charAt(l++)))>>4,n=(15&r)<<4|(o=this.KEY_STR.indexOf(e.charAt(l++)))>>2,i=(3&o)<<6|(a=this.KEY_STR.indexOf(e.charAt(l++))),s.push(t),64!==o&&s.push(n),64!==a&&s.push(i),t=n=i="",r=o=a="",l>8&255]}function o(e){return[255&e,e>>8&255,e>>16&255,e>>24&255]}function a(e){return e[3]<<24|e[2]<<16|e[1]<<8|e[0]}function u(e){return D(e,23,4)}function s(e){return D(e,52,8)}function l(e,t,n,r){var i=x(n),n=L(e);if(i+t>n.byteLength)throw j(_);return e=L(n.buffer).bytes,n=i+n.byteOffset,t=e.slice(n,n+t),r?t:t.reverse()}function c(e,t,n,r,i,o){if(n=x(n),e=L(e),n+t>e.byteLength)throw j(_);for(var a=L(e.buffer).bytes,u=n+e.byteOffset,s=r(+i),l=0;lW;)(B=q[W++])in z||d(z,B,M[B]);y.constructor=z}k&&E(n)!==C&&k(n,C);var C=new P(new z(2)),H=n.setInt8;C.setInt8(0,2147483648),C.setInt8(1,2147483649),!C.getInt8(0)&&C.getInt8(1)||v(n,{setInt8:function(e,t){H.call(this,e,t<<24>>24)},setUint8:function(e,t){H.call(this,e,t<<24>>24)}},{unsafe:!0})}else z=function(e){g(this,z,I);e=x(e);R(this,{bytes:F.call(new Array(e),0),byteLength:e}),p||(this.byteLength=e)},P=function(e,t,n){g(this,P,U),g(e,z,U);var r=L(e).byteLength,t=m(t);if(t<0||r>24},getUint8:function(e){return l(this,1,e)[0]},getInt16:function(e,t){t=l(this,2,e,1>16},getUint16:function(e,t){t=l(this,2,e,1>>0},getFloat32:function(e,t){return N(l(this,4,e,1")}),y="$0"==="a".replace(/./,"$0"),n=f("replace"),g=!!/./[n]&&""===/./[n]("a","$0"),m=!c(function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};e="ab".split(e);return 2!==e.length||"a"!==e[0]||"b"!==e[1]});e.exports=function(n,e,t,r){var o,i,a=f(n),u=!c(function(){var e={};return e[a]=function(){return 7},7!=""[n](e)}),s=u&&!c(function(){var e=!1,t=/a/;return"split"===n&&((t={constructor:{}}).constructor[d]=function(){return t},t.flags="",t[a]=/./[a]),t.exec=function(){return e=!0,null},t[a](""),!e});u&&s&&("replace"!==n||v&&y&&!g)&&("split"!==n||m)||(o=/./[a],t=(s=t(a,""[n],function(e,t,n,r,i){return t.exec===p?u&&!i?{done:!0,value:o.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}},{REPLACE_KEEPS_$0:y,REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE:g}))[0],i=s[1],l(String.prototype,n,t),l(RegExp.prototype,a,2==e?function(e,t){return i.call(e,this,t)}:function(e){return i.call(e,this)})),r&&h(RegExp.prototype[a],"sham",!0)}},9974:function(e,t,n){var o=n(3099);e.exports=function(r,i,e){if(o(r),void 0===i)return r;switch(e){case 0:return function(){return r.call(i)};case 1:return function(e){return r.call(i,e)};case 2:return function(e,t){return r.call(i,e,t)};case 3:return function(e,t,n){return r.call(i,e,t,n)}}return function(){return r.apply(i,arguments)}}},5005:function(e,t,n){function r(e){return"function"==typeof e?e:void 0}var i=n(857),o=n(7854);e.exports=function(e,t){return arguments.length<2?r(i[e])||r(o[e]):i[e]&&i[e][t]||o[e]&&o[e][t]}},1246:function(e,t,n){var r=n(648),i=n(7497),o=n(5112)("iterator");e.exports=function(e){if(null!=e)return e[o]||e["@@iterator"]||i[r(e)]}},8554:function(e,t,n){var r=n(9670),i=n(1246);e.exports=function(e){var t=i(e);if("function"!=typeof t)throw TypeError(String(e)+" is not iterable");return r(t.call(e))}},647:function(e,t,n){var r=n(7908),p=Math.floor,i="".replace,h=/\$([$&'`]|\d\d?|<[^>]*>)/g,d=/\$([$&'`]|\d\d?)/g;e.exports=function(o,a,u,s,l,e){var c=u+o.length,f=s.length,t=d;return void 0!==l&&(l=r(l),t=h),i.call(e,t,function(e,t){var n;switch(t.charAt(0)){case"$":return"$";case"&":return o;case"`":return a.slice(0,u);case"'":return a.slice(c);case"<":n=l[t.slice(1,-1)];break;default:var r=+t;if(0==r)return e;if(f>1,l=23===t?h(2,-24)-h(2,-77):0,c=e<0||0===e&&1/e<0?1:0,f=0;for((e=p(e))!=e||e===1/0?(i=e!=e?1:0,r=u):(r=d(v(e)/y),e*(n=h(2,-r))<1&&(r--,n*=2),2<=(e+=1<=r+s?l/n:l*h(2,1-s))*n&&(r++,n/=2),u<=r+s?(i=0,r=u):1<=r+s?(i=(e*n-1)*h(2,t),r+=s):(i=e*h(2,s-1)*h(2,t),r=0));8<=t;o[f++]=255&i,i/=256,t-=8);for(r=r<>1,u=i-7,s=r-1,r=e[s--],l=127&r;for(r>>=7;0>=-u,u+=t;0"+e+""}var o,a=n(9670),u=n(6048),s=n(748),l=n(3501),c=n(490),f=n(317),n=n(6200),p="prototype",h="script",d=n("IE_PROTO"),v=function(){try{o=document.domain&&new ActiveXObject("htmlfile")}catch(e){}var e;v=o?function(e){e.write(i("")),e.close();var t=e.parentWindow.Object;return e=null,t}(o):((e=f("iframe")).style.display="none",c.appendChild(e),e.src=String("javascript:"),(e=e.contentWindow.document).open(),e.write(i("document.F=Object")),e.close(),e.F);for(var t=s.length;t--;)delete v[p][s[t]];return v()};l[d]=!0,e.exports=Object.create||function(e,t){var n;return null!==e?(r[p]=a(e),n=new r,r[p]=null,n[d]=e):n=v(),void 0===t?n:u(n,t)}},6048:function(e,t,n){var r=n(9781),a=n(3070),u=n(9670),s=n(1956);e.exports=r?Object.defineProperties:function(e,t){u(e);for(var n,r=s(t),i=r.length,o=0;oi;)a(r,n=t[i++])&&(~s(o,n)||o.push(n));return o}},1956:function(e,t,n){var r=n(6324),i=n(748);e.exports=Object.keys||function(e){return r(e,i)}},5296:function(e,t){"use strict";var n={}.propertyIsEnumerable,r=Object.getOwnPropertyDescriptor,i=r&&!n.call({1:2},1);t.f=i?function(e){e=r(this,e);return!!e&&e.enumerable}:n},7674:function(e,t,n){var i=n(9670),o=n(6077);e.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var n,r=!1,e={};try{(n=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set).call(e,[]),r=e instanceof Array}catch(e){}return function(e,t){return i(e),o(t),r?n.call(e,t):e.__proto__=t,e}}():void 0)},288:function(e,t,n){"use strict";var r=n(1694),i=n(648);e.exports=r?{}.toString:function(){return"[object "+i(this)+"]"}},3887:function(e,t,n){var r=n(5005),i=n(8006),o=n(5181),a=n(9670);e.exports=r("Reflect","ownKeys")||function(e){var t=i.f(a(e)),n=o.f;return n?t.concat(n(e)):t}},857:function(e,t,n){n=n(7854);e.exports=n},2248:function(e,t,n){var i=n(1320);e.exports=function(e,t,n){for(var r in t)i(e,r,t[r],n);return e}},1320:function(e,t,n){var u=n(7854),s=n(8880),l=n(6656),c=n(3505),r=n(2788),n=n(9909),i=n.get,f=n.enforce,p=String(String).split("String");(e.exports=function(e,t,n,r){var i=!!r&&!!r.unsafe,o=!!r&&!!r.enumerable,a=!!r&&!!r.noTargetGet;"function"==typeof n&&("string"!=typeof t||l(n,"name")||s(n,"name",t),(r=f(n)).source||(r.source=p.join("string"==typeof t?t:""))),e!==u?(i?!a&&e[t]&&(o=!0):delete e[t],o?e[t]=n:s(e,t,n)):o?e[t]=n:c(t,n)})(Function.prototype,"toString",function(){return"function"==typeof this&&i(this).source||r(this)})},7651:function(e,t,n){var r=n(4326),i=n(2261);e.exports=function(e,t){var n=e.exec;if("function"==typeof n){n=n.call(e,t);if("object"!=typeof n)throw TypeError("RegExp exec method returned something other than an Object or null");return n}if("RegExp"!==r(e))throw TypeError("RegExp#exec called on incompatible receiver");return i.call(e,t)}},2261:function(e,t,n){"use strict";var r,f=n(7066),i=n(2999),p=RegExp.prototype.exec,h=String.prototype.replace,o=p,d=(r=/a/,n=/b*/g,p.call(r,"a"),p.call(n,"a"),0!==r.lastIndex||0!==n.lastIndex),v=i.UNSUPPORTED_Y||i.BROKEN_CARET,y=void 0!==/()??/.exec("")[1];(d||y||v)&&(o=function(e){var t,n,r,i,o=this,a=v&&o.sticky,u=f.call(o),s=o.source,l=0,c=e;return a&&(-1===(u=u.replace("y","")).indexOf("g")&&(u+="g"),c=String(e).slice(o.lastIndex),0x((m-o)/f))throw RangeError(b);for(o+=(l-i)*f,i=l,c=0;cm)throw RangeError(b);if(t==i){for(var p=o,h=36;;h+=36){var d=h<=a?1:a+26<=h?26:h-a;if(p>1,e+=x(e/t);455=t.length?{value:e.target=void 0,done:!0}:"keys"==n?{value:r,done:!1}:"values"==n?{value:t[r],done:!1}:{value:[r,t[r]],done:!1}},"values"),o.Arguments=o.Array,i("keys"),i("values"),i("entries")},1249:function(e,t,n){"use strict";var r=n(2109),i=n(2092).map;r({target:"Array",proto:!0,forced:!n(1194)("map")},{map:function(e,t){return i(this,e,1=t.length?{value:void 0,done:!0}:(n=r(t,n),e.index+=n.length,{value:n,done:!1})})},4723:function(e,t,n){"use strict";var r=n(7007),c=n(9670),f=n(7466),i=n(4488),p=n(1530),h=n(7651);r("match",1,function(r,s,l){return[function(e){var t=i(this),n=null==e?void 0:e[r];return void 0!==n?n.call(e,t):new RegExp(e)[r](String(t))},function(e){var t=l(s,e,this);if(t.done)return t.value;var n=c(e),r=String(this);if(!n.global)return h(n,r);for(var i=n.unicode,o=[],a=n.lastIndex=0;null!==(u=h(n,r));){var u=String(u[0]);""===(o[a]=u)&&(n.lastIndex=p(r,f(n.lastIndex),i)),a++}return 0===a?null:o}]})},5306:function(e,t,n){"use strict";var r=n(7007),S=n(9670),F=n(7466),T=n(9958),o=n(4488),C=n(1530),L=n(647),R=n(7651),I=Math.max,U=Math.min;r("replace",2,function(i,x,w,e){var E=e.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE,k=e.REPLACE_KEEPS_$0,A=E?"$":"$0";return[function(e,t){var n=o(this),r=null==e?void 0:e[i];return void 0!==r?r.call(e,n,t):x.call(String(n),e,t)},function(e,t){if(!E&&k||"string"==typeof t&&-1===t.indexOf(A)){var n=w(x,e,this,t);if(n.done)return n.value}var r=S(e),i=String(this),o="function"==typeof t;o||(t=String(t));var a,u=r.global;u&&(a=r.unicode,r.lastIndex=0);for(var s=[];;){var l=R(r,i);if(null===l)break;if(s.push(l),!u)break;""===String(l[0])&&(r.lastIndex=C(i,F(r.lastIndex),a))}for(var c,f="",p=0,h=0;h>>0;if(0==r)return[];if(void 0===e)return[n];if(!c(e))return d.call(n,e,r);for(var i,o,a,u=[],t=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(e.sticky?"y":""),s=0,l=new RegExp(e.source,t+"g");(i=p.call(l,n))&&!(s<(o=l.lastIndex)&&(u.push(n.slice(s,i.index)),1=r));)l.lastIndex===i.index&&l.lastIndex++;return s===n.length?!a&&l.test("")||u.push(""):u.push(n.slice(s)),u.length>r?u.slice(0,r):u}:"0".split(void 0,0).length?function(e,t){return void 0===e&&0===t?[]:d.call(this,e,t)}:d;return[function(e,t){var n=f(this),r=null==e?void 0:e[i];return void 0!==r?r.call(e,n,t):y.call(String(n),e,t)},function(e,t){var n=v(y,e,this,t,y!==d);if(n.done)return n.value;var r=g(e),i=String(this),n=m(r,RegExp),o=r.unicode,e=(r.ignoreCase?"i":"")+(r.multiline?"m":"")+(r.unicode?"u":"")+(A?"y":"g"),a=new n(A?r:"^(?:"+r.source+")",e),u=void 0===t?k:t>>>0;if(0==u)return[];if(0===i.length)return null===w(a,i)?[i]:[];for(var s=0,l=0,c=[];le.key){r.splice(t,0,e);break}t===o&&r.push(e)}n.updateURL()},forEach:function(e,t){for(var n,r=O(this).entries,i=x(e,1=_(256,5-t))return null}else if(255":1,"`":1}),J=v({},Z,{"#":1,"?":1,"{":1,"}":1}),ee=v({},J,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),te=function(e,t){var n=y(e,0);return 32=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:t}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,a=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return o=e.done,e},e:function(e){a=!0,i=e},f:function(){try{o||null==n.return||n.return()}finally{if(a)throw i}}}}function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:t}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,a=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return o=e.done,e},e:function(e){a=!0,i=e},f:function(){try{o||null==n.return||n.return()}finally{if(a)throw i}}}}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n'),this.element.appendChild(e));var r=e.getElementsByTagName("span")[0];return r&&(null!=r.textContent?r.textContent=this.options.dictFallbackMessage:null!=r.innerText&&(r.innerText=this.options.dictFallbackMessage)),this.element.appendChild(this.getFallbackForm())},resize:function(e,t,n,r){var i={srcX:0,srcY:0,srcWidth:e.width,srcHeight:e.height},o=e.width/e.height;null==t&&null==n?(t=i.srcWidth,n=i.srcHeight):null==t?t=n*o:null==n&&(n=t/o);var a=(t=Math.min(t,i.srcWidth))/(n=Math.min(n,i.srcHeight));if(i.srcWidth>t||i.srcHeight>n)if("crop"===r)a
Check
Error
',drop:function(){return this.element.classList.remove("dz-drag-hover")},dragstart:function(){},dragend:function(){return this.element.classList.remove("dz-drag-hover")},dragenter:function(){return this.element.classList.add("dz-drag-hover")},dragover:function(){return this.element.classList.add("dz-drag-hover")},dragleave:function(){return this.element.classList.remove("dz-drag-hover")},paste:function(){},reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(t){var n=this;if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer&&!this.options.disablePreviews){t.previewElement=g.createElement(this.options.previewTemplate.trim()),t.previewTemplate=t.previewElement,this.previewsContainer.appendChild(t.previewElement);var e,r=c(t.previewElement.querySelectorAll("[data-dz-name]"),!0);try{for(r.s();!(e=r.n()).done;){var i=e.value;i.textContent=t.name}}catch(e){r.e(e)}finally{r.f()}var o,a=c(t.previewElement.querySelectorAll("[data-dz-size]"),!0);try{for(a.s();!(o=a.n()).done;)(i=o.value).innerHTML=this.filesize(t.size)}catch(e){a.e(e)}finally{a.f()}this.options.addRemoveLinks&&(t._removeLink=g.createElement(''.concat(this.options.dictRemoveFile,"")),t.previewElement.appendChild(t._removeLink));var u,s=function(e){return e.preventDefault(),e.stopPropagation(),t.status===g.UPLOADING?g.confirm(n.options.dictCancelUploadConfirmation,function(){return n.removeFile(t)}):n.options.dictRemoveFileConfirmation?g.confirm(n.options.dictRemoveFileConfirmation,function(){return n.removeFile(t)}):n.removeFile(t)},l=c(t.previewElement.querySelectorAll("[data-dz-remove]"),!0);try{for(l.s();!(u=l.n()).done;)u.value.addEventListener("click",s)}catch(e){l.e(e)}finally{l.f()}}},removedfile:function(e){return null!=e.previewElement&&null!=e.previewElement.parentNode&&e.previewElement.parentNode.removeChild(e.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(e,t){if(e.previewElement){e.previewElement.classList.remove("dz-file-preview");var n=c(e.previewElement.querySelectorAll("[data-dz-thumbnail]"),!0);try{for(n.s();!(r=n.n()).done;){var r=r.value;r.alt=e.name,r.src=t}}catch(e){n.e(e)}finally{n.f()}return setTimeout(function(){return e.previewElement.classList.add("dz-image-preview")},1)}},error:function(e,t){if(e.previewElement){e.previewElement.classList.add("dz-error"),"string"!=typeof t&&t.error&&(t=t.error);var n,r=c(e.previewElement.querySelectorAll("[data-dz-errormessage]"),!0);try{for(r.s();!(n=r.n()).done;)n.value.textContent=t}catch(e){r.e(e)}finally{r.f()}}},errormultiple:function(){},processing:function(e){if(e.previewElement&&(e.previewElement.classList.add("dz-processing"),e._removeLink))return e._removeLink.innerHTML=this.options.dictCancelUpload},processingmultiple:function(){},uploadprogress:function(e,t){if(e.previewElement){var n=c(e.previewElement.querySelectorAll("[data-dz-uploadprogress]"),!0);try{for(n.s();!(r=n.n()).done;){var r=r.value;"PROGRESS"===r.nodeName?r.value=t:r.style.width="".concat(t,"%")}}catch(e){n.e(e)}finally{n.f()}}},totaluploadprogress:function(){},sending:function(){},sendingmultiple:function(){},success:function(e){if(e.previewElement)return e.previewElement.classList.add("dz-success")},successmultiple:function(){},canceled:function(e){return this.emit("error",e,this.options.dictUploadCanceled)},canceledmultiple:function(){},complete:function(e){if(e._removeLink&&(e._removeLink.innerHTML=this.options.dictRemoveFile),e.previewElement)return e.previewElement.classList.add("dz-complete")},completemultiple:function(){},maxfilesexceeded:function(){},maxfilesreached:function(){},queuecomplete:function(){},addedfiles:function(){}};function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function w(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return f(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return f(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,t=function(){};return{s:t,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:t}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,a=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return o=e.done,e},e:function(e){a=!0,i=e},f:function(){try{o||null==n.return||n.return()}finally{if(a)throw i}}}}function f(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n"))),this.clickableElements.length&&function r(){i.hiddenFileInput&&i.hiddenFileInput.parentNode.removeChild(i.hiddenFileInput),i.hiddenFileInput=document.createElement("input"),i.hiddenFileInput.setAttribute("type","file"),(null===i.options.maxFiles||1")),t+='');t=x.createElement(t);return"FORM"!==this.element.tagName?(e=x.createElement('
'))).appendChild(t):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=e?e:t}},{key:"getExistingFallback",value:function(){for(var e=0,t=["div","form"];e".concat(t," ").concat(this.options.dictFileSizeUnits[n])}},{key:"_updateMaxFilesReachedClass",value:function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")}},{key:"drop",value:function(e){if(e.dataTransfer){this.emit("drop",e);for(var t,n=[],r=0;r1024*this.options.maxFilesize*1024?t(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(e.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):x.isValidFile(e,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(t(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",e)):this.options.accept.call(this,e,t):t(this.options.dictInvalidFileType)}},{key:"addFile",value:function(t){var n=this;t.upload={uuid:x.uuidv4(),progress:0,total:t.size,bytesSent:0,filename:this._renameFile(t)},this.files.push(t),t.status=x.ADDED,this.emit("addedfile",t),this._enqueueThumbnail(t),this.accept(t,function(e){e?(t.accepted=!1,n._errorProcessing([t],e)):(t.accepted=!0,n.options.autoQueue&&n.enqueueFile(t)),n._updateMaxFilesReachedClass()})}},{key:"enqueueFiles",value:function(e){var t=w(e,!0);try{for(t.s();!(n=t.n()).done;){var n=n.value;this.enqueueFile(n)}}catch(e){t.e(e)}finally{t.f()}return null}},{key:"enqueueFile",value:function(e){var t=this;if(e.status!==x.ADDED||!0!==e.accepted)throw new Error("This file can't be queued because it has already been processed or was rejected.");if(e.status=x.QUEUED,this.options.autoProcessQueue)return setTimeout(function(){return t.processQueue()},0)}},{key:"_enqueueThumbnail",value:function(e){var t=this;if(this.options.createImageThumbnails&&e.type.match(/image.*/)&&e.size<=1024*this.options.maxThumbnailFilesize*1024)return this._thumbnailQueue.push(e),setTimeout(function(){return t._processThumbnailQueue()},0)}},{key:"_processThumbnailQueue",value:function(){var t=this;if(!this._processingThumbnail&&0!==this._thumbnailQueue.length){this._processingThumbnail=!0;var n=this._thumbnailQueue.shift();return this.createThumbnail(n,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,!0,function(e){return t.emit("thumbnail",n,e),t._processingThumbnail=!1,t._processThumbnailQueue()})}}},{key:"removeFile",value:function(e){if(e.status===x.UPLOADING&&this.cancelUpload(e),this.files=m(this.files,e),this.emit("removedfile",e),0===this.files.length)return this.emit("reset")}},{key:"removeAllFiles",value:function(e){null==e&&(e=!1);var t=w(this.files.slice(),!0);try{for(t.s();!(n=t.n()).done;){var n=n.value;n.status===x.UPLOADING&&!e||this.removeFile(n)}}catch(e){t.e(e)}finally{t.f()}return null}},{key:"resizeImage",value:function(r,e,t,n,i){var o=this;return this.createThumbnail(r,e,t,n,!0,function(e,t){if(null==t)return i(r);var n=o.options.resizeMimeType;null==n&&(n=r.type);t=t.toDataURL(n,o.options.resizeQuality);return"image/jpeg"!==n&&"image/jpg"!==n||(t=k.restore(r.dataURL,t)),i(x.dataURItoBlob(t))})}},{key:"createThumbnail",value:function(e,t,n,r,i,o){var a=this,u=new FileReader;u.onload=function(){e.dataURL=u.result,"image/svg+xml"!==e.type?a.createThumbnailFromUrl(e,t,n,r,i,o):null!=o&&o(u.result)},u.readAsDataURL(e)}},{key:"displayExistingFile",value:function(t,e,n,r,i){var o=this,i=!(4l.options.chunkSize),s[0].upload.totalChunkCount=Math.ceil(t.size/l.options.chunkSize)),s[0].upload.chunked){var i=s[0],r=e[0];i.upload.chunks=[];var o=function(){for(var e,t,n=0;void 0!==i.upload.chunks[n];)n++;n>=i.upload.totalChunkCount||(e=n*l.options.chunkSize,t=Math.min(e+l.options.chunkSize,r.size),t={name:l._getParamName(0),data:r.webkitSlice?r.webkitSlice(e,t):r.slice(e,t),filename:i.upload.filename,chunkIndex:n},i.upload.chunks[n]={file:i,index:n,dataBlock:t,status:x.UPLOADING,progress:0,retries:0},l._uploadData(s,[t]))};if(i.upload.finishedChunkUpload=function(e,t){var n=!0;e.status=x.SUCCESS,e.dataBlock=null,e.xhr=null;for(var r=0;r>1;t=a/t;return 0==t?1:t}(t);return e.drawImage(t,n,r,i,o,a,u,s,l/c)},k=function(){function e(){a(this,e)}return t(e,null,[{key:"initClass",value:function(){this.KEY_STR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}},{key:"encode64",value:function(e){for(var t,n,r,i,o="",a="",u=void 0,s="",l=0;r=(t=e[l++])>>2,i=(3&t)<<4|(n=e[l++])>>4,u=(15&n)<<2|(a=e[l++])>>6,s=63&a,isNaN(n)?u=s=64:isNaN(a)&&(s=64),o=o+this.KEY_STR.charAt(r)+this.KEY_STR.charAt(i)+this.KEY_STR.charAt(u)+this.KEY_STR.charAt(s),u=s=a="",le.length)break}return i}},{key:"decode64",value:function(e){var t,n,r,i,o=void 0,a="",u=0,s=[];for(/[^A-Za-z0-9\+\/\=]/g.exec(e)&&console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");t=this.KEY_STR.indexOf(e.charAt(u++))<<2|(n=this.KEY_STR.indexOf(e.charAt(u++)))>>4,o=(15&n)<<4|(r=this.KEY_STR.indexOf(e.charAt(u++)))>>2,a=(3&r)<<6|(i=this.KEY_STR.indexOf(e.charAt(u++))),s.push(t),64!==r&&s.push(o),64!==i&&s.push(a),o=a="",ut.length)&&(e=t.length);for(var n=0,o=new Array(e);n"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),G("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),G("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style["will-change"]="all",o=function(){G("delayEnded",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:"choose",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(V,t.trim(),Ht)}),h(l,"dragover",Yt),h(l,"mousemove",Yt),h(l,"touchmove",Yt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(V,"dragend",this),h(Q,"dragstart",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfvideo/img/facebook.svg b/media/plg_fields_acfvideo/img/facebook.svg new file mode 100644 index 00000000..b3934ebb --- /dev/null +++ b/media/plg_fields_acfvideo/img/facebook.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfvideo/img/selfhostedvideo.svg b/media/plg_fields_acfvideo/img/selfhostedvideo.svg new file mode 100644 index 00000000..e1d5e4cc --- /dev/null +++ b/media/plg_fields_acfvideo/img/selfhostedvideo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/media/plg_fields_acfvideo/img/vimeo.svg b/media/plg_fields_acfvideo/img/vimeo.svg new file mode 100644 index 00000000..6e9f22ba --- /dev/null +++ b/media/plg_fields_acfvideo/img/vimeo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/media/plg_fields_acfvideo/img/youtube.svg b/media/plg_fields_acfvideo/img/youtube.svg new file mode 100644 index 00000000..7712e15a --- /dev/null +++ b/media/plg_fields_acfvideo/img/youtube.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/media/plg_system_acf/css/acf-backend.css b/media/plg_system_acf/css/acf-backend.css new file mode 100644 index 00000000..ce708ba4 --- /dev/null +++ b/media/plg_system_acf/css/acf-backend.css @@ -0,0 +1,3 @@ +body.acf-field-previewer-open{overflow:hidden}.acf-field-previewer{-webkit-box-sizing:border-box;box-sizing:border-box;position:fixed;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;bottom:46px;right:16px;min-width:500px;border:1px solid #ccc;border-radius:8px;padding:16px;-webkit-box-shadow:0 0 12px 0 rgba(0,0,0,0.1);box-shadow:0 0 12px 0 rgba(0,0,0,0.1);background:#fff;z-index:9999}.acf-field-previewer.fullscreen{width:100% !important;height:100% !important;top:0 !important;left:0 !important;right:auto;bottom:auto;z-index:99999;border-radius:0;padding:0;background:#f5f5f5}.acf-field-previewer.fullscreen .resizers{display:none}.acf-field-previewer.fullscreen .title{padding:8px;background:#fff;-webkit-box-shadow:0px 0px 2px 3px rgba(0,0,0,0.05);box-shadow:0px 0px 2px 3px rgba(0,0,0,0.05);z-index:1}.acf-field-previewer.fullscreen .title .fullscreen-actions svg:nth-child(1){display:none}.acf-field-previewer.fullscreen .title .acf-responsive-controls{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;gap:8px}.acf-field-previewer.fullscreen .title>*{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.acf-field-previewer.fullscreen .title>.left .acf-field-previewer-loading-indicator{margin-right:auto}.acf-field-previewer.fullscreen .title>.actions .fullscreen-actions{margin-left:auto}.acf-field-previewer.fullscreen .body{height:100% !important;padding:0;margin-top:0;overflow-y:scroll}.acf-field-previewer.fullscreen .body iframe{margin:0 auto;position:relative;top:auto;left:auto;border-width:8px;border-color:#fff;background:#fff;padding:16px}.acf-field-previewer.fullscreen[data-device="desktop"] .title .acf-field-preview-responsive-device[data-device="desktop"]{opacity:1}.acf-field-previewer.fullscreen[data-device="desktop"] .body{background:#fff}.acf-field-previewer.fullscreen[data-device="desktop"] .body iframe{height:100% !important}.acf-field-previewer.fullscreen[data-device="tablet"] .body iframe,.acf-field-previewer.fullscreen[data-device="mobile"] .body iframe{margin:28px auto;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.05);box-shadow:0 4px 10px rgba(0,0,0,0.05);border-radius:16px}.acf-field-previewer.fullscreen[data-device="tablet"] .title .acf-field-preview-responsive-device[data-device="tablet"]{opacity:1}.acf-field-previewer.fullscreen[data-device="tablet"] .body iframe{width:768px;height:1024px !important}.acf-field-previewer.fullscreen[data-device="mobile"] .title .acf-field-preview-responsive-device[data-device="mobile"]{opacity:1}.acf-field-previewer.fullscreen[data-device="mobile"] .body iframe{width:375px;height:667px !important}.acf-field-previewer:not(.fullscreen) .fullscreen-actions svg:nth-child(2){display:none}.acf-field-previewer .acf-field-previewer-loading-indicator{opacity:0;visibility:hidden;z-index:-1;font-size:10px;position:relative;text-indent:-9999em;border-top:2px solid #333;border-right:2px solid #333;border-bottom:2px solid #333;border-left:2px solid #ffffff;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.acf-field-previewer .acf-field-previewer-loading-indicator.is-visible{opacity:1;visibility:visible;z-index:5;-webkit-animation:acf-field-previewer-loading 1.1s infinite linear;animation:acf-field-previewer-loading 1.1s infinite linear}@-webkit-keyframes acf-field-previewer-loading{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes acf-field-previewer-loading{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.acf-field-previewer .acf-field-previewer-loading-indicator,.acf-field-previewer .acf-field-previewer-loading-indicator:after{border-radius:50%;width:12px;height:12px}.acf-field-previewer>.title{display:-webkit-box;display:-ms-flexbox;display:flex;font-weight:bold;text-transform:uppercase;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;gap:32px;font-size:13px}.acf-field-previewer>.title .info-icon,.acf-field-previewer>.title .left{gap:4px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.acf-field-previewer>.title .acf-responsive-controls{display:none}.acf-field-previewer>.title .acf-responsive-controls .acf-field-preview-responsive-device{cursor:pointer}.acf-field-previewer>.title .acf-responsive-controls .acf-field-preview-responsive-device:not(.active){opacity:.5}.acf-field-previewer>.title .acf-responsive-controls .acf-field-preview-responsive-device:hover{opacity:1}.acf-field-previewer>.title .actions{display:-webkit-box;display:-ms-flexbox;display:flex;gap:16px}.acf-field-previewer>.title .actions .fullscreen-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-right:1px solid #ccc;padding-right:16px}.acf-field-previewer>.title .actions svg,.acf-field-previewer>.title .actions a{color:#333;cursor:pointer}.acf-field-previewer>.title .actions svg:hover,.acf-field-previewer>.title .actions a:hover{color:#000}.acf-field-previewer>.title .actions a{display:inline-block;position:relative;width:16px;height:16px}.acf-field-previewer>.title .actions a:before,.acf-field-previewer>.title .actions a:after{position:absolute;content:' ';right:7px;height:17px;width:1px;background-color:#333}.acf-field-previewer>.title .actions a:before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.acf-field-previewer>.title .actions a:after{opacity:0;visibility:hidden;z-index:-1}.acf-field-previewer>.body{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;margin-top:8px;padding-top:16px;border-top:1px solid #ccc;width:100%;overflow:hidden;-webkit-box-sizing:content-box;box-sizing:content-box}.acf-field-previewer>.body iframe{width:100%;border:0;margin:0;padding:0;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.acf-field-previewer>.body iframe.refreshing{-webkit-transition:none;transition:none;opacity:0;visibility:hidden;z-index:-1}.acf-field-previewer.is-hidden{min-width:auto !important}.acf-field-previewer.is-hidden .resizers{display:none}.acf-field-previewer.is-hidden .body,.acf-field-previewer.is-hidden .actions .fullscreen-actions{display:none}.acf-field-previewer.is-hidden .actions a:after{opacity:1;visibility:visible;z-index:2}.acf-field-previewer.is-hidden-full{display:none !important}@media only screen and (max-width: 576px){.acf-field-previewer{min-width:auto;right:8px;left:8px;bottom:8px}}.spacer .acf label{font-weight:bold;font-size:13px;margin-top:20px;border-left:dotted 1px #a9a9a9;padding-left:8px;cursor:default;text-transform:uppercase;letter-spacing:1px}.spacer .acf-l-b label{font-weight:bold}#attrib-conditions{padding:30px}#attrib-conditions #fieldset-conditions{padding:0;margin:0;border:none}#attrib-conditions #fieldset-conditions>legend{display:none}.well.assign{margin:0;border-color:#fff;padding:15px}.acfupload-edit-modal-content{padding:16px}.acfupload-edit-modal-content .acfupload-edit-modal-editing-item{margin-bottom:16px;font-size:13px}.acfupload-edit-modal-content .acfupload-edit-modal-editing-item .acfupload-edit-modal-editing-item-name{font-style:italic} + + diff --git a/media/plg_system_acf/css/joomla3.css b/media/plg_system_acf/css/joomla3.css new file mode 100644 index 00000000..de71701e --- /dev/null +++ b/media/plg_system_acf/css/joomla3.css @@ -0,0 +1,3 @@ +.acfupload,.cfup-file{font-size:14px}#attrib-conditions{padding:0}.acf-address-field-location-details input[type="text"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:13px 8px;width:100%}#acfUploadItemEditModal input[type="text"]{width:100%;-webkit-box-sizing:border-box;box-sizing:border-box;height:auto}.acf-countdown-item-edit-wrapper .controls{margin:0 !important}.tf-phone-control{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);border-radius:3px;padding:4px 6px} + + diff --git a/media/plg_system_acf/css/joomla4.css b/media/plg_system_acf/css/joomla4.css new file mode 100644 index 00000000..fb4f015d --- /dev/null +++ b/media/plg_system_acf/css/joomla4.css @@ -0,0 +1,3 @@ +.nrtoggle{margin:6px 0} + + diff --git a/media/plg_system_acf/css/responsive_embed.css b/media/plg_system_acf/css/responsive_embed.css new file mode 100644 index 00000000..386be52e --- /dev/null +++ b/media/plg_system_acf/css/responsive_embed.css @@ -0,0 +1,3 @@ +.acf-responsive-embed{position:relative;padding-bottom:56.25%;height:0;overflow:hidden}.acf-responsive-embed iframe,.acf-responsive-embed object,.acf-responsive-embed embed{position:absolute;top:0;left:0;width:100%;height:100%} + + diff --git a/media/plg_system_acf/data/previewer/.htaccess b/media/plg_system_acf/data/previewer/.htaccess new file mode 100644 index 00000000..cb7f1227 --- /dev/null +++ b/media/plg_system_acf/data/previewer/.htaccess @@ -0,0 +1,6 @@ + + # Block direct PHP access + + deny from all + + \ No newline at end of file diff --git a/media/plg_system_acf/data/previewer/index.html b/media/plg_system_acf/data/previewer/index.html new file mode 100644 index 00000000..3af63015 --- /dev/null +++ b/media/plg_system_acf/data/previewer/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_acf/js/field_previewer.js b/media/plg_system_acf/js/field_previewer.js new file mode 100644 index 00000000..4b5d1837 --- /dev/null +++ b/media/plg_system_acf/js/field_previewer.js @@ -0,0 +1,2 @@ +var ACF_Field_Previewer=function(){function e(e){this.field=e,this.field_name="acf"+e.replace(/\s+/g,"").toLowerCase(),this.wrapper=".acf-field-previewer",this.ACFFieldsPreviewerData=window.ACFFieldsPreviewerData||{},this.JoomlaOptions=null,this.root_url=this.getOption("root_url"),this.base_url=this.root_url.replace("/administrator",""),this.app_ajax_url="?option=com_ajax&format=raw&plugin=acf&task=FieldsPreviewer",this.preview_ajax_url="?option=com_ajax&format=raw&plugin=acf&task=FieldsPreviewerHTML",this.run()}var t=e.prototype;return t.run=function(){if(window.MutationObserver){var e=document.querySelector('select[id="jform_type"]');if(e)if(e.value!==this.field_name){var t=this,i=new MutationObserver(function(e){if(e)for(m in e)e[m].target.value.startsWith(t.field_name)&&(t.previewerInit(),i.disconnect())});i.observe(e,{childList:!0,subtree:!0,attributes:!0})}else this.previewerInit()}},t.previewerInit=function(){var e=this;this.addPreviewerToPage(),this.isPreviewerVisible()&&this.updatePreviewer(),setTimeout(function(){e.togglePreviewerByTab()},100),this.initEvents()},t.initEvents=function(){function t(e,t,i){r||(r=!0,setTimeout(function(){e(i),r=!1},t))}var r=!1;document.addEventListener("click",function(e){this.toggleFullscreen(e),this.toggleOpenClose(e),this.selectResponsiveControl(e),this.J3togglePreviewerOnTab(e),this.J4togglePreviewerOnTab(e)}.bind(this)),document.addEventListener("blur",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this)),jQuery(document).on("blur","input.minicolors",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this)),jQuery(document).on("change",".tfHasChosen",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this)),document.addEventListener("change",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this))},t.J3togglePreviewerOnTab=function(e){var t=e.target.closest(".nav.nav-tabs");t&&t.parentElement.classList.contains("form-horizontal")&&e.target.closest("a")&&this.togglePreviewerByTab()},t.J4togglePreviewerOnTab=function(e){e.target.closest("joomla-tab")&&e.target.closest('button[type="button"]')&&this.togglePreviewerByTab()},t.togglePreviewerByTab=function(){this.isJ3GeneralTab()||this.isJ4GeneralTab()?this.showPreviewer():this.hidePreviewer(!0)},t.isJ3GeneralTab=function(){var e=document.querySelector("form > .form-horizontal > .nav.nav-tabs > li.active > a");if(e)return"#general"===e.getAttribute("href")},t.isJ4GeneralTab=function(){var e=document.querySelector('form joomla-tab button[aria-expanded="true"]');if(e)return"general"===e.getAttribute("aria-controls")},t.toggleFullscreen=function(e){var t=e.target.closest(".acf-field-previewer-fullscreen-toggle");if(t){document.body.classList.toggle("acf-field-previewer-open");var i=this.getPreviewer(t);i.classList.toggle("fullscreen"),i.classList.contains("fullscreen")||(i.querySelector(".body iframe").height=""),this.updatePreviewer(e)}},t.toggleOpenClose=function(e){var t=e.target.closest(".acf-field-previewer-show-hide-btn");if(t){e.preventDefault();var i=this.getPreviewer(t);document.body.classList.remove("acf-field-previewer-open"),i.classList.remove("fullscreen");var r=!i.classList.contains("is-hidden");i.querySelector(".body iframe").height="",localStorage.setItem("acf-field-previewer-hidden",r),r?this.hidePreviewer():this.showPreviewer()}},t.isPreviewerVisible=function(){return!this.getPreviewer().classList.contains("is-hidden")},t.showPreviewer=function(){var e=this.getPreviewer(),t=localStorage.getItem("acf-field-previewer-hidden");e.classList.contains("is-hidden")&&("true"!==t&&e.classList.remove("is-hidden"),e.classList.remove("is-hidden-full"),this.updatePreviewer())},t.hidePreviewer=function(e){void 0===e&&(e=!1);var t=this.getPreviewer();e&&t.classList.add("is-hidden-full"),t.classList.contains("is-hidden")||t.classList.add("is-hidden")},t.selectResponsiveControl=function(e){var t=e.target.closest(".acf-field-preview-responsive-device");t&&this.getPreviewer(t).setAttribute("data-device",t.dataset.device)},t.addPreviewerToPage=function(){var e=document.createElement("div"),t=this.wrapper.substring(1),i=localStorage.getItem("acf-field-previewer-hidden");(this.ACFFieldsPreviewerData.hidden&&!i||"true"===i)&&(t+=" is-hidden"),this.ACFFieldsPreviewerData.width&&(e.style.minWidth=this.ACFFieldsPreviewerData.width+"px"),this.ACFFieldsPreviewerData.height&&(e.style.minHeight=this.ACFFieldsPreviewerData.height+"px"),e.className=t,e.dataset.device="desktop";var r=document.createElement("div");r.className="title";var s=document.createElement("div");s.className="left";var n='
';if(s.innerHTML=n,s.innerHTML+=window.parent.Joomla.Text._("ACF_FIELD_PREVIEWER"),s.innerHTML+='
',r.appendChild(s),this.ACFFieldsPreviewerData.responsiveControls){var o=document.createElement("div");o.className="acf-responsive-controls",o.innerHTML+='',o.innerHTML+='',o.innerHTML+='',r.appendChild(o)}var a=document.createElement("div");a.className="actions",this.ACFFieldsPreviewerData.fullscreenActions&&(a.innerHTML='
'),a.innerHTML+='',r.appendChild(a),e.appendChild(r);var l=document.createElement("div");l.className="body";var c=document.createElement("iframe");c.src=this.getIFrameURL(),l.appendChild(c),e.appendChild(l),document.body.appendChild(e)},t.getIFrameURL=function(){return this.root_url+this.preview_ajax_url+"&field="+this.field_name+"&"+this.getJoomlaOption("csrf.token")+"=1"},t.updatePreviewer=function(e){if(!(e&&e.target&&e.target.name&&-1===e.target.name.indexOf("jform[fieldparams]"))){var t=this.getPreviewer();if(!t.classList.contains("is-hidden")){var i=new FormData(document.querySelector('form[name="adminForm"]'));(i=Object.fromEntries(i)).fullscreen=this.ACFFieldsPreviewerData.fullscreenActions&&t.classList.contains("fullscreen");var r=this,s=this.root_url+this.app_ajax_url+"&field="+this.field+"&"+this.getJoomlaOption("csrf.token")+"=1";this.showLoadingIndicator();var n=this.getIFrame();n.classList.add("refreshing"),n.height="",fetch(s,{method:"post",body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){e.error?alert(e.message):r.updatePreviewerContents(function(){r.hideLoadingIndicator()})}).catch(function(e){alert(e)})}}},t.showLoadingIndicator=function(){document.querySelector(".acf-field-previewer-loading-indicator").classList.add("is-visible")},t.hideLoadingIndicator=function(){document.querySelector(".acf-field-previewer-loading-indicator").classList.remove("is-visible")},t.updatePreviewerContents=function(e){void 0===e&&(e=null);var t=this.getIFrame();t.src=this.getIFrameURL(),t.height="",t.onload=function(){t.classList.remove("refreshing"),t.height=t.contentWindow.document.body.scrollHeight+"px","function"==typeof e&&e()}},t.getIFrame=function(){return document.querySelector(".acf-field-previewer iframe")},t.getJoomlaOption=function(e,t){if(!this.JoomlaOptions){var i=document.querySelector(".joomla-script-options");if(!i)return;this.JoomlaOptions=JSON.parse(i.text||i.textContent)}return void 0!==this.JoomlaOptions[e]?this.JoomlaOptions[e]:t},t.getOption=function(e,t){t=void 0===t?"":t;var i=this.getJoomlaOption("acf_js_object");return i&&void 0!==i[e]?i[e]:t},t.getPreviewer=function(e){return void 0===e&&(e=""),e?e.closest(this.wrapper):document.querySelector(this.wrapper)},e}(); + diff --git a/media/plg_system_acf/js/helper.js b/media/plg_system_acf/js/helper.js new file mode 100644 index 00000000..7deeff7c --- /dev/null +++ b/media/plg_system_acf/js/helper.js @@ -0,0 +1,2 @@ +!function(window,document){"use strict";var helper={asyncScript:function(js_script){document.addEventListener("DOMContentLoaded",function(){window.addEventListener("load",function(){eval(js_script)})})},lazyloadIframes:function(){document.querySelectorAll("iframe.lazyload[data-src]").forEach(function(t){t.setAttribute("src",t.getAttribute("data-src")),t.removeAttribute("data-src")})}};window.ACFHelper=helper}(window,document); + diff --git a/media/plg_system_nrframework/css/addresslookup.css b/media/plg_system_nrframework/css/addresslookup.css new file mode 100644 index 00000000..d63a4b51 --- /dev/null +++ b/media/plg_system_nrframework/css/addresslookup.css @@ -0,0 +1,2 @@ +.tf-address-lookup-container{position:relative}.tf-address-lookup-container.is-hidden{display:none}.tf-address-lookup-container input{padding-right:35px !important}.tf-address-lookup-container svg{position:absolute;right:9px;top:9px;stroke:#999}.tf-address-lookup-container .tf-address-lookup-field-autocomplete-results{position:absolute;display:none;width:100%;height:250px;background:#fff;border:1px solid #dedede;z-index:500;overflow:auto}.tf-address-lookup-container .tf-address-lookup-field-autocomplete-results .tf-address-lookup-field-autocomplete-item{padding:5px;cursor:pointer}.tf-address-lookup-container .tf-address-lookup-field-autocomplete-results .tf-address-lookup-field-autocomplete-item:hover{background:#f5f5f5}.tf-address-lookup-container .tf-address-lookup-field-autocomplete-results .tf-address-lookup-field-autocomplete-item+.tf-address-lookup-field-autocomplete-item{border-top:1px solid #ededed}.tf-address-lookup-container .tf-address-lookup-field-autocomplete-results.is-visible{display:block} + diff --git a/media/plg_system_nrframework/css/assignmentselection.css b/media/plg_system_nrframework/css/assignmentselection.css new file mode 100644 index 00000000..fa4d1526 --- /dev/null +++ b/media/plg_system_nrframework/css/assignmentselection.css @@ -0,0 +1,2 @@ +.assign{background-color:#f0f0f0;border:solid 1px #dedede;color:inherit !important;padding:10px;margin-bottom:-1px}.assign .control-group{display:-webkit-box;display:-ms-flexbox;display:flex}.assign .control-group .control-label{max-width:260px;padding:0}.assign .control-group>div{margin:0;-webkit-box-flex:1;-ms-flex:1;flex:1}.assign .control-group.assignmentselection{-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0}.assign .control-group.assignmentselection label{margin:0;padding:0}.assign .control-group.assignmentselection .chzn-color-state.chzn-single[rel=value_0]{background:none !important;color:inherit !important}.assign .control-group.assignmentselection .chzn-color-state.chzn-single[rel=value_2]{background-color:#bd362f !important;color:#fff}.assign .assign-options .control-group{margin:10px 0 0 0}.assign .assign-options .control-label>label{padding-top:5px}.assign .btn_ignore.btn.active{background-color:#efefef;color:#555}.assign .btn_exclude.btn.active{background-color:#942a25;border-color:#942a25}.assign .btn{min-width:60px}.assign .controls:empty{display:none}.assign .well-note{font-size:.9em}.assign-group{padding-top:20px;margin-top:20px}.assign-group p{opacity:.7;margin-bottom:15px}.assign-group>label{font-weight:bold;font-size:16px} + diff --git a/media/plg_system_nrframework/css/chainedfields.css b/media/plg_system_nrframework/css/chainedfields.css new file mode 100644 index 00000000..00c82415 --- /dev/null +++ b/media/plg_system_nrframework/css/chainedfields.css @@ -0,0 +1,2 @@ +.nr-chained-fields{display:-webkit-box;display:-ms-flexbox;display:flex;gap:16px;-ms-flex-wrap:wrap;flex-wrap:wrap}.nr-chained-fields select{-webkit-box-flex:1;-ms-flex:1 0 300px;flex:1 0 300px} + diff --git a/media/plg_system_nrframework/css/colorpicker-layout.css b/media/plg_system_nrframework/css/colorpicker-layout.css new file mode 100644 index 00000000..c2d83009 --- /dev/null +++ b/media/plg_system_nrframework/css/colorpicker-layout.css @@ -0,0 +1,3 @@ +.nrf-colorpicker-wrapper:not(.no-style){position:relative}.nrf-colorpicker-wrapper:not(.no-style) .nrf-colorpicker{position:absolute;left:3px;top:2px;width:33px;height:33px;padding:0;margin:0;border:none;outline:none;-webkit-box-shadow:none;box-shadow:none;background:transparent}.nrf-colorpicker-wrapper:not(.no-style) .nrf-colorpicker-input{border-radius:3px;padding:10px 10px 10px 40px;height:auto;line-height:1} + + diff --git a/media/plg_system_nrframework/css/conditionbuilder.css b/media/plg_system_nrframework/css/conditionbuilder.css new file mode 100644 index 00000000..8f694c6f --- /dev/null +++ b/media/plg_system_nrframework/css/conditionbuilder.css @@ -0,0 +1,2 @@ +.cb{-webkit-box-sizing:border-box;box-sizing:border-box}.cb *{-webkit-box-sizing:border-box;box-sizing:border-box}.cb.disabled{pointer-events:none}.cb:not(.init-load-done)>.actions{display:none}.cb .tf-conditionbuilder-initial-message{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:8px}.cb .alert p:last-of-type{margin-bottom:0}.cb input[type=text],.cb input[type=number]{height:auto}.cb .select2-container{width:100% !important}.cb .hasPopover:before{display:none !important}.cb small.form-text{display:none}.cb .actions{margin:25px 0 0 -10px}.cb .ruleValueHint{font-size:.9em;color:#aaa}.cb .control-group>.ruleValueHint{margin-left:190px}.cb .btn-group{font-size:inherit;height:auto}.cb .form-select{background-color:#fff;background-size:90rem}.cb .form-select.form-select-small{padding:.3rem 2rem .3rem .7rem;background-size:92rem}.cb .chosen-container{width:100% !important}.cb .chosen-container .chosen-results{margin:0;padding:0}.cb .chosen-container .chosen-results li{padding:8px 15px;-webkit-transition:all 50ms ease;transition:all 50ms ease}.cb .chosen-container .chosen-results li.disabled-result.group-option{margin-left:20px !important;margin-right:20px !important;border-bottom:1px solid #dedede;padding:5px 0 5px 0 !important}.cb .chosen-container .chosen-results .highlighted{background-image:none !important}.cb .chosen-container .chosen-drop{border:solid 1px var(--template-bg-dark-20, #aaa) !important}.cb .chosen-container.chosen-container-single{border:0;padding:0;background-color:rgba(0,0,0,0);border-radius:0}.cb .chosen-container.chosen-container-single .chosen-search{margin:3px}.cb .chosen-container.chosen-container-single .group-result:empty{display:none !important}.cb .chosen-container.chosen-container-single .chosen-drop{width:350px !important}.cb .chosen-container.chosen-container-single .active-result{padding-left:20px}.cb .chosen-container.chosen-container-single .active-result.result-selected{pointer-events:none;color:#ccc;background:none;cursor:default}.cb .chosen-container.chosen-container-single a.chosen-single{font-size:1.1em;display:block;height:auto;color:var(--template-link-color, #2a69b8);background:url("../../../administrator/templates/atum/images/select-bg.svg") no-repeat right center/90rem;padding:5px 28px 5px 10px;margin-left:-10px}.cb .chosen-container.chosen-container-single a.chosen-single span{margin-right:0}.cb .chosen-container.chosen-container-multi .chosen-drop{left:-1px;width:calc(100% + 2px);margin-top:-1px}.cb .chosen-container.chosen-container-multi .chosen-choices{position:relative;left:-10px;border:0;padding:0;background:rgba(0,0,0,0);-webkit-box-shadow:none;box-shadow:none;margin-top:-5px;margin-bottom:-5px}.cb .chosen-container.chosen-container-multi .chosen-choices .search-field{padding:3px;margin-left:6px}.cb .chosen-container.chosen-container-multi .chosen-choices .search-field input{height:26px !important}.cb .chosen-container.chosen-container-multi .chosen-choices .search-choice{border:none;-webkit-box-shadow:none;box-shadow:none;background:#ededed;line-height:1;padding:6px 20px 6px 6px}.cb .chosen-container.chosen-container-multi .chosen-choices .search-choice .search-choice-close{top:8px;right:4px}.cb input:focus,.cb input:active,.cb select:focus,.cb select:active,.cb textarea:focus,.cb textarea:active{outline:none}.cb-group{background-color:#f9f9f9;border:solid 1px #ddd;border-radius:3px;padding:20px;-webkit-box-shadow:1px 1px 2px 1px rgba(0,0,0,.1);box-shadow:1px 1px 2px 1px rgba(0,0,0,.1)}.cb-group:not(:last-child){position:relative;margin-bottom:80px}.cb-group:not(:last-child):before,.cb-group:not(:last-child):after{content:"";height:80px;position:absolute;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:.9em;top:100%;left:50%;right:0}.cb-group:not(:last-child):before{border-left:dashed 2px #ddd}.cb-group:not(:last-child):after{content:"OR";height:20px;margin-top:30px;background:#fff;-webkit-transform:translateX(-50%);transform:translateX(-50%);color:#a9a9a9}.cb-group .cb-item-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:30px}.cb-group .cb-item-toolbar .chosen-single{background:rgba(0,0,0,0);border:none;-webkit-box-shadow:none;box-shadow:none;margin:0;padding:0;border-radius:0}.cb-group .cb-item-toolbar .chosen-single>div{display:none}.cb-group .cb-item-toolbar.group{margin-bottom:15px}.cb-group .toggle-status{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-left:6px}.cb-group:hover>.cb-item-toolbar .cb-item-buttons .links{opacity:1}.cb-group:first-child:last-child .removeGroupCondition,.cb-group .cb-items .cb-item:first-child:last-child .tf-cb-remove-condition{display:none !important;opacity:0}.cb-item{position:relative;border:solid 1px #ddd;background:#fff;margin-bottom:10px}.cb-item:first-of-type,.cb-item:first-of-type .cb-item-toolbar{border-radius:3px 3px 0 0}.cb-item:last-of-type,.cb-item:last-of-type .cb-item-toolbar{border-radius:0 0 3px 3px}.cb-item:hover .cb-item-buttons .links{opacity:1}.cb-item.ajax-loading .cb-button{opacity:.2;pointer-events:none}.cb-item.ajax-loading .cb-item-buttons .loading{display:inline-block}.cb-button{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:4px;text-align:center;text-decoration:none;color:#888}.cb-button:hover,.cb-button:focus{color:#333;text-decoration:none;background:#ededed}.cb-button.outline{opacity:.8;color:#333;background:#fff;border-radius:3px;border:1px solid #bbb;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.cb-button.outline span{vertical-align:middle;margin:0}.cb-button.outline:hover{opacity:1}.cb-button span{margin:0}.cb-button.ajax-loading,.cb-button.disabled{opacity:.2;pointer-events:none}.cb-button.only-icon{width:30px;height:30px}.cb-button.icon span.icon{margin-right:5px}.cb-button.remove:hover,.cb-button.remove:focus{color:red;background:rgba(255,0,0,.1)}.cb-item-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-transition:all 100ms linear;transition:all 100ms linear}.cb-item-toolbar>.show{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.cb-item-toolbar>.show select{margin:0 10px;width:auto}.cb-item-toolbar:not(.group){padding:20px 20px 15px 20px}.cb-item-toolbar .cb-dropdown .controls,.cb-item-toolbar .cb-dropdown .control-group{margin:0;min-width:auto}.cb-item-toolbar .cb-dropdown ul li.disabled-result.group-option.is-pro{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:0;color:#999;cursor:pointer;padding:5px 20px !important;margin:0 !important}.cb-item-toolbar .cb-dropdown ul li.disabled-result.group-option.is-pro svg{width:20px;color:currentColor}.cb-item-toolbar .cb-dropdown ul li.disabled-result.group-option.is-pro:hover{color:#333;background:#f5f5f5}.cb-item-toolbar .cb-dropdown ul li.disabled-result.group-option.is-pro:not(:hover) .locks svg:nth-child(2),.cb-item-toolbar .cb-dropdown ul li.disabled-result.group-option.is-pro:hover .locks svg:nth-child(1){display:none}.cb-item-toolbar .cb-dropdown ul li.disabled-result.group-option.is-pro .locks{margin-left:auto}.cb-item-toolbar .cb-item-buttons{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.cb-item-toolbar .cb-item-buttons .loading{display:none;margin-right:5px}.cb-item-toolbar .cb-item-buttons .links{opacity:0}.cb-item-toolbar .cb-item-buttons .links>a{text-decoration:none}.cb-item-content{padding:0 20px 20px 20px}.cb-item-content .select-condition-message{text-align:center;color:#888;padding:30px 0}.cb-item-content>.control-group{margin-bottom:10px;display:-webkit-box;display:-ms-flexbox;display:flex}.cb-item-content>.control-group .nrtoggle{margin-top:4px}.cb-item-content>.control-group.hidden{display:none}.cb-item-content>.control-group .control-label{width:190px;-ms-flex-negative:0;flex-shrink:0}.cb-item-content>.control-group .control-label label{font-size:.95em}.cb-item-content>.control-group .control-label label .star{display:none}.cb-item-content>.control-group .controls{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin-left:0}.cb-item-content>.control-group:last-child{margin-bottom:0}.cb-item-content .controls .field-user-wrapper>.input-append{display:-webkit-box;display:-ms-flexbox;display:flex}.actions .tf-cb-add-new-group{padding:10px}.tf-cb-add-new-group{color:inherit}.tf-cb-add-new-group .loading{display:none;margin-left:5px}.tf-cb-add-new-group.ajax-loading .loading{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}@media only screen and (min-width: 780px){.select2-container .select2-dropdown{width:500px !important}} + diff --git a/media/plg_system_nrframework/css/controls/choiceselector.css b/media/plg_system_nrframework/css/controls/choiceselector.css new file mode 100644 index 00000000..a64b6b3d --- /dev/null +++ b/media/plg_system_nrframework/css/controls/choiceselector.css @@ -0,0 +1,2 @@ +.tf-choiceselector-control{display:grid;gap:var(--gap, 10px);grid-template-columns:repeat(var(--columns, 3), 1fr)}.tf-choiceselector-control.max-width-700{max-width:700px}@media only screen and (max-width: 640px){.tf-choiceselector-control{--columns: 1}}@media only screen and (max-width: 991px){.tf-choiceselector-control{--columns: 2}}@media only screen and (max-width: 1190px){.tf-choiceselector-control{--columns: 3}}.tf-choiceselector-control *{-webkit-box-sizing:border-box;box-sizing:border-box}.tf-choiceselector-control--item{border-radius:3px;border:1px solid #e6e6e6;position:relative}.tf-choiceselector-control--item:not(.pro) input:checked+label{border-color:rgba(0,0,0,0);background:#3071a9;color:#fff;border-radius:3px}.tf-choiceselector-control--item:not(.pro) input:not(:checked)+label .text{opacity:.75}.tf-choiceselector-control--item label{width:100%;margin:0;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center;font-size:13px;padding:.75em;min-height:80px;background:rgba(0,0,0,0);cursor:pointer}.tf-choiceselector-control--item input{display:none !important;margin:0}.tf-choiceselector-control--item .pro{height:20px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;position:absolute;top:3px;right:3px;font-size:10px;line-height:19px;background:#bd362f;color:#fff;border-radius:8px;padding:2px 8px;text-transform:uppercase;-webkit-font-smoothing:subpixel-antialiased}.tf-choiceselector-control--item .bottom{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:13px}.tf-choiceselector-control.mode-svg .tf-choiceselector-control--item svg{margin-bottom:5px}.tf-choiceselector-control.mode-svg .tf-choiceselector-control--item input:checked+label svg *[fill]{fill:#fff}.tf-choiceselector-control.mode-svg .tf-choiceselector-control--item input:checked+label svg *[stroke]:not([stroke=white]){stroke:#fff}.tf-choiceselector-control.mode-svg .tf-choiceselector-control--item input:checked+label svg *[stroke][stroke=white]{stroke:#3071a9} + diff --git a/media/plg_system_nrframework/css/controls/dimension.css b/media/plg_system_nrframework/css/controls/dimension.css new file mode 100644 index 00000000..86368a2c --- /dev/null +++ b/media/plg_system_nrframework/css/controls/dimension.css @@ -0,0 +1,2 @@ +.tf-dimension-control{--input-link-button-width: 42px;--label-color: #444;--gap: 6px;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:15px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.tf-dimension-control .tf-dimension-controls{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;width:100%;gap:var(--gap)}.tf-dimension-control .tf-dimension-controls--item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex:1;flex:1;position:relative;width:25%;gap:1px;cursor:text}.tf-dimension-control .tf-dimension-controls--item--value{-moz-appearance:textfield;-webkit-appearance:textfield;appearance:textfield;padding:0 !important;border:0 !important;background:rgba(0,0,0,0) !important;outline:none !important;margin:0 !important}.tf-dimension-control .tf-dimension-controls--item--value::-webkit-outer-spin-button,.tf-dimension-control .tf-dimension-controls--item--value::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.tf-dimension-control .tf-dimension-controls--item:not(.has-value) .tf-unit-control-dropdown{display:none}.tf-dimension-control .tf-dimension-controls--item.has-focus{color:#212529;background-color:#fff;border-color:var(--focus);outline:0;-webkit-box-shadow:0 0 0 .2rem var(--focus-shadow);box-shadow:0 0 0 .2rem var(--focus-shadow)}.tf-dimension-control--locks{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tf-dimension-control--locks>svg{cursor:pointer;color:#444}.tf-dimension-control--locks>svg:not(.active){display:none}.tf-dimension-control--locks>svg:not(:focus):not(.active):hover{color:#222}.tf-dimension-control--locks>svg:focus{color:#222}.tf-dimension-control--locks>svg.active{color:#000} + diff --git a/media/plg_system_nrframework/css/controls/editor.css b/media/plg_system_nrframework/css/controls/editor.css new file mode 100644 index 00000000..f289c78c --- /dev/null +++ b/media/plg_system_nrframework/css/controls/editor.css @@ -0,0 +1,2 @@ +.tf-editor-wrapper .tox-statusbar{display:none !important} + diff --git a/media/plg_system_nrframework/css/controls/imagedimensions.css b/media/plg_system_nrframework/css/controls/imagedimensions.css new file mode 100644 index 00000000..78050cd8 --- /dev/null +++ b/media/plg_system_nrframework/css/controls/imagedimensions.css @@ -0,0 +1,2 @@ +.tf-imagedimensions-control{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px}.tf-imagedimensions-control .control-group{margin:0 !important;-webkit-box-flex:1;-ms-flex:1 1 21%;flex:1 1 21%}.tf-imagedimensions-control .control-group:has(.separator-label){width:auto;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:0;-ms-flex:0;flex:0}.tf-imagedimensions-control .control-group .controls{min-width:auto}.tf-imagedimensions-control .control-group .controls select{min-width:120px}.tf-imagedimensions-control .control-group .controls .input-group input{-moz-appearance:textfield;border-radius:4px !important;padding-right:27px !important}.tf-imagedimensions-control .control-group .controls .input-group inputinput::-webkit-outer-spin-button,.tf-imagedimensions-control .control-group .controls .input-group inputinput::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.tf-imagedimensions-control .control-group .controls .input-group .add-on{position:absolute;right:9px;top:0;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;z-index:10}.tf-imagedimensions-control .control-group .controls .input-group .add-on>.input-group-text{background:rgba(0,0,0,0);padding:0;border:0;color:#333} + diff --git a/media/plg_system_nrframework/css/controls/phone.css b/media/plg_system_nrframework/css/controls/phone.css new file mode 100644 index 00000000..e14233e3 --- /dev/null +++ b/media/plg_system_nrframework/css/controls/phone.css @@ -0,0 +1,2 @@ +.tf-phone-control{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;gap:3px}.tf-phone-control[readonly]{pointer-events:none}.tf-phone-control--flag{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0;border:0;-webkit-box-shadow:none !important;box-shadow:none !important;padding:var(--input-padding);padding-top:0;padding-right:0;padding-bottom:0;line-height:1;-ms-flex-negative:0;flex-shrink:0;height:100%}.tf-phone-control--flag--selector{position:absolute;left:0;top:0;z-index:-1;opacity:0;width:0;height:0}.tf-phone-control--flag::after{display:none}.tf-phone-control--flag .tf-arrow{color:var(--input-color, #333);margin:0 6px 0 2px}.tf-phone-control--flag.is-open .tf-arrow{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.tf-phone-control--flag .tf-phonecontrol-label{max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.1}.tf-phone-control--flag>.choices__inner{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;background:rgba(0,0,0,0) !important;border:0;padding:0 !important;min-height:auto;border-radius:0}.tf-phone-control--flag>.choices__inner>.choices__list{padding:0}.tf-phone-control--flag>.choices__list{width:300px;z-index:9999;border-radius:6px}.tf-phone-control--flag>.choices__list>.choices__input{padding:16px;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:none;box-shadow:none}.tf-phone-control--flag>.choices__list>.choices__input:hover,.tf-phone-control--flag>.choices__list>.choices__input:active,.tf-phone-control--flag>.choices__list>.choices__input:focus{border-top-color:rgba(0,0,0,0);border-left-color:rgba(0,0,0,0);border-right-color:rgba(0,0,0,0)}.tf-phone-control--flag>.choices__list .choices__item{gap:10px;padding:13px 16px !important;color:#333}.tf-phone-control--flag>.choices__list .choices__item img{width:27px;height:auto;display:block;min-height:19px}.tf-phone-control--flag>.choices__list .choices__item .tf-phonecontrol-code{margin-left:auto;color:#999}.tf-phone-control--flag .choices__item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-right:0 !important}.tf-phone-control--flag .choices__item.tf-selectjs-selected-item{background:#e9e9e9;pointer-events:none}.tf-phone-control--flag.is-open .choices__list--dropdown,.tf-phone-control--flag.is-open .choices__list[aria-expanded]{border-color:#dedede}.tf-phone-control>.choices{margin:0 !important}.tf-phone-control--number[type=tel]{--input-border-color: transparent;--input-background-color: transparent;-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important;color:var(--input-color, #222);border:0 !important;background-color:rgba(0,0,0,0);padding:0;-webkit-transition:none;transition:none;margin:0;font-weight:normal;-webkit-box-shadow:none;box-shadow:none}.tf-phone-control--number[type=tel]:focus,.tf-phone-control--number[type=tel]:active{border:0 !important;outline:none;-webkit-box-shadow:none;box-shadow:none}.tf-phone-control--number[type=tel]::-webkit-input-placeholder{color:currentColor}.tf-phone-control--number[type=tel]::-moz-placeholder{color:currentColor}.tf-phone-control--number[type=tel]::-ms-placeholder{color:currentColor}.tf-phone-control--number[type=tel]::-webkit-input-placeholder{color:currentColor}.tf-phone-control--number[type=tel]::-moz-placeholder{color:currentColor}.tf-phone-control--number[type=tel]:-ms-input-placeholder{color:currentColor}.tf-phone-control--number[type=tel]::-ms-input-placeholder{color:currentColor}.tf-phone-control--number[type=tel]::placeholder{color:currentColor} + diff --git a/media/plg_system_nrframework/css/controls/responsive_control.css b/media/plg_system_nrframework/css/controls/responsive_control.css new file mode 100644 index 00000000..a0482c8d --- /dev/null +++ b/media/plg_system_nrframework/css/controls/responsive_control.css @@ -0,0 +1,2 @@ +.nr-responsive-control{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;position:relative;padding-top:var(--tf-rc-padding-top, 26px);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nr-responsive-control.minus-margin-top{margin-top:var(--tf-rc-minus-margin-top, -24px)}.nr-responsive-control.compact{--tf-rc-padding-top: 0}.nr-responsive-control.compact .tf-unit-control{cursor:text}.nr-responsive-control.is-one-line{--tf-rc-padding-top: 0}.nr-responsive-control.is-one-line .nr-responsive-control--item{gap:15px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.nr-responsive-control.is-one-line .nr-responsive-control--item .nr-responsive-control--item--top{display:-webkit-box;display:-ms-flexbox;display:flex;--top-position: relative;--top-position-top: auto;--top-position-right: auto;-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.nr-responsive-control--item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;gap:8px}.nr-responsive-control--item .control-group .controls{margin:0}.nr-responsive-control--item--top{display:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;gap:10px;position:var(--top-position, absolute);top:var(--top-position-top, 0);right:var(--top-position-right, 0);height:18px}.nr-responsive-control--item--top--breakpoint--switcher{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;position:relative}.nr-responsive-control--item--top--breakpoint--switcher--toggle{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:3px;cursor:pointer}.nr-responsive-control--item--top--breakpoint--switcher--toggle>svg:first-of-type{width:17px}.nr-responsive-control--item--top--breakpoint--switcher--dropdown{position:absolute;right:0;top:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.2);border:solid 1px #e6e6e6;padding:10px;border-radius:5px;background:#fff;z-index:15;width:267px;gap:1px}.nr-responsive-control--item--top--breakpoint--switcher--dropdown--item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;border-radius:5px;gap:15px;padding:15px 20px;cursor:pointer}.nr-responsive-control--item--top--breakpoint--switcher--dropdown--item:hover,.nr-responsive-control--item--top--breakpoint--switcher--dropdown--item.is-active{background:#f6f6f6}.nr-responsive-control--item--top--breakpoint--switcher--dropdown--item--text--desc{font-size:14px;color:#878787}.nr-responsive-control--item--top--breakpoint--switcher--dropdown:not(.is-open){display:none}.control-group.tf-responsive-control-top-visible .nr-responsive-control .nr-responsive-control--item .nr-responsive-control--item--top{display:-webkit-box;display:-ms-flexbox;display:flex}.nr-responsive-control-group:not(.nr-responsive-control-group--active){display:none !important}joomla-tab-element[id=appearance] .control-group:not(:has(joomla-field-media))>.control-label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center} + diff --git a/media/plg_system_nrframework/css/controls/unit.css b/media/plg_system_nrframework/css/controls/unit.css new file mode 100644 index 00000000..9c247253 --- /dev/null +++ b/media/plg_system_nrframework/css/controls/unit.css @@ -0,0 +1,2 @@ +.tf-unit-control{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:1px;cursor:text}.tf-unit-control--value{-moz-appearance:textfield;-webkit-appearance:textfield;appearance:textfield;min-width:.5px !important;padding:0 !important;border:0 !important;background:rgba(0,0,0,0) !important;outline:none !important;margin:0 !important;-webkit-transition:none !important;transition:none !important}.tf-unit-control--value::-webkit-outer-spin-button,.tf-unit-control--value::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.tf-unit-control:not(.has-value) .tf-unit-control-dropdown{display:none}.tf-unit-control.has-focus:not(.is-readonly){color:#212529;background-color:#fff;border-color:var(--focus);outline:0;-webkit-box-shadow:0 0 0 .2rem var(--focus-shadow);box-shadow:0 0 0 .2rem var(--focus-shadow)}.tf-unit-control.has-multiple-units .tf-unit-control-dropdown--opener{cursor:pointer}.tf-unit-control.has-multiple-units .tf-unit-control-dropdown--opener:hover{color:#333} + diff --git a/media/plg_system_nrframework/css/controls/unit_selector.css b/media/plg_system_nrframework/css/controls/unit_selector.css new file mode 100644 index 00000000..884775ad --- /dev/null +++ b/media/plg_system_nrframework/css/controls/unit_selector.css @@ -0,0 +1,2 @@ +.tf-unit-control-dropdown{--font-size: 16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;font-size:var(--font-size);position:relative}.tf-unit-control-dropdown--opener{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;gap:3px;height:100%}.tf-unit-control-dropdown.has-multiple-units .tf-unit-control-dropdown--opener{cursor:pointer}.tf-unit-control-dropdown.is-open .tf-unit-control-dropdown--opener svg{opacity:1;visibility:visible;z-index:10;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.tf-unit-control-dropdown--drop{position:absolute;left:0;top:100%;margin:0;list-style-type:none;background:#fff;width:100px;z-index:50;color:#333;-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.2);border:solid 1px #e6e6e6;padding:10px;border-radius:5px;background:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;gap:1px}.tf-unit-control-dropdown--drop>li{padding:7.5px 10px;cursor:pointer;font-size:var(--font-size);border-radius:5px;text-align:center}.tf-unit-control-dropdown--drop>li:hover,.tf-unit-control-dropdown--drop>li.selected{background:#f6f6f6}.tf-unit-control-dropdown:not(.is-open) .tf-unit-control-dropdown--drop{display:none}.tf-unit-control-dropdown:not(.is-open) .tf-unit-control-dropdown--opener:not(:hover) svg{opacity:0;visibility:hidden;z-index:-1} + diff --git a/media/plg_system_nrframework/css/controls/widthheight.css b/media/plg_system_nrframework/css/controls/widthheight.css new file mode 100644 index 00000000..05990515 --- /dev/null +++ b/media/plg_system_nrframework/css/controls/widthheight.css @@ -0,0 +1,2 @@ +.tf-widthheight-control{display:-webkit-box;display:-ms-flexbox;display:flex;gap:10px}.tf-widthheight-control>.control-group{margin:0 !important;-webkit-box-flex:1;-ms-flex:1 1 21%;flex:1 1 21%}.tf-widthheight-control>.control-group:not(:has(.separator-label)){width:270px}.tf-widthheight-control>.control-group:has(.separator-label){width:auto;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:0;-ms-flex:0;flex:0}.tf-widthheight-control>.control-group .input-append.input-group input{-moz-appearance:textfield;border-radius:.25rem !important;padding-right:45px !important}.tf-widthheight-control>.control-group .input-append.input-group input::-webkit-outer-spin-button,.tf-widthheight-control>.control-group .input-append.input-group input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.tf-widthheight-control>.control-group .input-append.input-group .add-on.input-group-append{position:absolute;right:0;top:0;z-index:10}.tf-widthheight-control>.control-group .input-append.input-group .add-on.input-group-append>.input-group-text{font-size:inherit;background:rgba(0,0,0,0);border:0;color:inherit} + diff --git a/media/plg_system_nrframework/css/fields.css b/media/plg_system_nrframework/css/fields.css new file mode 100644 index 00000000..76b56ee7 --- /dev/null +++ b/media/plg_system_nrframework/css/fields.css @@ -0,0 +1,2 @@ +.nr-well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f0f0f0;border:1px solid #f0f0f0;border-radius:3px;position:relative}.nr-well .well-desc{margin-bottom:25px;color:#555}.nr-well h4{font-size:16px;margin-top:0}.nr-well>.control-group:last-child{margin-bottom:0}.nr-well .controls:empty,.nr-well .control-label:empty,.nr-well .control-group:empty{display:none}.nr-well .wellbtn{position:absolute;top:0;right:0;margin:19px}.nr-well .wellbtn span{margin:0}.well-note{font-size:11px;line-height:1.5;opacity:.7}.input-xmini{width:30px}.input-fullwidth{width:100% !important}.input-flex{display:-webkit-box;display:-ms-flexbox;display:flex;margin:0 -5px}.input-flex .input-flex-item{padding:0 5px}.nr-hide-control-group-label>.control-label{display:none}.nr-hide-control-group-label>.controls{margin-left:0 !important} + diff --git a/media/plg_system_nrframework/css/global_devices_selector.css b/media/plg_system_nrframework/css/global_devices_selector.css new file mode 100644 index 00000000..1d719e79 --- /dev/null +++ b/media/plg_system_nrframework/css/global_devices_selector.css @@ -0,0 +1,2 @@ +.tf-global-devices-selector{text-align:center}.tf-global-devices-selector--items{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;background:#f0f0f0;padding:3px;border-radius:20px}.tf-global-devices-selector--items--item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;gap:4px;cursor:pointer;color:#555;padding:6px 0}.tf-global-devices-selector--items--item:not(.is-active):first-of-type{padding-left:8px;padding-right:8px}.tf-global-devices-selector--items--item:not(.is-active):first-of-type~.tf-global-devices-selector--items--item:not(.is-active):last-of-type{padding-left:8px;padding-right:8px}.tf-global-devices-selector--items--item:not(.is-active):nth-of-type(2){padding-left:0;padding-right:8px}.tf-global-devices-selector--items--item.is-active:first-of-type~.tf-global-devices-selector--items--item{padding-right:8px}.tf-global-devices-selector--items--item.is-active:first-of-type~.tf-global-devices-selector--items--item:not(:last-of-type){padding-left:8px}.tf-global-devices-selector--items--item svg{width:20px;color:currentColor}.tf-global-devices-selector--items--item:hover{color:#333}.tf-global-devices-selector--items--item--label{display:none}.tf-global-devices-selector--items--item.is-active{padding-left:8px;padding-right:8px;background:#fff;color:#000;border-radius:17px}.tf-global-devices-selector--items--item.is-active .tf-global-devices-selector--items--item--label{display:inline-block} + diff --git a/media/plg_system_nrframework/css/images-selector-field.css b/media/plg_system_nrframework/css/images-selector-field.css new file mode 100644 index 00000000..7dad8a3e --- /dev/null +++ b/media/plg_system_nrframework/css/images-selector-field.css @@ -0,0 +1,2 @@ +.nr-images-selector{--border-color: #ccc;--border-hover-color: #b1b1b1;--selected-color: #46A546;--item-padding: 10px;display:grid;text-align:center;gap:var(--gap, 10px);max-width:var(--max-width);grid-template-columns:repeat(var(--columns, 3), 1fr)}.nr-images-selector.no-padding .image label{--item-padding: 0}@media only screen and (max-width: 1190px){.nr-images-selector{--columns: 3 !important}}@media only screen and (max-width: 991px){.nr-images-selector{--columns: 2 !important}}@media only screen and (max-width: 640px){.nr-images-selector{--columns: 1 !important}}.nr-images-selector .nr-images-selector-item{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;height:var(--item-height)}.nr-images-selector .nr-images-selector-item label{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:var(--item-padding);cursor:pointer;border:1px solid var(--border-color);border-radius:3px;max-width:var(--item-width);gap:var(--item-gap);text-align:center}.nr-images-selector .nr-images-selector-item label:hover{border-color:var(--border-hover-color)}.nr-images-selector .nr-images-selector-item label .checkmark{position:absolute;top:-12px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);z-index:5;background:#fff;border-radius:50%}.nr-images-selector .nr-images-selector-item label img{-o-object-fit:contain;object-fit:contain;max-width:100%;image-rendering:crisp-edges;-ms-interpolation-mode:nearest-neighbor;width:var(--image-width)}.nr-images-selector .nr-images-selector-item label .item-label{color:#999}.nr-images-selector .nr-images-selector-item.is-pro .is-pro-overlay{position:absolute;width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;background:rgba(255,255,255,.4);color:#c52827;cursor:pointer;z-index:15}.nr-images-selector .nr-images-selector-item.is-pro .is-pro-overlay span{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:40px;height:40px;background:currentColor;border:1px solid currentColor;border-radius:4px;font-size:16px}.nr-images-selector .nr-images-selector-item.is-pro .is-pro-overlay span svg{color:#fff}.nr-images-selector .nr-images-selector-item.is-pro .is-pro-overlay:not(:hover) span .unlock-icon{display:none}.nr-images-selector .nr-images-selector-item.is-pro .is-pro-overlay:hover span .lock-icon{display:none}.nr-images-selector .nr-images-selector-item.is-pro .is-pro-overlay:hover{color:#a72221}.nr-images-selector .nr-images-selector-item input[type=radio]:not(:checked)+label .checkmark{display:none}.nr-images-selector .nr-images-selector-item input[type=radio]:checked+label{color:var(--selected-color);border-color:currentColor;-webkit-box-shadow:0 0 0 2px var(--selected-color);box-shadow:0 0 0 2px var(--selected-color)}.nr-images-selector .nr-images-selector-item input[type=radio]:checked+label .item-label{color:#333}.nr-images-selector .nr-images-selector-item input{display:none} + diff --git a/media/plg_system_nrframework/css/inline-control-group.css b/media/plg_system_nrframework/css/inline-control-group.css new file mode 100644 index 00000000..b64327b5 --- /dev/null +++ b/media/plg_system_nrframework/css/inline-control-group.css @@ -0,0 +1,2 @@ +.inline-control-group{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.inline-control-group .controls{margin-left:0 !important;line-height:1;min-width:auto}.inline-control-group .control-label{padding:0 10px 0 0 !important}.inline-control-group .control-label:empty{display:none}.inline-control-group fieldset{padding:0 !important}.inline-control-group .control-group{margin:0}.inline-control-group .control-group:not([class*=hidden]){-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.inline-control-group.tf-inline-fields.tf-align-items-start{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.inline-control-group.tf-inline-fields .tf-center-note>*{font-weight:normal;margin:0}@media only screen and (min-width: 1070px){.inline-control-group.tf-inline-fields{-ms-flex-wrap:nowrap;flex-wrap:nowrap}}.inline-control-group.tf-inline-fields .control-group:nth-child(1){-ms-flex-negative:0;flex-shrink:0}.inline-control-group.tf-inline-fields .control-group:nth-child(2):not(:has(.tf-center-note)),.inline-control-group.tf-inline-fields .control-group:nth-child(2):has(.tf-center-note)+.control-group,.inline-control-group.tf-inline-fields .control-group:has(.field-calendar){-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.inline-control-group.timetable{margin:-7px -5px}.inline-control-group.timetable input[type=checkbox],.inline-control-group.timetable label,.inline-control-group.timetable .form-check{margin:0;padding:0;position:static}.inline-control-group.timetable .control-group:nth-child(2) .control-label{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1;padding-left:7px !important;width:90px} + diff --git a/media/plg_system_nrframework/css/inlinefileupload.css b/media/plg_system_nrframework/css/inlinefileupload.css new file mode 100644 index 00000000..80ab9e11 --- /dev/null +++ b/media/plg_system_nrframework/css/inlinefileupload.css @@ -0,0 +1,2 @@ +.nr-inline-file-upload{position:relative;display:inline-block;word-break:break-word;width:300px;min-height:20px}.nr-inline-file-upload .upload-area{position:relative;margin-bottom:15px}.nr-inline-file-upload .upload-area.hidden{display:none}.nr-inline-file-upload .upload-area .file-selector-opener{background:rgba(0,0,0,0);border:1px solid #dedede;border-radius:5px;text-align:center}.nr-inline-file-upload .upload-area .file-selector-opener:hover{background:#f5f5f5}.nr-inline-file-upload .upload-area .file-selector-opener:active{background:#ededed}.nr-inline-file-upload .upload-area input[type=file]{display:none;position:fixed;left:-100vw}.nr-inline-file-upload .error{display:none;border:1px solid #bd362f;padding:10px;color:#bd362f}.nr-inline-file-upload .error.visible{display:block}.nr-inline-file-upload .error .actions{margin-top:10px}.nr-inline-file-upload .error .actions button{background:rgba(0,0,0,0);border:1px solid #dedede;border-radius:5px;text-align:center}.nr-inline-file-upload .error .actions button:hover{background:#f5f5f5}.nr-inline-file-upload .error .actions button:active{background:#ededed}.nr-inline-file-upload .loader{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;position:absolute;left:0;top:0;width:100%;padding:10px 5px;border-radius:5px;background:#f5f5f5;z-index:-1;opacity:0;visibility:hidden}.nr-inline-file-upload.loading{cursor:not-allowed;pointer-events:none}.nr-inline-file-upload.loading .loader{opacity:1;visibility:visible;z-index:50}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item .icon{margin:0}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item .file-name{margin:0 5px}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item .size{color:#999;min-width:60px}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item .remove{position:absolute;right:0;top:0;text-decoration:none;color:#999;opacity:0;font-size:20px;background:#fff}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item .remove:hover{color:#333}.nr-inline-file-upload .uploaded-files:not(:empty) .nr-inline-file-upload-item:hover .remove{opacity:1} + diff --git a/media/plg_system_nrframework/css/joomla3.css b/media/plg_system_nrframework/css/joomla3.css new file mode 100644 index 00000000..b8e548e2 --- /dev/null +++ b/media/plg_system_nrframework/css/joomla3.css @@ -0,0 +1,2 @@ +.assign .control-group .control-label{max-width:180px !important}.minicolors-theme-bootstrap .minicolors-input{font-family:inherit !important;width:182px !important}.cb{margin-left:-180px}.cb .chzn-container{width:100% !important}.cb .chzn-container .chzn-results{margin:0;padding:0}.cb .chzn-container .chzn-results li{padding:8px 15px;-webkit-transition:all 50ms ease;transition:all 50ms ease}.cb .chzn-container .chzn-results li.disabled-result.group-option{margin-left:20px !important;margin-right:20px !important;border-bottom:1px solid #dedede;padding:5px 0 5px 0 !important}.cb .chzn-container .chzn-results .highlighted{background-image:none !important}.cb .chzn-container .chzn-drop{border:solid 1px var(--template-bg-dark-20, #aaa) !important;z-index:50 !important}.cb .chzn-container.chzn-container-single{border:0;padding:0;background-color:rgba(0,0,0,0);background-position-x:0;border-radius:0}.cb .chzn-container.chzn-container-single .chzn-search{margin:3px}.cb .chzn-container.chzn-container-single .group-result:empty{display:none !important}.cb .chzn-container.chzn-container-single .chzn-drop{width:350px !important;margin-top:5px !important}.cb .chzn-container.chzn-container-single .active-result{padding-left:20px}.cb .chzn-container.chzn-container-single .active-result.result-selected{pointer-events:none;color:#ccc;background:none;cursor:default}.cb .chzn-container.chzn-container-single a.chzn-single{border:none;font-size:1.1em;line-height:1em;display:block;height:auto;color:var(--template-link-color, #2a69b8);padding:5px 28px 5px 10px;margin-left:-10px;-webkit-box-shadow:none;box-shadow:none}.cb .chzn-container.chzn-container-single a.chzn-single div{background:none;border:none;width:35px;top:5px}.cb .chzn-container.chzn-container-single a.chzn-single span{margin-right:0}.cb .chzn-container.chosen-container-multi .chosen-drop{left:-1px;width:calc(100% + 2px);margin-top:-1px}.cb .chzn-container.chosen-container-multi .chosen-choices{position:relative;left:-10px;border:0;padding:0;background:rgba(0,0,0,0);-webkit-box-shadow:none;box-shadow:none;margin-top:-5px;margin-bottom:-5px}.cb .chzn-container.chosen-container-multi .chosen-choices .search-field{padding:3px;margin-left:6px}.cb .chzn-container.chosen-container-multi .chosen-choices .search-field input{height:26px !important}.cb .chzn-container.chosen-container-multi .chosen-choices .search-choice{border:none;-webkit-box-shadow:none;box-shadow:none;background:#ededed;line-height:1;padding:6px 20px 6px 6px}.cb .chzn-container.chosen-container-multi .chosen-choices .search-choice .search-choice-close{top:8px;right:4px}.cb .control-group>.ruleValueHint{margin-left:0 !important}.cb .cb-item-toolbar select{height:auto;padding:.4rem !important}.cb .cb-item-content>.control-group .nrtoggle{margin-top:0}.cb .cb-item-content input:not([class*=input]),.cb .cb-item-content select:not([class*=input]),.cb .cb-item-content textarea:not([class*=input]){width:100%;max-width:100%}.cb .cb-item-content .tf-ajaxify-wrapper li.select2-search,.cb .cb-item-content .tf-ajaxify-wrapper input{width:100% !important}.cb .cb-item-content>.control-group{margin-bottom:10px !important}.cb .cb-item-content>.control-group:last-child{margin-bottom:0 !important}.cb .group-remove span:before{content:"L" !important}.cb .cb-group:not(:last-child):before,.cb .cb-group:not(:last-child):after{z-index:-1}.input-group.input-append{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group.input-append input{border-radius:3px}.input-group.input-append .add-on.input-group-append{background:rgba(0,0,0,0);border:0;font-size:12px !important;color:#444;height:100%;padding:0;padding-left:8px}span.nr_treeselect-toggle{line-height:12px}.add-on{height:100% !important}.tf-gallery-actions .btn{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.tf-gallery-actions .btn[disabled]{pointer-events:none}.tf-gallery-actions .btn i{margin-right:5px}.tf-gallery-actions .btn-group .btn.dropdown-toggle{min-width:35px}.nrf-widget.tf-mapaddress-editor .tf-mapaddress-field-location-details input{width:100%;height:auto}.nrf-widget.tf-mapaddress-editor .tf-mapaddress-field-location-details .control-group.address svg{right:6px !important;top:2px !important}.nrf-widget.tf-mapaddress-editor:not(.is-required) .tf-mapaddress-map-wrapper.no-map:not(.clear-is-hidden){padding-top:30px}.nrf-widget.tf-mapaddress-editor .map-markers+.tf-mapeditor-settings{margin-top:18px}.nr-chained-fields .chzn-container{-webkit-box-flex:1;-ms-flex:1 0 300px;flex:1 0 300px}.nr-responsive-control .nr-responsive-control--item input,.nr-responsive-control .nr-responsive-control--item textarea{-webkit-box-sizing:border-box;box-sizing:border-box;height:auto}.nr-responsive-control .nr-responsive-control--item .tf-unit-control--value{line-height:1}.nr-responsive-control .nr-responsive-control--item .input-group{position:relative;-ms-flex-wrap:nowrap;flex-wrap:nowrap;width:100%}.nr-responsive-control .nr-responsive-control--item .minicolors{-ms-flex-item-align:start;align-self:flex-start}.nr-responsive-control .nr-responsive-control--item .minicolors>.minicolors-panel{left:0;top:100%}.tf-subform-hide-label>.subform-wrapper>.control-group>.controls{margin:0}.tf-subform-hide-label>.subform-wrapper>.control-group>.controls .subform-repeatable>.btn-toolbar .group-add{margin:0}.tf-subform-hide-label>.subform-wrapper>.control-group>.controls .subform-repeatable .subform-repeatable-group{margin-left:0}.tf-subform-hide-label>.subform-wrapper>.control-group>.controls .subform-repeatable .subform-repeatable-group>.btn-toolbar{right:0;top:1px}.tf-address-lookup-container svg{top:auto !important;right:9px !important;bottom:4px !important}.tf-bold{font-weight:bold}@media only screen and (min-width: 1000px){#tfMapEditorMarkerEditModal .modal-body>.control-group:nth-of-type(3){float:left}#tfMapEditorMarkerEditModal .modal-body>.control-group:nth-of-type(2) .controls{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}#tfMapEditorMarkerEditModal .modal-body>.control-group>.control-label{width:180px !important;min-width:180px !important;max-width:180px !important}}.tf-unit-control.form-control,.tf-dimension-controls--item.form-control{font-size:13px;height:18px;padding:4px 6px;line-height:18px;color:#555;border-radius:3px;background-color:#fff;border:1px solid #ccc;-webkit-transition:border linear .2s,-webkit-box-shadow linear .2s;transition:border linear .2s,-webkit-box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s,-webkit-box-shadow linear .2s}.tf-unit-control.form-control.has-focus,.tf-dimension-controls--item.form-control.has-focus{--focus: rgba(82,168,236,0.8);outline:0}.rstbox-item .tab-content>div#appearance>.control-group:first-of-type{position:sticky;max-width:100%;top:90px;z-index:100}.tf-unit-control-dropdown{--font-size: 14px !important}.tf-ajaxify-wrapper .select2-container--default .select2-selection--single .select2-selection__rendered{padding:4px 6px;line-height:20px}.tf-ecomm-range-extra-settings .control-group>.controls>.field-calendar>.input-append{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.tf-gallery-preview-item textarea.item-alt{width:100%}.tf-gallery-manager-edit-modal-content--form--item--content--input{-webkit-box-sizing:content-box;box-sizing:content-box}.tf-phone-control--number[type=tel]{line-height:1}.tf-imagedimensions-control{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}.tf-imagedimensions-control .control-group:has(.separator-label) .control-label{width:auto !important}.tf-imagedimensions-control>.control-group>.controls{margin:0}.tf-gallery-preview-item--alt--fields textarea{background:rgba(0,0,0,0) !important}.tf-gallery-preview-item--alt--fields textarea:focus{border:solid 10px #fff !important;border-left-width:15px !important;border-right-width:15px !important}.modal:has(.tf-missing-setting-value-modal){width:500px;margin-left:-250px}.modal:has(.tf-missing-setting-value-modal) .tf-missing-setting-value-modal{width:100%} + diff --git a/media/plg_system_nrframework/css/joomla4.css b/media/plg_system_nrframework/css/joomla4.css new file mode 100644 index 00000000..744b79c4 --- /dev/null +++ b/media/plg_system_nrframework/css/joomla4.css @@ -0,0 +1,2 @@ +.form-control::-webkit-input-placeholder{font-style:normal}.form-control::-moz-placeholder{font-style:normal}.form-control:-ms-input-placeholder{font-style:normal}.form-control::-ms-input-placeholder{font-style:normal}.form-control::placeholder{font-style:normal}select.input-small{max-width:95px}.boxColor{background:url(../../../media/vendor/minicolors/css/jquery.minicolors.png) -80px 0}a{text-decoration:none}a[target=_blank]::before{content:none}#proOnlyModal .btn-danger{background:#c52827;color:#fff;-webkit-box-shadow:none;box-shadow:none;margin:0}#proOnlyModal .pro-only-bonus:before{content:"" !important;font-family:"Font Awesome 5 Free" !important}a[data-pro-only] span{position:static !important}.modal a.btn-primary{color:var(--atum-text-light) !important;background-color:var(--atum-link-color) !important;border-color:var(--atum-link-color) !important}.form-control::-webkit-input-placeholder{opacity:.6}.form-control::-webkit-input-placeholder,.form-control::-moz-placeholder,.form-control::placeholder{opacity:.6}.cb .group-move span:before{content:""}.cb .geo-db-checker{font-size:14px}@media only screen and (max-width: 526px){.cb .geo-db-checker{margin-left:0}}.nrf-checkboxes-field-columns{display:grid}@media only screen and (min-width: 992px){.nrf-checkboxes-field-columns{grid-gap:10px;grid-template-columns:140px 140px 140px 140px}}.tf-bold{font-weight:bold}.modal .modal-footer .btn.tf-modal-btn-primary{color:#fff;background:#3d618f;border:0}.modal .modal-footer .btn.tf-modal-btn-primary:hover{background:#2e486b}.tf-unit-control select{width:auto}.minicolors>.minicolors-input{padding-left:36px}.minicolors>.minicolors-swatch{border-radius:50% !important;width:22px !important;height:22px !important;left:8px !important;top:10px !important}.minicolors>.minicolors-swatch::after{border-radius:50%}joomla-field-fancy-select .choices[data-type=select-one] .choices__item.is-highlighted{background:#f5f5f5}joomla-field-fancy-select .choices[data-type=select-one] .choices__item.is-selected{background:#eee}joomla-field-fancy-select .choices[data-type=select-one].is-open .choices__list--dropdown{-webkit-box-shadow:0 3px 0 .2rem #eaeaea;box-shadow:0 3px 0 .2rem #eaeaea;border-bottom-color:#39f;border-left-color:#39f;border-right-color:#39f}joomla-field-fancy-select .choices[data-type=select-one]>.choices__inner>.choices__list--single>.choices__item--selectable>button{display:none}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner{padding-left:8px}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner .choices__input{margin-left:5px}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner .choices__input::-webkit-input-placeholder{color:#999}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner .choices__input::-moz-placeholder{color:#999}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner .choices__input:-ms-input-placeholder{color:#999}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner .choices__input::-ms-input-placeholder{color:#999}joomla-field-fancy-select .choices[data-type=select-multiple]>.choices__inner .choices__input::placeholder{color:#999}joomla-field-fancy-select .choices__placeholder{opacity:1}joomla-field-fancy-select .choices__list--dropdown .choices__list:has(.choices__item.is-selected)>.choices__item:not(:hover).is-highlighted:not(.is-selected){background:rgba(0,0,0,0)}.select2-container .select2-search__field{min-height:31px} + diff --git a/media/plg_system_nrframework/css/modals/missing_value.css b/media/plg_system_nrframework/css/modals/missing_value.css new file mode 100644 index 00000000..0e58b29c --- /dev/null +++ b/media/plg_system_nrframework/css/modals/missing_value.css @@ -0,0 +1,2 @@ +.modal:has(.tf-missing-setting-value-modal) .modal-dialog{--modal-width: 500px}.tf-missing-setting-value-modal{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:30px;gap:20px;text-align:center;font-size:16px;line-height:25.6px}.tf-missing-setting-value-modal p{margin:0}.tf-missing-setting-value-modal a{margin:0;text-decoration:underline}.tf-missing-setting-value-modal a:before{display:none} + diff --git a/media/plg_system_nrframework/css/notices.css b/media/plg_system_nrframework/css/notices.css new file mode 100644 index 00000000..c74a5b4e --- /dev/null +++ b/media/plg_system_nrframework/css/notices.css @@ -0,0 +1,2 @@ +.tf-notices{--gap: 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;gap:5px}.tf-notice{--warning-color: #F4B400;--error-color: #DB4437;--info-color: #4285F4;--success-color: #0F9D58;--white: #fff;--color: transparent;--text-color: #333;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;line-height:1;gap:var(--gap);padding:calc(.8*var(--gap)) var(--gap);border:0;border-left:5px solid var(--color);font-size:15px;font-weight:400;background:var(--white);-webkit-box-shadow:0 2px 2px rgba(0,0,0,.05);box-shadow:0 2px 2px rgba(0,0,0,.05);color:var(--text-color);border-radius:0;font-family:Arial}.tf-notice:last-of-type{margin-bottom:var(--gap)}.tf-notice .orange-text{color:var(--warning-color)}.tf-notice .text-bold{font-weight:bold}.tf-notice.warning{--color: var(--warning-color)}.tf-notice.error{--color: var(--error-color)}.tf-notice.info{--color: var(--info-color)}.tf-notice.success{--color: var(--success-color)}.tf-notice .green-text{color:var(--success-color)}.tf-notice .tf-notice-btn{--tf-notice-btn-bg-color: #999;--tf-notice-btn-color: var(--white);--tf-notice-btn-border-color: transparent;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;gap:calc(.25*var(--gap));border-radius:5px;background:var(--tf-notice-btn-bg-color);color:var(--tf-notice-btn-color);border:1px solid var(--tf-notice-btn-border-color);padding:9.5px 22px;text-decoration:none;-webkit-box-sizing:border-box;box-sizing:border-box}.tf-notice .tf-notice-btn.success{--tf-notice-btn-bg-color: var(--success-color)}.tf-notice .tf-notice-btn.info{--tf-notice-btn-bg-color: var(--info-color)}.tf-notice .tf-notice-btn:not(.outline):hover{opacity:.8}.tf-notice .tf-notice-btn.outline{--tf-notice-btn-bg-color: transparent;--tf-notice-btn-border-color: #999;--tf-notice-btn-color: #666}.tf-notice .tf-notice-btn.outline:hover{--tf-notice-btn-bg-color: #999;--tf-notice-btn-color: var(--white)}.tf-notice .tf-notice-btn svg{display:none}.tf-notice .tf-notice-btn.loading{opacity:.5;pointer-events:none}.tf-notice .tf-notice-btn.loading svg{display:inline-block}.tf-notice>svg:first-child{min-width:38px}.tf-notice>svg.color{fill:var(--color)}.tf-notice svg:not(.color){fill:rgba(0,0,0,.3)}.tf-notice svg:not(.color):hover{fill:#000}.tf-notice .content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;gap:1px;padding:0;margin:0;margin-right:auto;line-height:24px}.tf-notice .content .title{font-size:1.2em;font-weight:700;line-height:23px}.tf-notice .content .description{color:#5c5c5c}.tf-notice .actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:var(--gap);white-space:nowrap}.tf-notice .actions a:before{display:none}.tf-notice .btn-close:not(button){position:relative;top:auto;right:auto;background:rgba(0,0,0,0);width:auto;height:auto;border-radius:0;border:0;padding:0}.tf-notice button.btn-close{position:absolute;background:rgba(0,0,0,0);padding:0;color:rgba(0,0,0,.3);opacity:1;top:var(--gap);right:var(--gap);width:auto;height:auto}.tf-notice button.btn-close:hover{opacity:1}.tf-notice .notice-tooltip-wrapper{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.tf-notice .notice-tooltip-wrapper .notice-tooltip{--notice-tooltip-top: 8px;z-index:-1;opacity:0;visibility:hidden;position:absolute;min-width:250px;right:-32px;top:calc(100% + var(--notice-tooltip-top))}.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-arrow{position:absolute;top:0;right:44px}.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-arrow:before,.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-arrow:after{bottom:100%;left:50%;border:solid rgba(0,0,0,0);content:"";height:0;width:0;position:absolute;pointer-events:none}.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-arrow:before{border-color:rgba(179,179,179,0);border-bottom-color:#cacaca;border-width:11px;margin-left:-11px}.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-arrow:after{border-color:rgba(255,255,255,0);border-bottom-color:#fff;border-width:10px;margin-left:-10px}.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-inner{padding:16px;color:#333;font-size:15px;background:#fff;border-radius:3px;-webkit-box-shadow:0 0 10px 2px rgba(0,0,0,.1);box-shadow:0 0 10px 2px rgba(0,0,0,.1);line-height:20px}.tf-notice .notice-tooltip-wrapper .notice-tooltip.leftPosition{right:auto;left:-25px}.tf-notice .notice-tooltip-wrapper .notice-tooltip.leftPosition .notice-tooltip-arrow{right:auto;left:37px}.tf-notice .notice-tooltip-wrapper .notice-tooltip.is-visible{--notice-tooltip-top: 16px;z-index:99999;opacity:1;visibility:visible}@media only screen and (min-width: 992px){.tf-notice .notice-tooltip-wrapper .notice-tooltip{width:500px}}@media only screen and (min-width: 552x)and (max-width: 991px){.tf-notice .notice-tooltip-wrapper .notice-tooltip{width:300px}}@media only screen and (max-width: 435px){.tf-notice .notice-tooltip-wrapper{width:100%}.tf-notice .notice-tooltip-wrapper .notice-tooltip{min-width:calc(100vw - 32px);left:0;right:auto;right:auto;left:-25px}.tf-notice .notice-tooltip-wrapper .notice-tooltip .notice-tooltip-arrow{right:auto;left:37px}}.tf-notice a:not(.tf-notice-btn){color:var(--text-color);text-decoration:underline;opacity:.8}.tf-notice a:not(.tf-notice-btn):hover{opacity:1}.tf-notice input[type=text]{padding:8px;height:auto;background:#f8f8f8;border:1px solid rgba(0,0,0,.1);border-radius:2px;margin:0;font-size:1em;-webkit-transition:none;transition:none}.tf-notice input[type=text]:hover{border-color:rgba(0,0,0,.25)}.tf-notice input[type=text]:focus{border-color:rgba(0,0,0,.5)}@media only screen and (max-width: 1200px){.tf-notice{-ms-flex-wrap:wrap;flex-wrap:wrap}.tf-notice .actions{-ms-flex-wrap:wrap;flex-wrap:wrap}}@media only screen and (min-width: 992px){.tf-notice .tf-notice-btn:not(.tf-notice-download-key-btn){width:150px}.tf-notice.alert-dismissible{padding-right:calc(3*var(--gap))}.tf-notice button.btn-close{top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}}@media only screen and (min-width: 576px){.tf-notice input[type=text].tf-notice-download-key{min-width:300px}} + diff --git a/media/plg_system_nrframework/css/photoswipe/default-skin/default-skin.css b/media/plg_system_nrframework/css/photoswipe/default-skin/default-skin.css new file mode 100644 index 00000000..c9616326 --- /dev/null +++ b/media/plg_system_nrframework/css/photoswipe/default-skin/default-skin.css @@ -0,0 +1,482 @@ +/*! PhotoSwipe Default UI CSS by Dmitry Semenov | photoswipe.com | MIT license */ +/* + + Contents: + + 1. Buttons + 2. Share modal and links + 3. Index indicator ("1 of X" counter) + 4. Caption + 5. Loading indicator + 6. Additional styles (root element, top bar, idle state, hidden state, etc.) + +*/ +/* + + 1. Buttons + + */ +/* "))),this.clickableElements.length&&function r(){i.hiddenFileInput&&i.hiddenFileInput.parentNode.removeChild(i.hiddenFileInput),i.hiddenFileInput=document.createElement("input"),i.hiddenFileInput.setAttribute("type","file"),(null===i.options.maxFiles||1")),t+='');t=x.createElement(t);return"FORM"!==this.element.tagName?(e=x.createElement('
'))).appendChild(t):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=e?e:t}},{key:"getExistingFallback",value:function(){for(var e=0,t=["div","form"];e".concat(t," ").concat(this.options.dictFileSizeUnits[n])}},{key:"_updateMaxFilesReachedClass",value:function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")}},{key:"drop",value:function(e){if(e.dataTransfer){this.emit("drop",e);for(var t,n=[],r=0;r1024*this.options.maxFilesize*1024?t(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(e.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):x.isValidFile(e,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(t(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",e)):this.options.accept.call(this,e,t):t(this.options.dictInvalidFileType)}},{key:"addFile",value:function(t){var n=this;t.upload={uuid:x.uuidv4(),progress:0,total:t.size,bytesSent:0,filename:this._renameFile(t)},this.files.push(t),t.status=x.ADDED,this.emit("addedfile",t),this._enqueueThumbnail(t),this.accept(t,function(e){e?(t.accepted=!1,n._errorProcessing([t],e)):(t.accepted=!0,n.options.autoQueue&&n.enqueueFile(t)),n._updateMaxFilesReachedClass()})}},{key:"enqueueFiles",value:function(e){var t=w(e,!0);try{for(t.s();!(n=t.n()).done;){var n=n.value;this.enqueueFile(n)}}catch(e){t.e(e)}finally{t.f()}return null}},{key:"enqueueFile",value:function(e){var t=this;if(e.status!==x.ADDED||!0!==e.accepted)throw new Error("This file can't be queued because it has already been processed or was rejected.");if(e.status=x.QUEUED,this.options.autoProcessQueue)return setTimeout(function(){return t.processQueue()},0)}},{key:"_enqueueThumbnail",value:function(e){var t=this;if(this.options.createImageThumbnails&&e.type.match(/image.*/)&&e.size<=1024*this.options.maxThumbnailFilesize*1024)return this._thumbnailQueue.push(e),setTimeout(function(){return t._processThumbnailQueue()},0)}},{key:"_processThumbnailQueue",value:function(){var t=this;if(!this._processingThumbnail&&0!==this._thumbnailQueue.length){this._processingThumbnail=!0;var n=this._thumbnailQueue.shift();return this.createThumbnail(n,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,!0,function(e){return t.emit("thumbnail",n,e),t._processingThumbnail=!1,t._processThumbnailQueue()})}}},{key:"removeFile",value:function(e){if(e.status===x.UPLOADING&&this.cancelUpload(e),this.files=m(this.files,e),this.emit("removedfile",e),0===this.files.length)return this.emit("reset")}},{key:"removeAllFiles",value:function(e){null==e&&(e=!1);var t=w(this.files.slice(),!0);try{for(t.s();!(n=t.n()).done;){var n=n.value;n.status===x.UPLOADING&&!e||this.removeFile(n)}}catch(e){t.e(e)}finally{t.f()}return null}},{key:"resizeImage",value:function(r,e,t,n,i){var o=this;return this.createThumbnail(r,e,t,n,!0,function(e,t){if(null==t)return i(r);var n=o.options.resizeMimeType;null==n&&(n=r.type);t=t.toDataURL(n,o.options.resizeQuality);return"image/jpeg"!==n&&"image/jpg"!==n||(t=k.restore(r.dataURL,t)),i(x.dataURItoBlob(t))})}},{key:"createThumbnail",value:function(e,t,n,r,i,o){var a=this,u=new FileReader;u.onload=function(){e.dataURL=u.result,"image/svg+xml"!==e.type?a.createThumbnailFromUrl(e,t,n,r,i,o):null!=o&&o(u.result)},u.readAsDataURL(e)}},{key:"displayExistingFile",value:function(t,e,n,r,i){var o=this,i=!(4l.options.chunkSize),s[0].upload.totalChunkCount=Math.ceil(t.size/l.options.chunkSize)),s[0].upload.chunked){var i=s[0],r=e[0];i.upload.chunks=[];var o=function(){for(var e,t,n=0;void 0!==i.upload.chunks[n];)n++;n>=i.upload.totalChunkCount||(e=n*l.options.chunkSize,t=Math.min(e+l.options.chunkSize,r.size),t={name:l._getParamName(0),data:r.webkitSlice?r.webkitSlice(e,t):r.slice(e,t),filename:i.upload.filename,chunkIndex:n},i.upload.chunks[n]={file:i,index:n,dataBlock:t,status:x.UPLOADING,progress:0,retries:0},l._uploadData(s,[t]))};if(i.upload.finishedChunkUpload=function(e,t){var n=!0;e.status=x.SUCCESS,e.dataBlock=null,e.xhr=null;for(var r=0;r>1;t=a/t;return 0==t?1:t}(t);return e.drawImage(t,n,r,i,o,a,u,s,l/c)},k=function(){function e(){a(this,e)}return t(e,null,[{key:"initClass",value:function(){this.KEY_STR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}},{key:"encode64",value:function(e){for(var t,n,r,i,o="",a="",u=void 0,s="",l=0;r=(t=e[l++])>>2,i=(3&t)<<4|(n=e[l++])>>4,u=(15&n)<<2|(a=e[l++])>>6,s=63&a,isNaN(n)?u=s=64:isNaN(a)&&(s=64),o=o+this.KEY_STR.charAt(r)+this.KEY_STR.charAt(i)+this.KEY_STR.charAt(u)+this.KEY_STR.charAt(s),u=s=a="",le.length)break}return i}},{key:"decode64",value:function(e){var t,n,r,i,o=void 0,a="",u=0,s=[];for(/[^A-Za-z0-9\+\/\=]/g.exec(e)&&console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");t=this.KEY_STR.indexOf(e.charAt(u++))<<2|(n=this.KEY_STR.indexOf(e.charAt(u++)))>>4,o=(15&n)<<4|(r=this.KEY_STR.indexOf(e.charAt(u++)))>>2,a=(3&r)<<6|(i=this.KEY_STR.indexOf(e.charAt(u++))),s.push(t),64!==r&&s.push(o),64!==i&&s.push(a),o=a="",u input[type="hidden"]');e&&(e.removeAttribute("required"),e.classList.remove("required"))},e}();document.addEventListener("DOMContentLoaded",function(e){new TF_Images_Selector_Field}); + diff --git a/media/plg_system_nrframework/js/inlinefileupload.js b/media/plg_system_nrframework/js/inlinefileupload.js new file mode 100644 index 00000000..a9c4b0c8 --- /dev/null +++ b/media/plg_system_nrframework/js/inlinefileupload.js @@ -0,0 +1,2 @@ +var NRInlineFileUploadSelector=function(){function e(){this.url_path="?option=com_ajax&format=raw&plugin=nrframework&path=plugins/system/nrframework/fields/&class=JFormFieldNRInlineFileUpload&file=nrinlinefileupload&task=Include",this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("click",function(e){this.handleOpen(e),this.handleClearUploadedItem(e),this.handleRemoveUploadedItem(e)}.bind(this)),document.addEventListener("change",function(e){this.handleChange(e)}.bind(this))},t.handleOpen=function(e){var t=e.target.closest(".file-selector-opener");t&&(e.preventDefault(),t.nextElementSibling.click())},t.handleClearUploadedItem=function(e){var t=e.target.closest(".nr-inline-file-upload-item-clear");t&&(e.preventDefault(),t.closest(".error").querySelector('input[type="hidden"]').value="",t.closest(".error").classList.remove("visible"),this.showUploadArea(t.closest(".nr-inline-file-upload")))},t.handleRemoveUploadedItem=function(e){var t,n,r,i=e.target.closest(".nr-inline-file-upload-item-remove");i&&(e.preventDefault(),confirm(i.dataset.confirm))&&(t=this,n=e.target.closest(".nr-inline-file-upload"),this.showLoader(n),(r=e.target.closest(".uploaded-files")).innerHTML="",(e=new FormData).append("remove_file",i.nextElementSibling.value),e.append(Joomla.getOptions("csrf.token"),1),fetch(n.dataset.baseUrl+this.url_path+"&action=remove",{method:"post",body:e}).then(function(e){return e.json()}).then(function(e){e.error?(t.showUploadArea(n),t.showError(n,e.response)):(t.hideError(n),r.previousElementSibling.classList.remove("hidden")),t.hideLoader(n)}))},t.handleChange=function(e){var t,n,r=e.target.closest(".file-selector");r&&(t=this,n=r.closest(".nr-inline-file-upload"),this.hideError(n),this.hideUploadArea(n),this.hideSelectedFile(n),this.showLoader(n),(e=new FormData).append("file",r.files[0]),e.append("upload_folder",n.dataset.uploadFolder),e.append(Joomla.getOptions("csrf.token"),1),fetch(n.dataset.baseUrl+this.url_path,{method:"post",body:e}).then(function(e){return e.json()}).then(function(e){e.error?(t.showUploadArea(n),t.showError(n,e.response)):(t.hideError(n),t.replaceSelectedFile(n,e)),t.hideLoader(n),r.value=""}))},t.hideSelectedFile=function(e){e.querySelector(".uploaded-files").innerHTML=""},t.replaceSelectedFile=function(e,t){e.querySelector(".uploaded-files").innerHTML="";var n=document.querySelector("template.nr-inline-file-upload-item").content.cloneNode(!0),r=(n.querySelector(".file-name").innerHTML=atob(t.file_name),n.querySelector(".size").innerHTML=t.file_size,document.createElement("div")),n=(r.classList.add("nr-inline-file-upload-item"),r.append(n),document.createElement("input"));n.setAttribute("type","hidden"),n.setAttribute("name",e.dataset.name),n.setAttribute("value",atob(t.file)),r.appendChild(n),e.querySelector(".uploaded-files").append(r),this.hideUploadArea(e)},t.showError=function(e,t){e.querySelector(".error").innerHTML=t,e.querySelector(".error").classList.add("visible")},t.hideError=function(e){e.querySelector(".error").innerHTML="",e.querySelector(".error").classList.remove("visible")},t.showLoader=function(e){e.classList.add("loading")},t.hideLoader=function(e){e.classList.remove("loading")},t.hideUploadArea=function(e){e.querySelector(".upload-area").classList.add("hidden")},t.showUploadArea=function(e){e.querySelector(".upload-area").classList.remove("hidden")},e}();document.addEventListener("DOMContentLoaded",function(){new NRInlineFileUploadSelector}); + diff --git a/media/plg_system_nrframework/js/mapeditor.js b/media/plg_system_nrframework/js/mapeditor.js new file mode 100644 index 00000000..d5bffb76 --- /dev/null +++ b/media/plg_system_nrframework/js/mapeditor.js @@ -0,0 +1,2 @@ +/*! For license information please see mapeditor.js.LICENSE.txt */ +(()=>{var t={922:t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var n="",i=void 0!==e[5];return e[4]&&(n+="@supports (".concat(e[4],") {")),e[2]&&(n+="@media ".concat(e[2]," {")),i&&(n+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),n+=t(e),i&&(n+="}"),e[2]&&(n+="}"),e[4]&&(n+="}"),n})).join("")},e.i=function(t,n,i,o,r){"string"==typeof t&&(t=[[null,t,void 0]]);var a={};if(i)for(var s=0;s0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=r),n&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=n):u[2]=n),o&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=o):u[4]="".concat(o)),e.push(u))}},e}},251:t=>{"use strict";t.exports=function(t){var e=t[1],n=t[3];if(!n)return e;if("function"==typeof btoa){var i=btoa(unescape(encodeURIComponent(JSON.stringify(n)))),o="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(i),r="/*# ".concat(o," */");return[e].concat([r]).join("\n")}return[e].join("\n")}},565:(t,e,n)=>{var i,o,r;o=[n(208)],void 0===(r="function"==typeof(i=function(t){t.Map.mergeOptions({contextmenuItems:[]}),t.Map.ContextMenu=t.Handler.extend({_touchstart:t.Browser.msPointer?"MSPointerDown":t.Browser.pointer?"pointerdown":"touchstart",statics:{BASE_CLS:"leaflet-contextmenu"},initialize:function(e){t.Handler.prototype.initialize.call(this,e),this._items=[],this._visible=!1;var n=this._container=t.DomUtil.create("div",t.Map.ContextMenu.BASE_CLS,e._container);n.style.zIndex=1e4,n.style.position="absolute",e.options.contextmenuWidth&&(n.style.width=e.options.contextmenuWidth+"px"),this._createItems(),t.DomEvent.on(n,"click",t.DomEvent.stop).on(n,"mousedown",t.DomEvent.stop).on(n,"dblclick",t.DomEvent.stop).on(n,"contextmenu",t.DomEvent.stop)},addHooks:function(){var e=this._map.getContainer();t.DomEvent.on(e,"mouseleave",this._hide,this).on(document,"keydown",this._onKeyDown,this),t.Browser.touch&&t.DomEvent.on(document,this._touchstart,this._hide,this),this._map.on({contextmenu:this._show,mousedown:this._hide,movestart:this._hide,zoomstart:this._hide},this)},removeHooks:function(){var e=this._map.getContainer();t.DomEvent.off(e,"mouseleave",this._hide,this).off(document,"keydown",this._onKeyDown,this),t.Browser.touch&&t.DomEvent.off(document,this._touchstart,this._hide,this),this._map.off({contextmenu:this._show,mousedown:this._hide,movestart:this._hide,zoomstart:this._hide},this)},showAt:function(e,n){e instanceof t.LatLng&&(e=this._map.latLngToContainerPoint(e)),this._showAtPoint(e,n)},hide:function(){this._hide()},addItem:function(t){return this.insertItem(t)},insertItem:function(t,e){e=void 0!==e?e:this._items.length;var n=this._createItem(this._container,t,e);return this._items.push(n),this._sizeChanged=!0,this._map.fire("contextmenu.additem",{contextmenu:this,el:n.el,index:e}),n.el},removeItem:function(e){var n=this._container;return isNaN(e)||(e=n.children[e]),e?(this._removeItem(t.Util.stamp(e)),this._sizeChanged=!0,this._map.fire("contextmenu.removeitem",{contextmenu:this,el:e}),e):null},removeAllItems:function(){for(var e,n=this._container.children;n.length;)e=n[0],this._removeItem(t.Util.stamp(e));return n},hideAllItems:function(){var t,e;for(t=0,e=this._items.length;t':c&&(u=''),a.innerHTML=u+n.text,a.href="#",t.DomEvent.on(a,"mouseover",this._onItemMouseOver,this).on(a,"mouseout",this._onItemMouseOut,this).on(a,"mousedown",t.DomEvent.stopPropagation).on(a,"click",s),t.Browser.touch&&t.DomEvent.on(a,this._touchstart,t.DomEvent.stopPropagation),t.Browser.pointer||t.DomEvent.on(a,"click",this._onItemMouseOut,this),{id:t.Util.stamp(a),el:a,callback:s}},_removeItem:function(e){var n,i,o,r,a;for(o=0,r=this._items.length;oi.x?(o.style.left="auto",o.style.right=Math.min(Math.max(i.x-e.x,0),i.x-r.x-1)+"px"):(o.style.left=Math.max(e.x,0)+"px",o.style.right="auto"),e.y+r.y>i.y?(o.style.top="auto",o.style.bottom=Math.min(Math.max(i.y-e.y,0),i.y-r.y-1)+"px"):(o.style.top=Math.max(e.y,0)+"px",o.style.bottom="auto")},_getElementSize:function(t){var e=this._size,n=t.style.display;return e&&!this._sizeChanged||(e={},t.style.left="-999999px",t.style.right="auto",t.style.display="block",e.x=t.offsetWidth,e.y=t.offsetHeight,t.style.left="auto",t.style.display=n,this._sizeChanged=!1),e},_onKeyDown:function(t){27===t.keyCode&&this._hide()},_onItemMouseOver:function(e){t.DomUtil.addClass(e.target||e.srcElement,"over")},_onItemMouseOut:function(e){t.DomUtil.removeClass(e.target||e.srcElement,"over")}}),t.Map.addInitHook("addHandler","contextmenu",t.Map.ContextMenu),t.Mixin.ContextMenu={bindContextMenu:function(e){return t.setOptions(this,e),this._initContextMenu(),this},unbindContextMenu:function(){return this.off("contextmenu",this._showContextMenu,this),this},addContextMenuItem:function(t){this.options.contextmenuItems.push(t)},removeContextMenuItemWithIndex:function(t){for(var e=[],n=0;n0?Math.floor(t):Math.ceil(t)};function I(t,e,n){return t instanceof R?t:v(t)?new R(t[0],t[1]):null==t?t:"object"===a(t)&&"x"in t&&"y"in t?new R(t.x,t.y):new R(t,e,n)}function j(t,e){if(t)for(var n=e?[t,e]:t,i=0,o=n.length;i=this.min.x&&n.x<=this.max.x&&e.y>=this.min.y&&n.y<=this.max.y},intersects:function(t){t=z(t);var e=this.min,n=this.max,i=t.min,o=t.max,r=o.x>=e.x&&i.x<=n.x,a=o.y>=e.y&&i.y<=n.y;return r&&a},overlaps:function(t){t=z(t);var e=this.min,n=this.max,i=t.min,o=t.max,r=o.x>e.x&&i.xe.y&&i.y=i.lat&&n.lat<=o.lat&&e.lng>=i.lng&&n.lng<=o.lng},intersects:function(t){t=Z(t);var e=this._southWest,n=this._northEast,i=t.getSouthWest(),o=t.getNorthEast(),r=o.lat>=e.lat&&i.lat<=n.lat,a=o.lng>=e.lng&&i.lng<=n.lng;return r&&a},overlaps:function(t){t=Z(t);var e=this._southWest,n=this._northEast,i=t.getSouthWest(),o=t.getNorthEast(),r=o.lat>e.lat&&i.late.lng&&i.lng1,kt=function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("testPassiveEventSupport",c,e),window.removeEventListener("testPassiveEventSupport",c,e)}catch(t){}return t}(),At=!!document.createElement("canvas").getContext,Rt=!(!document.createElementNS||!X("svg").createSVGRect),Ct=!!Rt&&(($=document.createElement("div")).innerHTML="","http://www.w3.org/2000/svg"===($.firstChild&&$.firstChild.namespaceURI)),It=!Rt&&function(){try{var t=document.createElement("div");t.innerHTML='';var e=t.firstChild;return e.style.behavior="url(#default#VML)",e&&"object"===a(e.adj)}catch(t){return!1}}();function jt(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}var zt={ie:et,ielt9:nt,edge:it,webkit:ot,android:rt,android23:at,androidStock:lt,opera:ct,chrome:ut,gecko:ht,safari:dt,phantom:pt,opera12:ft,win:mt,ie3d:_t,webkit3d:vt,gecko3d:gt,any3d:yt,mobile:bt,mobileWebkit:wt,mobileWebkit3d:xt,msPointer:Et,pointer:Lt,touch:Tt,touchNative:Pt,mobileOpera:Mt,mobileGecko:Ot,retina:St,passiveEvents:kt,canvas:At,svg:Rt,vml:It,inlineSvg:Ct,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},Nt=zt.msPointer?"MSPointerDown":"pointerdown",Zt=zt.msPointer?"MSPointerMove":"pointermove",Bt=zt.msPointer?"MSPointerUp":"pointerup",Dt=zt.msPointer?"MSPointerCancel":"pointercancel",Ht={touchstart:Nt,touchmove:Zt,touchend:Bt,touchcancel:Dt},Ft={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&Be(e),Kt(t,e)},touchmove:Kt,touchend:Kt,touchcancel:Kt},Ut={},Wt=!1;function Jt(t){Ut[t.pointerId]=t}function Vt(t){Ut[t.pointerId]&&(Ut[t.pointerId]=t)}function qt(t){delete Ut[t.pointerId]}function Kt(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){for(var n in e.touches=[],Ut)e.touches.push(Ut[n]);e.changedTouches=[e],t(e)}}var Gt,Yt,Xt,Qt,$t,te=200,ee=ve(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),ne=ve(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),ie="webkitTransition"===ne||"OTransition"===ne?ne+"End":"transitionend";function oe(t){return"string"==typeof t?document.getElementById(t):t}function re(t,e){var n=t.style[e]||t.currentStyle&&t.currentStyle[e];if((!n||"auto"===n)&&document.defaultView){var i=document.defaultView.getComputedStyle(t,null);n=i?i[e]:null}return"auto"===n?null:n}function ae(t,e,n){var i=document.createElement(t);return i.className=e||"",n&&n.appendChild(i),i}function se(t){var e=t.parentNode;e&&e.removeChild(t)}function le(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ce(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function ue(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function he(t,e){if(void 0!==t.classList)return t.classList.contains(e);var n=me(t);return n.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(n)}function de(t,e){if(void 0!==t.classList)for(var n=d(e),i=0,o=n.length;i0?2*window.devicePixelRatio:1;function We(t){return zt.edge?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/Ue:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function Je(t,e){var n=e.relatedTarget;if(!n)return!0;try{for(;n&&n!==t;)n=n.parentNode}catch(t){return!1}return n!==t}var Ve={__proto__:null,on:Se,off:Ae,stopPropagation:ze,disableScrollPropagation:Ne,disableClickPropagation:Ze,preventDefault:Be,stop:De,getPropagationPath:He,getMousePosition:Fe,getWheelDelta:We,isExternalTarget:Je,addListener:Se,removeListener:Ae},qe=A.extend({run:function(t,e,n,i){this.stop(),this._el=t,this._inProgress=!0,this._duration=n||.25,this._easeOutPower=1/Math.max(i||.5,.2),this._startPos=be(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=T(this._animate,this),this._step()},_step:function(t){var e=+new Date-this._startTime,n=1e3*this._duration;ethis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var n=this.getCenter(),i=this._limitCenter(n,this._zoom,Z(t));return n.equals(i)||this.panTo(i,e),this._enforcingBounds=!1,this},panInside:function(t,e){var n=I((e=e||{}).paddingTopLeft||e.padding||[0,0]),i=I(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),r=this.project(t),a=this.getPixelBounds(),s=z([a.min.add(n),a.max.subtract(i)]),l=s.getSize();if(!s.contains(r)){this._enforcingBounds=!0;var c=r.subtract(s.getCenter()),u=s.extend(r).getSize().subtract(l);o.x+=c.x<0?-u.x:u.x,o.y+=c.y<0?-u.y:u.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1}return this},invalidateSize:function(t){if(!this._loaded)return this;t=e({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),r=n.divideBy(2).round(),a=o.divideBy(2).round(),s=r.subtract(a);return s.x||s.y?(t.animate&&t.pan?this.panBy(s):(t.pan&&this._rawPanBy(s),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(i(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=e({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=i(this._handleGeolocationResponse,this),o=i(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){if(this._container._leaflet_id){var e=t.code,n=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+n+"."})}},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e=new B(t.coords.latitude,t.coords.longitude),n=e.toBounds(2*t.coords.accuracy),i=this._locateOptions;if(i.setView){var o=this.getBoundsZoom(n);this.setView(e,i.maxZoom?Math.min(o,i.maxZoom):o)}var r={latlng:e,bounds:n,timestamp:t.timestamp};for(var a in t.coords)"number"==typeof t.coords[a]&&(r[a]=t.coords[a]);this.fire("locationfound",r)}},addHandler:function(t,e){if(!e)return this;var n=this[t]=new e(this);return this._handlers.push(n),this.options[t]&&n.enable(),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}var t;for(t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),se(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(M(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)se(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){var n=ae("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),e||this._mapPane);return t&&(this._panes[t]=n),n},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new N(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,n){t=Z(t),n=I(n||[0,0]);var i=this.getZoom()||0,o=this.getMinZoom(),r=this.getMaxZoom(),a=t.getNorthWest(),s=t.getSouthEast(),l=this.getSize().subtract(n),c=z(this.project(s,i),this.project(a,i)).getSize(),u=zt.any3d?this.options.zoomSnap:1,h=l.x/c.x,d=l.y/c.y,p=e?Math.max(h,d):Math.min(h,d);return i=this.getScaleZoom(p,i),u&&(i=Math.round(i/(u/100))*(u/100),i=e?Math.ceil(i/u)*u:Math.floor(i/u)*u),Math.max(o,Math.min(r,i))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new R(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){var n=this._getTopLeftPoint(t,e);return new j(n,n.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var n=this.options.crs;return e=void 0===e?this._zoom:e,n.scale(t)/n.scale(e)},getScaleZoom:function(t,e){var n=this.options.crs;e=void 0===e?this._zoom:e;var i=n.zoom(t*n.scale(e));return isNaN(i)?1/0:i},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(D(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(I(t),e)},layerPointToLatLng:function(t){var e=I(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){return this.project(D(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(D(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(Z(t))},distance:function(t,e){return this.options.crs.distance(D(t),D(e))},containerPointToLayerPoint:function(t){return I(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return I(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(I(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(D(t)))},mouseEventToContainerPoint:function(t){return Fe(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=oe(t);if(!e)throw new Error("Map container not found.");if(e._leaflet_id)throw new Error("Map container is already initialized.");Se(e,"scroll",this._onScroll,this),this._containerId=r(e)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&zt.any3d,de(t,"leaflet-container"+(zt.touch?" leaflet-touch":"")+(zt.retina?" leaflet-retina":"")+(zt.ielt9?" leaflet-oldie":"")+(zt.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var e=re(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&"sticky"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),ye(this._mapPane,new R(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(de(t.markerPane,"leaflet-zoom-hide"),de(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e,n){ye(this._mapPane,new R(0,0));var i=!this._loaded;this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset");var o=this._zoom!==e;this._moveStart(o,n)._move(t,e)._moveEnd(o),this.fire("viewreset"),i&&this.fire("load")},_moveStart:function(t,e){return t&&this.fire("zoomstart"),e||this.fire("movestart"),this},_move:function(t,e,n,i){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),i?n&&n.pinch&&this.fire("zoom",n):((o||n&&n.pinch)&&this.fire("zoom",n),this.fire("move",n)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return M(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){ye(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[r(this._container)]=this;var e=t?Ae:Se;e(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&e(window,"resize",this._onResize,this),zt.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){M(this._resizeRequest),this._resizeRequest=T((function(){this.invalidateSize({debounceMoveend:!0})}),this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var n,i=[],o="mouseout"===e||"mouseover"===e,a=t.target||t.srcElement,s=!1;a;){if((n=this._targets[r(a)])&&("click"===e||"preclick"===e)&&this._draggableMoved(n)){s=!0;break}if(n&&n.listens(e,!0)){if(o&&!Je(a,t))break;if(i.push(n),o)break}if(a===this._container)break;a=a.parentNode}return i.length||s||o||!this.listens(e,!0)||(i=[this]),i},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e=t.target||t.srcElement;if(!(!this._loaded||e._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(e))){var n=t.type;"mousedown"===n&&Le(e),this._fireDOMEvent(t,n)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,n,i){if("click"===t.type){var o=e({},t);o.type="preclick",this._fireDOMEvent(o,o.type,i)}var r=this._findEventTargets(t,n);if(i){for(var a=[],s=0;s0?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(t){var e=this.getMinZoom(),n=this.getMaxZoom(),i=zt.any3d?this.options.zoomSnap:1;return i&&(t=Math.round(t/i)*i),Math.max(e,Math.min(n,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){pe(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var n=this._getCenterOffset(t)._trunc();return!(!0!==(e&&e.animate)&&!this.getSize().contains(n)||(this.panBy(n,e),0))},_createAnimProxy:function(){var t=this._proxy=ae("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",(function(t){var e=ee,n=this._proxy.style[e];ge(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),n===this._proxy.style[e]&&this._animatingZoom&&this._onZoomTransitionEnd()}),this),this.on("load moveend",this._animMoveEnd,this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){se(this._proxy),this.off("load moveend",this._animMoveEnd,this),delete this._proxy},_animMoveEnd:function(){var t=this.getCenter(),e=this.getZoom();ge(this._proxy,this.project(t,e),this.getZoomScale(e,1))},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,n){if(this._animatingZoom)return!0;if(n=n||{},!this._zoomAnimated||!1===n.animate||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var i=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/i);return!(!0!==n.animate&&!this.getSize().contains(o)||(T((function(){this._moveStart(!0,n.noMoveStart||!1)._animateZoom(t,e,!0)}),this),0))},_animateZoom:function(t,e,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,de(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:o}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(i(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&pe(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}}),Ge=S.extend({options:{position:"topright"},initialize:function(t){p(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),n=this.getPosition(),i=t._controlCorners[n];return de(e,"leaflet-control"),-1!==n.indexOf("bottom")?i.insertBefore(e,i.firstChild):i.appendChild(e),this._map.on("unload",this.remove,this),this},remove:function(){return this._map?(se(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),Ye=function(t){return new Ge(t)};Ke.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){var t=this._controlCorners={},e="leaflet-",n=this._controlContainer=ae("div",e+"control-container",this._container);function i(i,o){var r=e+i+" "+e+o;t[i+o]=ae("div",r,n)}i("top","left"),i("top","right"),i("bottom","left"),i("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)se(this._controlCorners[t]);se(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Xe=Ge.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,e,n,i){return n1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=e&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var e=this._getLayer(r(t.target)),n=e.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;n&&this._map.fire(n,e)},_createRadioElement:function(t,e){var n='",i=document.createElement("div");return i.innerHTML=n,i.firstChild},_addItem:function(t){var e,n=document.createElement("label"),i=this._map.hasLayer(t.layer);t.overlay?((e=document.createElement("input")).type="checkbox",e.className="leaflet-control-layers-selector",e.defaultChecked=i):e=this._createRadioElement("leaflet-base-layers_"+r(this),i),this._layerControlInputs.push(e),e.layerId=r(t.layer),Se(e,"click",this._onInputClick,this);var o=document.createElement("span");o.innerHTML=" "+t.name;var a=document.createElement("span");return n.appendChild(a),a.appendChild(e),a.appendChild(o),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(n),this._checkDisabledLayers(),n},_onInputClick:function(){if(!this._preventClick){var t,e,n=this._layerControlInputs,i=[],o=[];this._handlingClick=!0;for(var r=n.length-1;r>=0;r--)t=n[r],e=this._getLayer(t.layerId).layer,t.checked?i.push(e):t.checked||o.push(e);for(r=0;r=0;o--)t=n[o],e=this._getLayer(t.layerId).layer,t.disabled=void 0!==e.options.minZoom&&ie.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section;this._preventClick=!0,Se(t,"click",Be),this.expand();var e=this;setTimeout((function(){Ae(t,"click",Be),e._preventClick=!1}))}}),Qe=Ge.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",n=ae("div",e+" leaflet-bar"),i=this.options;return this._zoomInButton=this._createButton(i.zoomInText,i.zoomInTitle,e+"-in",n,this._zoomIn),this._zoomOutButton=this._createButton(i.zoomOutText,i.zoomOutTitle,e+"-out",n,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),n},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,n,i,o){var r=ae("a",n,i);return r.innerHTML=t,r.href="#",r.title=e,r.setAttribute("role","button"),r.setAttribute("aria-label",e),Ze(r),Se(r,"click",De),Se(r,"click",o,this),Se(r,"click",this._refocusOnMap,this),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";pe(this._zoomInButton,e),pe(this._zoomOutButton,e),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),(this._disabled||t._zoom===t.getMinZoom())&&(de(this._zoomOutButton,e),this._zoomOutButton.setAttribute("aria-disabled","true")),(this._disabled||t._zoom===t.getMaxZoom())&&(de(this._zoomInButton,e),this._zoomInButton.setAttribute("aria-disabled","true"))}});Ke.mergeOptions({zoomControl:!0}),Ke.addInitHook((function(){this.options.zoomControl&&(this.zoomControl=new Qe,this.addControl(this.zoomControl))}));var $e=Ge.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",n=ae("div",e),i=this.options;return this._addScales(i,e+"-line",n),t.on(i.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),n},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,n){t.metric&&(this._mScale=ae("div",e,n)),t.imperial&&(this._iScale=ae("div",e,n))},_update:function(){var t=this._map,e=t.getSize().y/2,n=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(n)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t),n=e<1e3?e+" m":e/1e3+" km";this._updateScale(this._mScale,n,e/t)},_updateImperial:function(t){var e,n,i,o=3.2808399*t;o>5280?(e=o/5280,n=this._getRoundNum(e),this._updateScale(this._iScale,n+" mi",n/e)):(i=this._getRoundNum(o),this._updateScale(this._iScale,i+" ft",i/o))},_updateScale:function(t,e,n){t.style.width=Math.round(this.options.maxWidth*n)+"px",t.innerHTML=e},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),n=t/e;return e*(n>=10?10:n>=5?5:n>=3?3:n>=2?2:1)}}),tn=Ge.extend({options:{position:"bottomright",prefix:''+(zt.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){p(this,t),this._attributions={}},onAdd:function(t){for(var e in t.attributionControl=this,this._container=ae("div","leaflet-control-attribution"),Ze(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),t.on("layeradd",this._addAttribution,this),this._container},onRemove:function(t){t.off("layeradd",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once("remove",(function(){this.removeAttribution(t.layer.getAttribution())}),this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var n=[];this.options.prefix&&n.push(this.options.prefix),t.length&&n.push(t.join(", ")),this._container.innerHTML=n.join(' ')}}});Ke.mergeOptions({attributionControl:!0}),Ke.addInitHook((function(){this.options.attributionControl&&(new tn).addTo(this)})),Ge.Layers=Xe,Ge.Zoom=Qe,Ge.Scale=$e,Ge.Attribution=tn,Ye.layers=function(t,e,n){return new Xe(t,e,n)},Ye.zoom=function(t){return new Qe(t)},Ye.scale=function(t){return new $e(t)},Ye.attribution=function(t){return new tn(t)};var en=S.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});en.addTo=function(t,e){return t.addHandler(e,this),this};var nn={Events:k},on=zt.touch?"touchstart mousedown":"mousedown",rn=A.extend({options:{clickTolerance:3},initialize:function(t,e,n,i){p(this,i),this._element=t,this._dragStartTarget=e||t,this._preventOutline=n},enable:function(){this._enabled||(Se(this._dragStartTarget,on,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(rn._dragging===this&&this.finishDrag(!0),Ae(this._dragStartTarget,on,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(this._enabled&&(this._moved=!1,!he(this._element,"leaflet-zoom-anim")))if(t.touches&&1!==t.touches.length)rn._dragging===this&&this.finishDrag();else if(!(rn._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(rn._dragging=this,this._preventOutline&&Le(this._element),xe(),Gt(),this._moving))){this.fire("down");var e=t.touches?t.touches[0]:t,n=Te(this._element);this._startPoint=new R(e.clientX,e.clientY),this._startPos=be(this._element),this._parentScale=Me(n);var i="mousedown"===t.type;Se(document,i?"mousemove":"touchmove",this._onMove,this),Se(document,i?"mouseup":"touchend touchcancel",this._onUp,this)}},_onMove:function(t){if(this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var e=t.touches&&1===t.touches.length?t.touches[0]:t,n=new R(e.clientX,e.clientY)._subtract(this._startPoint);(n.x||n.y)&&(Math.abs(n.x)+Math.abs(n.y)e&&(n.push(t[i]),o=i);var a,s,l,c;return ol&&(r=a,l=s);l>n&&(e[r]=1,pn(t,e,n,i,r),pn(t,e,n,r,o))}function fn(t,e,n,i,o){var r,a,s,l=i?cn:_n(t,n),c=_n(e,n);for(cn=c;;){if(!(l|c))return[t,e];if(l&c)return!1;s=_n(a=mn(t,e,r=l||c,n,o),n),r===l?(t=a,l=s):(e=a,c=s)}}function mn(t,e,n,i,o){var r,a,s=e.x-t.x,l=e.y-t.y,c=i.min,u=i.max;return 8&n?(r=t.x+s*(u.y-t.y)/l,a=u.y):4&n?(r=t.x+s*(c.y-t.y)/l,a=c.y):2&n?(r=u.x,a=t.y+l*(u.x-t.x)/s):1&n&&(r=c.x,a=t.y+l*(c.x-t.x)/s),new R(r,a,o)}function _n(t,e){var n=0;return t.xe.max.x&&(n|=2),t.ye.max.y&&(n|=8),n}function vn(t,e,n,i){var o,r=e.x,a=e.y,s=n.x-r,l=n.y-a,c=s*s+l*l;return c>0&&((o=((t.x-r)*s+(t.y-a)*l)/c)>1?(r=n.x,a=n.y):o>0&&(r+=s*o,a+=l*o)),s=t.x-r,l=t.y-a,i?s*s+l*l:new R(r,a)}function gn(t){return!v(t[0])||"object"!==a(t[0][0])&&void 0!==t[0][0]}function yn(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),gn(t)}function bn(t,e){var n,i,o,r,a,s,l,c;if(!t||0===t.length)throw new Error("latlngs not passed");gn(t)||(console.warn("latlngs are not flat! Only the first ring will be used"),t=t[0]);var u=D([0,0]),h=Z(t);h.getNorthWest().distanceTo(h.getSouthWest())*h.getNorthEast().distanceTo(h.getNorthWest())<1700&&(u=ln(t));var d=t.length,p=[];for(n=0;ni){l=(r-i)/o,c=[s.x-l*(s.x-a.x),s.y-l*(s.y-a.y)];break}var m=e.unproject(I(c));return D([m.lat+u.lat,m.lng+u.lng])}var wn={__proto__:null,simplify:hn,pointToSegmentDistance:dn,closestPointOnSegment:function(t,e,n){return vn(t,e,n)},clipSegment:fn,_getEdgeIntersection:mn,_getBitCode:_n,_sqClosestPointOnSegment:vn,isFlat:gn,_flat:yn,polylineCenter:bn},xn={project:function(t){return new R(t.lng,t.lat)},unproject:function(t){return new B(t.y,t.x)},bounds:new j([-180,-90],[180,90])},En={R:6378137,R_MINOR:6356752.314245179,bounds:new j([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var e=Math.PI/180,n=this.R,i=t.lat*e,o=this.R_MINOR/n,r=Math.sqrt(1-o*o),a=r*Math.sin(i),s=Math.tan(Math.PI/4-i/2)/Math.pow((1-a)/(1+a),r/2);return i=-n*Math.log(Math.max(s,1e-10)),new R(t.lng*e*n,i)},unproject:function(t){for(var e,n=180/Math.PI,i=this.R,o=this.R_MINOR/i,r=Math.sqrt(1-o*o),a=Math.exp(-t.y/i),s=Math.PI/2-2*Math.atan(a),l=0,c=.1;l<15&&Math.abs(c)>1e-7;l++)e=r*Math.sin(s),e=Math.pow((1-e)/(1+e),r/2),s+=c=Math.PI/2-2*Math.atan(a*e)-s;return new B(s*n,t.x*n/i)}},Ln={__proto__:null,LonLat:xn,Mercator:En,SphericalMercator:J},Pn=e({},U,{code:"EPSG:3395",projection:En,transformation:function(){var t=.5/(Math.PI*En.R);return q(t,.5,-t,.5)}()}),Tn=e({},U,{code:"EPSG:4326",projection:xn,transformation:q(1/180,1,-1/180,.5)}),Mn=e({},F,{projection:xn,transformation:q(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,e){var n=e.lng-t.lng,i=e.lat-t.lat;return Math.sqrt(n*n+i*i)},infinite:!0});F.Earth=U,F.EPSG3395=Pn,F.EPSG3857=G,F.EPSG900913=Y,F.EPSG4326=Tn,F.Simple=Mn;var On=A.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[r(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[r(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var e=t.target;if(e.hasLayer(this)){if(this._map=e,this._zoomAnimated=e._zoomAnimated,this.getEvents){var n=this.getEvents();e.on(n,this),this.once("remove",(function(){e.off(n,this)}),this)}this.onAdd(e),this.fire("add"),e.fire("layeradd",{layer:this})}}});Ke.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var e=r(t);return this._layers[e]||(this._layers[e]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t)),this},removeLayer:function(t){var e=r(t);return this._layers[e]?(this._loaded&&t.onRemove(this),delete this._layers[e],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return r(t)in this._layers},eachLayer:function(t,e){for(var n in this._layers)t.call(e,this._layers[n]);return this},_addLayers:function(t){for(var e=0,n=(t=t?v(t)?t:[t]:[]).length;ethis._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()=2&&e[0]instanceof B&&e[0].equals(e[n-1])&&e.pop(),e},_setLatLngs:function(t){Zn.prototype._setLatLngs.call(this,t),gn(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return gn(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,e=this.options.weight,n=new R(e,e);if(t=new j(t.min.subtract(n),t.max.add(n)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var i,o=0,r=this._rings.length;ot.y!=i.y>t.y&&t.x<(i.x-n.x)*(t.y-n.y)/(i.y-n.y)+n.x&&(c=!c);return c||Zn.prototype._containsPoint.call(this,t,!0)}}),Dn=kn.extend({initialize:function(t,e){p(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,n,i,o=v(t)?t:t.features;if(o){for(e=0,n=o.length;e0&&o.push(o[0].slice()),o}function qn(t,n){return t.feature?e({},t.feature,{geometry:n}):Kn(n)}function Kn(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}var Gn={toGeoJSON:function(t){return qn(this,{type:"Point",coordinates:Jn(this.getLatLng(),t)})}};function Yn(t,e){return new Dn(t,e)}In.include(Gn),Nn.include(Gn),zn.include(Gn),Zn.include({toGeoJSON:function(t){var e=!gn(this._latlngs);return qn(this,{type:(e?"Multi":"")+"LineString",coordinates:Vn(this._latlngs,e?1:0,!1,t)})}}),Bn.include({toGeoJSON:function(t){var e=!gn(this._latlngs),n=e&&!gn(this._latlngs[0]),i=Vn(this._latlngs,n?2:e?1:0,!0,t);return e||(i=[i]),qn(this,{type:(n?"Multi":"")+"Polygon",coordinates:i})}}),Sn.include({toMultiPoint:function(t){var e=[];return this.eachLayer((function(n){e.push(n.toGeoJSON(t).geometry.coordinates)})),qn(this,{type:"MultiPoint",coordinates:e})},toGeoJSON:function(t){var e=this.feature&&this.feature.geometry&&this.feature.geometry.type;if("MultiPoint"===e)return this.toMultiPoint(t);var n="GeometryCollection"===e,i=[];return this.eachLayer((function(e){if(e.toGeoJSON){var o=e.toGeoJSON(t);if(n)i.push(o.geometry);else{var r=Kn(o);"FeatureCollection"===r.type?i.push.apply(i,r.features):i.push(r)}}})),n?qn(this,{geometries:i,type:"GeometryCollection"}):{type:"FeatureCollection",features:i}}});var Xn=Yn,Qn=On.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(t,e,n){this._url=t,this._bounds=Z(e),p(this,n)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(de(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){se(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&ce(this._image),this},bringToBack:function(){return this._map&&ue(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=Z(t),this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t="IMG"===this._url.tagName,e=this._image=t?this._url:ae("img");de(e,"leaflet-image-layer"),this._zoomAnimated&&de(e,"leaflet-zoom-animated"),this.options.className&&de(e,this.options.className),e.onselectstart=c,e.onmousemove=c,e.onload=i(this.fire,this,"load"),e.onerror=i(this._overlayOnError,this,"error"),(this.options.crossOrigin||""===this.options.crossOrigin)&&(e.crossOrigin=!0===this.options.crossOrigin?"":this.options.crossOrigin),this.options.zIndex&&this._updateZIndex(),t?this._url=e.src:(e.src=this._url,e.alt=this.options.alt)},_animateZoom:function(t){var e=this._map.getZoomScale(t.zoom),n=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;ge(this._image,n,e)},_reset:function(){var t=this._image,e=new j(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),n=e.getSize();ye(t,e.min),t.style.width=n.x+"px",t.style.height=n.y+"px"},_updateOpacity:function(){_e(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var t=this.options.errorOverlayUrl;t&&this._url!==t&&(this._url=t,this._image.src=t)},getCenter:function(){return this._bounds.getCenter()}}),$n=Qn.extend({options:{autoplay:!0,loop:!0,keepAspectRatio:!0,muted:!1,playsInline:!0},_initImage:function(){var t="VIDEO"===this._url.tagName,e=this._image=t?this._url:ae("video");if(de(e,"leaflet-image-layer"),this._zoomAnimated&&de(e,"leaflet-zoom-animated"),this.options.className&&de(e,this.options.className),e.onselectstart=c,e.onmousemove=c,e.onloadeddata=i(this.fire,this,"load"),t){for(var n=e.getElementsByTagName("source"),o=[],r=0;r0?o:[e.src]}else{v(this._url)||(this._url=[this._url]),!this.options.keepAspectRatio&&Object.prototype.hasOwnProperty.call(e.style,"objectFit")&&(e.style.objectFit="fill"),e.autoplay=!!this.options.autoplay,e.loop=!!this.options.loop,e.muted=!!this.options.muted,e.playsInline=!!this.options.playsInline;for(var a=0;ao?(e.height=o+"px",de(t,r)):pe(t,r),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),n=this._getAnchor();ye(this._container,e.add(n))},_adjustPan:function(){if(this.options.autoPan)if(this._map._panAnim&&this._map._panAnim.stop(),this._autopanning)this._autopanning=!1;else{var t=this._map,e=parseInt(re(this._container,"marginBottom"),10)||0,n=this._container.offsetHeight+e,i=this._containerWidth,o=new R(this._containerLeft,-n-this._containerBottom);o._add(be(this._container));var r=t.layerPointToContainerPoint(o),a=I(this.options.autoPanPadding),s=I(this.options.autoPanPaddingTopLeft||a),l=I(this.options.autoPanPaddingBottomRight||a),c=t.getSize(),u=0,h=0;r.x+i+l.x>c.x&&(u=r.x+i-c.x+l.x),r.x-u-s.x<0&&(u=r.x-s.x),r.y+n+l.y>c.y&&(h=r.y+n-c.y+l.y),r.y-h-s.y<0&&(h=r.y-s.y),(u||h)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([u,h]))}},_getAnchor:function(){return I(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Ke.mergeOptions({closePopupOnClick:!0}),Ke.include({openPopup:function(t,e,n){return this._initOverlay(ni,t,e,n).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),On.include({bindPopup:function(t,e){return this._popup=this._initOverlay(ni,this._popup,t,e),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof kn||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){if(this._popup&&this._map){De(t);var e=t.layer||t.target;this._popup._source!==e||e instanceof jn?(this._popup._source=e,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng)}},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var ii=ei.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){ei.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(t){ei.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var t=ei.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ae("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+r(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,n,i=this._map,o=this._container,r=i.latLngToContainerPoint(i.getCenter()),a=i.layerPointToContainerPoint(t),s=this.options.direction,l=o.offsetWidth,c=o.offsetHeight,u=I(this.options.offset),h=this._getAnchor();"top"===s?(e=l/2,n=c):"bottom"===s?(e=l/2,n=0):"center"===s?(e=l/2,n=c/2):"right"===s?(e=0,n=c/2):"left"===s?(e=l,n=c/2):a.xthis.options.maxZoom||ni&&this._retainParent(o,r,a,i))},_retainChildren:function(t,e,n,i){for(var o=2*t;o<2*t+2;o++)for(var r=2*e;r<2*e+2;r++){var a=new R(o,r);a.z=n+1;var s=this._tileCoordsToKey(a),l=this._tiles[s];l&&l.active?l.retain=!0:(l&&l.loaded&&(l.retain=!0),n+1this.options.maxZoom||void 0!==this.options.minZoom&&o1)this._setView(t,n);else{for(var h=o.min.y;h<=o.max.y;h++)for(var d=o.min.x;d<=o.max.x;d++){var p=new R(d,h);if(p.z=this._tileZoom,this._isValidTile(p)){var f=this._tiles[this._tileCoordsToKey(p)];f?f.current=!0:a.push(p)}}if(a.sort((function(t,e){return t.distanceTo(r)-e.distanceTo(r)})),0!==a.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(d=0;dn.max.x)||!e.wrapLat&&(t.yn.max.y))return!1}if(!this.options.bounds)return!0;var i=this._tileCoordsToBounds(t);return Z(this.options.bounds).overlaps(i)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,n=this.getTileSize(),i=t.scaleBy(n),o=i.add(n);return[e.unproject(i,t.z),e.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var e=this._tileCoordsToNwSe(t),n=new N(e[0],e[1]);return this.options.noWrap||(n=this._map.wrapLatLngBounds(n)),n},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var e=t.split(":"),n=new R(+e[0],+e[1]);return n.z=+e[2],n},_removeTile:function(t){var e=this._tiles[t];e&&(se(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){de(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=c,t.onmousemove=c,zt.ielt9&&this.options.opacity<1&&_e(t,this.options.opacity)},_addTile:function(t,e){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),r=this.createTile(this._wrapCoords(t),i(this._tileReady,this,t));this._initTile(r),this.createTile.length<2&&T(i(this._tileReady,this,t,null,r)),ye(r,n),this._tiles[o]={el:r,coords:t,current:!0},e.appendChild(r),this.fire("tileloadstart",{tile:r,coords:t})},_tileReady:function(t,e,n){e&&this.fire("tileerror",{error:e,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(_e(n.el,0),M(this._fadeFrame),this._fadeFrame=T(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),e||(de(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),zt.ielt9||!this._map._fadeAnimated?T(this._pruneTiles,this):setTimeout(i(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new R(this._wrapX?l(t.x,this._wrapX):t.x,this._wrapY?l(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new j(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),ai=ri.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=p(this,e)).detectRetina&&zt.retina&&e.maxZoom>0?(e.tileSize=Math.floor(e.tileSize/2),e.zoomReverse?(e.zoomOffset--,e.minZoom=Math.min(e.maxZoom,e.minZoom+1)):(e.zoomOffset++,e.maxZoom=Math.max(e.minZoom,e.maxZoom-1)),e.minZoom=Math.max(0,e.minZoom)):e.zoomReverse?e.minZoom=Math.min(e.maxZoom,e.minZoom):e.maxZoom=Math.max(e.minZoom,e.maxZoom),"string"==typeof e.subdomains&&(e.subdomains=e.subdomains.split("")),this.on("tileunload",this._onTileRemove)},setUrl:function(t,e){return this._url===t&&void 0===e&&(e=!0),this._url=t,e||this.redraw(),this},createTile:function(t,e){var n=document.createElement("img");return Se(n,"load",i(this._tileOnLoad,this,e,n)),Se(n,"error",i(this._tileOnError,this,e,n)),(this.options.crossOrigin||""===this.options.crossOrigin)&&(n.crossOrigin=!0===this.options.crossOrigin?"":this.options.crossOrigin),"string"==typeof this.options.referrerPolicy&&(n.referrerPolicy=this.options.referrerPolicy),n.alt="",n.src=this.getTileUrl(t),n},getTileUrl:function(t){var n={r:zt.retina?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var i=this._globalTileRange.max.y-t.y;this.options.tms&&(n.y=i),n["-y"]=i}return _(this._url,e(n,this.options))},_tileOnLoad:function(t,e){zt.ielt9?setTimeout(i(t,this,null,e),0):t(null,e)},_tileOnError:function(t,e,n){var i=this.options.errorTileUrl;i&&e.getAttribute("src")!==i&&(e.src=i),t(n,e)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,e=this.options.maxZoom;return this.options.zoomReverse&&(t=e-t),t+this.options.zoomOffset},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_abortLoading:function(){var t,e;for(t in this._tiles)if(this._tiles[t].coords.z!==this._tileZoom&&((e=this._tiles[t].el).onload=c,e.onerror=c,!e.complete)){e.src=y;var n=this._tiles[t].coords;se(e),delete this._tiles[t],this.fire("tileabort",{tile:e,coords:n})}},_removeTile:function(t){var e=this._tiles[t];if(e)return e.el.setAttribute("src",y),ri.prototype._removeTile.call(this,t)},_tileReady:function(t,e,n){if(this._map&&(!n||n.getAttribute("src")!==y))return ri.prototype._tileReady.call(this,t,e,n)}});function si(t,e){return new ai(t,e)}var li=ai.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,n){this._url=t;var i=e({},this.defaultWmsParams);for(var o in n)o in this.options||(i[o]=n[o]);var r=(n=p(this,n)).detectRetina&&zt.retina?2:1,a=this.getTileSize();i.width=a.x*r,i.height=a.y*r,this.wmsParams=i},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,ai.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._tileCoordsToNwSe(t),n=this._crs,i=z(n.project(e[0]),n.project(e[1])),o=i.min,r=i.max,a=(this._wmsVersion>=1.3&&this._crs===Tn?[o.y,o.x,r.y,r.x]:[o.x,o.y,r.x,r.y]).join(","),s=ai.prototype.getTileUrl.call(this,t);return s+f(this.wmsParams,s,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+a},setParams:function(t,n){return e(this.wmsParams,t),n||this.redraw(),this}});ai.WMS=li,si.wms=function(t,e){return new li(t,e)};var ci=On.extend({options:{padding:.1},initialize:function(t){p(this,t),r(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),de(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,e){var n=this._map.getZoomScale(e,this._zoom),i=this._map.getSize().multiplyBy(.5+this.options.padding),o=this._map.project(this._center,e),r=i.multiplyBy(-n).add(o).subtract(this._map._getNewPixelOrigin(t,e));zt.any3d?ge(this._container,r,n):ye(this._container,r)},_reset:function(){for(var t in this._update(),this._updateTransform(this._center,this._zoom),this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,e=this._map.getSize(),n=this._map.containerPointToLayerPoint(e.multiplyBy(-t)).round();this._bounds=new j(n,n.add(e.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),ui=ci.extend({options:{tolerance:0},getEvents:function(){var t=ci.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){ci.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");Se(t,"mousemove",this._onMouseMove,this),Se(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),Se(t,"mouseout",this._handleMouseOut,this),t._leaflet_disable_events=!0,this._ctx=t.getContext("2d")},_destroyContainer:function(){M(this._redrawRequest),delete this._ctx,se(this._container),Ae(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){for(var t in this._redrawBounds=null,this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){ci.prototype._update.call(this);var t=this._bounds,e=this._container,n=t.getSize(),i=zt.retina?2:1;ye(e,t.min),e.width=i*n.x,e.height=i*n.y,e.style.width=n.x+"px",e.style.height=n.y+"px",zt.retina&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){ci.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[r(t)]=t;var e=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=e),this._drawLast=e,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var e=t._order,n=e.next,i=e.prev;n?n.prev=i:this._drawLast=i,i?i.next=n:this._drawFirst=n,delete t._order,delete this._layers[r(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if("string"==typeof t.options.dashArray){var e,n,i=t.options.dashArray.split(/[, ]+/),o=[];for(n=0;n')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),pi={_initContainer:function(){this._container=ae("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(ci.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=di("shape");de(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=di("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[r(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;se(e),t.removeInteractiveTarget(e),delete this._layers[r(t)]},_updateStyle:function(t){var e=t._stroke,n=t._fill,i=t.options,o=t._container;o.stroked=!!i.stroke,o.filled=!!i.fill,i.stroke?(e||(e=t._stroke=di("stroke")),o.appendChild(e),e.weight=i.weight+"px",e.color=i.color,e.opacity=i.opacity,i.dashArray?e.dashStyle=v(i.dashArray)?i.dashArray.join(" "):i.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=i.lineCap.replace("butt","flat"),e.joinstyle=i.lineJoin):e&&(o.removeChild(e),t._stroke=null),i.fill?(n||(n=t._fill=di("fill")),o.appendChild(n),n.color=i.fillColor||i.color,n.opacity=i.fillOpacity):n&&(o.removeChild(n),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),n=Math.round(t._radius),i=Math.round(t._radiusY||n);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+n+","+i+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){ce(t._container)},_bringToBack:function(t){ue(t._container)}},fi=zt.vml?di:X,mi=ci.extend({_initContainer:function(){this._container=fi("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=fi("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){se(this._container),Ae(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){if(!this._map._animatingZoom||!this._bounds){ci.prototype._update.call(this);var t=this._bounds,e=t.getSize(),n=this._container;this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,n.setAttribute("width",e.x),n.setAttribute("height",e.y)),ye(n,t.min),n.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update")}},_initPath:function(t){var e=t._path=fi("path");t.options.className&&de(e,t.options.className),t.options.interactive&&de(e,"leaflet-interactive"),this._updateStyle(t),this._layers[r(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){se(t._path),t.removeInteractiveTarget(t._path),delete this._layers[r(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,n=t.options;e&&(n.stroke?(e.setAttribute("stroke",n.color),e.setAttribute("stroke-opacity",n.opacity),e.setAttribute("stroke-width",n.weight),e.setAttribute("stroke-linecap",n.lineCap),e.setAttribute("stroke-linejoin",n.lineJoin),n.dashArray?e.setAttribute("stroke-dasharray",n.dashArray):e.removeAttribute("stroke-dasharray"),n.dashOffset?e.setAttribute("stroke-dashoffset",n.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),n.fill?(e.setAttribute("fill",n.fillColor||n.color),e.setAttribute("fill-opacity",n.fillOpacity),e.setAttribute("fill-rule",n.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,Q(t._parts,e))},_updateCircle:function(t){var e=t._point,n=Math.max(Math.round(t._radius),1),i="a"+n+","+(Math.max(Math.round(t._radiusY),1)||n)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(e.x-n)+","+e.y+i+2*n+",0 "+i+2*-n+",0 ";this._setPath(t,o)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){ce(t._path)},_bringToBack:function(t){ue(t._path)}});function _i(t){return zt.svg||zt.vml?new mi(t):null}zt.vml&&mi.include(pi),Ke.include({getRenderer:function(t){var e=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return e||(e=this._renderer=this._createRenderer()),this.hasLayer(e)||this.addLayer(e),e},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var e=this._paneRenderers[t];return void 0===e&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e},_createRenderer:function(t){return this.options.preferCanvas&&hi(t)||_i(t)}});var vi=Bn.extend({initialize:function(t,e){Bn.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=Z(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});mi.create=fi,mi.pointsToPath=Q,Dn.geometryToLayer=Hn,Dn.coordsToLatLng=Un,Dn.coordsToLatLngs=Wn,Dn.latLngToCoords=Jn,Dn.latLngsToCoords=Vn,Dn.getFeature=qn,Dn.asFeature=Kn,Ke.mergeOptions({boxZoom:!0});var gi=en.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){Se(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Ae(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){se(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),Gt(),xe(),this._startPoint=this._map.mouseEventToContainerPoint(t),Se(document,{contextmenu:De,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ae("div","leaflet-zoom-box",this._container),de(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var e=new j(this._point,this._startPoint),n=e.getSize();ye(this._box,e.min),this._box.style.width=n.x+"px",this._box.style.height=n.y+"px"},_finish:function(){this._moved&&(se(this._box),pe(this._container,"leaflet-crosshair")),Yt(),Ee(),Ae(document,{contextmenu:De,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(i(this._resetState,this),0);var e=new N(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(e).fire("boxzoomend",{boxZoomBounds:e})}},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}});Ke.addInitHook("addHandler","boxZoom",gi),Ke.mergeOptions({doubleClickZoom:!0});var yi=en.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,n=e.getZoom(),i=e.options.zoomDelta,o=t.originalEvent.shiftKey?n-i:n+i;"center"===e.options.doubleClickZoom?e.setZoom(o):e.setZoomAround(t.containerPoint,o)}});Ke.addInitHook("addHandler","doubleClickZoom",yi),Ke.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var bi=en.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new rn(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}de(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){pe(this._map._container,"leaflet-grab"),pe(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var e=Z(this._map.options.maxBounds);this._offsetLimit=z(this._map.latLngToContainerPoint(e.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(e.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var e=this._lastTime=+new Date,n=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(n),this._times.push(e),this._prunePositions(e)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,e){return t-(t-e)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),e=this._offsetLimit;t.xe.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),n=this._initialWorldOffset,i=this._draggable._newPos.x,o=(i-e+n)%t+e-n,r=(i+e+n)%t-e-n,a=Math.abs(o+n)0?r:-r))-e;this._delta=0,this._startTime=null,a&&("center"===t.options.scrollWheelZoom?t.setZoom(e+a):t.setZoomAround(this._lastMousePos,e+a))}});Ke.addInitHook("addHandler","scrollWheelZoom",xi),Ke.mergeOptions({tapHold:zt.touchNative&&zt.safari&&zt.mobile,tapTolerance:15});var Ei=en.extend({addHooks:function(){Se(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){Ae(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(clearTimeout(this._holdTimeout),1===t.touches.length){var e=t.touches[0];this._startPos=this._newPos=new R(e.clientX,e.clientY),this._holdTimeout=setTimeout(i((function(){this._cancel(),this._isTapValid()&&(Se(document,"touchend",Be),Se(document,"touchend touchcancel",this._cancelClickPrevent),this._simulateEvent("contextmenu",e))}),this),600),Se(document,"touchend touchcancel contextmenu",this._cancel,this),Se(document,"touchmove",this._onMove,this)}},_cancelClickPrevent:function t(){Ae(document,"touchend",Be),Ae(document,"touchend touchcancel",t)},_cancel:function(){clearTimeout(this._holdTimeout),Ae(document,"touchend touchcancel contextmenu",this._cancel,this),Ae(document,"touchmove",this._onMove,this)},_onMove:function(t){var e=t.touches[0];this._newPos=new R(e.clientX,e.clientY)},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_simulateEvent:function(t,e){var n=new MouseEvent(t,{bubbles:!0,cancelable:!0,view:window,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY});n._simulated=!0,e.target.dispatchEvent(n)}});Ke.addInitHook("addHandler","tapHold",Ei),Ke.mergeOptions({touchZoom:zt.touch,bounceAtZoomLimits:!0});var Li=en.extend({addHooks:function(){de(this._map._container,"leaflet-touch-zoom"),Se(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){pe(this._map._container,"leaflet-touch-zoom"),Ae(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var e=this._map;if(t.touches&&2===t.touches.length&&!e._animatingZoom&&!this._zooming){var n=e.mouseEventToContainerPoint(t.touches[0]),i=e.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=e.getSize()._divideBy(2),this._startLatLng=e.containerPointToLatLng(this._centerPoint),"center"!==e.options.touchZoom&&(this._pinchStartLatLng=e.containerPointToLatLng(n.add(i)._divideBy(2))),this._startDist=n.distanceTo(i),this._startZoom=e.getZoom(),this._moved=!1,this._zooming=!0,e._stop(),Se(document,"touchmove",this._onTouchMove,this),Se(document,"touchend touchcancel",this._onTouchEnd,this),Be(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var e=this._map,n=e.mouseEventToContainerPoint(t.touches[0]),o=e.mouseEventToContainerPoint(t.touches[1]),r=n.distanceTo(o)/this._startDist;if(this._zoom=e.getScaleZoom(r,this._startZoom),!e.options.bounceAtZoomLimits&&(this._zoome.getMaxZoom()&&r>1)&&(this._zoom=e._limitZoom(this._zoom)),"center"===e.options.touchZoom){if(this._center=this._startLatLng,1===r)return}else{var a=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===r&&0===a.x&&0===a.y)return;this._center=e.unproject(e.project(this._pinchStartLatLng,this._zoom).subtract(a),this._zoom)}this._moved||(e._moveStart(!0,!1),this._moved=!0),M(this._animRequest);var s=i(e._move,e,this._center,this._zoom,{pinch:!0,round:!1},void 0);this._animRequest=T(s,this,!0),Be(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,M(this._animRequest),Ae(document,"touchmove",this._onTouchMove,this),Ae(document,"touchend touchcancel",this._onTouchEnd,this),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});Ke.addInitHook("addHandler","touchZoom",Li),Ke.BoxZoom=gi,Ke.DoubleClickZoom=yi,Ke.Drag=bi,Ke.Keyboard=wi,Ke.ScrollWheelZoom=xi,Ke.TapHold=Ei,Ke.TouchZoom=Li,t.Bounds=j,t.Browser=zt,t.CRS=F,t.Canvas=ui,t.Circle=Nn,t.CircleMarker=zn,t.Class=S,t.Control=Ge,t.DivIcon=oi,t.DivOverlay=ei,t.DomEvent=Ve,t.DomUtil=Oe,t.Draggable=rn,t.Evented=A,t.FeatureGroup=kn,t.GeoJSON=Dn,t.GridLayer=ri,t.Handler=en,t.Icon=An,t.ImageOverlay=Qn,t.LatLng=B,t.LatLngBounds=N,t.Layer=On,t.LayerGroup=Sn,t.LineUtil=wn,t.Map=Ke,t.Marker=In,t.Mixin=nn,t.Path=jn,t.Point=R,t.PolyUtil=un,t.Polygon=Bn,t.Polyline=Zn,t.Popup=ni,t.PosAnimation=qe,t.Projection=Ln,t.Rectangle=vi,t.Renderer=ci,t.SVG=mi,t.SVGOverlay=ti,t.TileLayer=ai,t.Tooltip=ii,t.Transformation=V,t.Util=O,t.VideoOverlay=$n,t.bind=i,t.bounds=z,t.canvas=hi,t.circle=function(t,e,n){return new Nn(t,e,n)},t.circleMarker=function(t,e){return new zn(t,e)},t.control=Ye,t.divIcon=function(t){return new oi(t)},t.extend=e,t.featureGroup=function(t,e){return new kn(t,e)},t.geoJSON=Yn,t.geoJson=Xn,t.gridLayer=function(t){return new ri(t)},t.icon=function(t){return new An(t)},t.imageOverlay=function(t,e,n){return new Qn(t,e,n)},t.latLng=D,t.latLngBounds=Z,t.layerGroup=function(t,e){return new Sn(t,e)},t.map=function(t,e){return new Ke(t,e)},t.marker=function(t,e){return new In(t,e)},t.point=I,t.polygon=function(t,e){return new Bn(t,e)},t.polyline=function(t,e){return new Zn(t,e)},t.popup=function(t,e){return new ni(t,e)},t.rectangle=function(t,e){return new vi(t,e)},t.setOptions=p,t.stamp=r,t.svg=_i,t.svgOverlay=function(t,e,n){return new ti(t,e,n)},t.tileLayer=si,t.tooltip=function(t,e){return new ii(t,e)},t.transformation=q,t.version="1.9.4",t.videoOverlay=function(t,e,n){return new $n(t,e,n)};var Pi=window.L;t.noConflict=function(){return window.L=Pi,this},window.L=t},"object"===a(e)?r(e):(i=[e],void 0===(o="function"==typeof(n=r)?n.apply(e,i):n)||(t.exports=o))},769:(t,e,n)=>{"use strict";var i=n(533);e.s=i.createRoot,i.hydrateRoot},93:(t,e,n)=>{"use strict";n.d(e,{Z:()=>s});var i=n(251),o=n.n(i),r=n(922),a=n.n(r)()(o());a.push([t.id,".leaflet-contextmenu {\n display: none;\n box-shadow: 0 1px 7px rgba(0,0,0,0.4);\n -webkit-border-radius: 4px;\n border-radius: 4px;\n padding: 4px 0;\n background-color: #fff;\n cursor: default;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item {\n display: block;\n color: #222;\n font-size: 12px;\n line-height: 20px;\n text-decoration: none;\n padding: 0 12px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n cursor: default;\n outline: none;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item-disabled {\n opacity: 0.5;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item.over {\n background-color: #f4f4f4;\n border-top: 1px solid #f0f0f0;\n border-bottom: 1px solid #f0f0f0;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item-disabled.over {\n background-color: inherit;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n}\n\n.leaflet-contextmenu-icon {\n margin: 2px 8px 0 0;\n width: 16px;\n height: 16px;\n float: left;\n border: 0;\n}\n\n.leaflet-contextmenu-separator {\n border-bottom: 1px solid #ccc;\n margin: 5px 0;\n}\n","",{version:3,sources:["webpack://./node_modules/leaflet-contextmenu/dist/leaflet.contextmenu.css"],names:[],mappings:"AAAA;IACI,aAAa;IACb,qCAAqC;IACrC,0BAA0B;IAC1B,kBAAkB;IAClB,cAAc;IACd,sBAAsB;IACtB,eAAe;IACf,yBAAyB;IACzB,sBAAsB;IACtB,iBAAiB;AACrB;;AAEA;IACI,cAAc;IACd,WAAW;IACX,eAAe;IACf,iBAAiB;IACjB,qBAAqB;IACrB,eAAe;IACf,iCAAiC;IACjC,oCAAoC;IACpC,eAAe;IACf,aAAa;AACjB;;AAEA;IACI,YAAY;AAChB;;AAEA;IACI,yBAAyB;IACzB,6BAA6B;IAC7B,gCAAgC;AACpC;;AAEA;IACI,yBAAyB;IACzB,iCAAiC;IACjC,oCAAoC;AACxC;;AAEA;IACI,mBAAmB;IACnB,WAAW;IACX,YAAY;IACZ,WAAW;IACX,SAAS;AACb;;AAEA;IACI,6BAA6B;IAC7B,aAAa;AACjB",sourcesContent:[".leaflet-contextmenu {\n display: none;\n box-shadow: 0 1px 7px rgba(0,0,0,0.4);\n -webkit-border-radius: 4px;\n border-radius: 4px;\n padding: 4px 0;\n background-color: #fff;\n cursor: default;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item {\n display: block;\n color: #222;\n font-size: 12px;\n line-height: 20px;\n text-decoration: none;\n padding: 0 12px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n cursor: default;\n outline: none;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item-disabled {\n opacity: 0.5;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item.over {\n background-color: #f4f4f4;\n border-top: 1px solid #f0f0f0;\n border-bottom: 1px solid #f0f0f0;\n}\n\n.leaflet-contextmenu a.leaflet-contextmenu-item-disabled.over {\n background-color: inherit;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n}\n\n.leaflet-contextmenu-icon {\n margin: 2px 8px 0 0;\n width: 16px;\n height: 16px;\n float: left;\n border: 0;\n}\n\n.leaflet-contextmenu-separator {\n border-bottom: 1px solid #ccc;\n margin: 5px 0;\n}\n"],sourceRoot:""}]);const s=a},379:t=>{"use strict";var e=[];function n(t){for(var n=-1,i=0;i{"use strict";var e={};t.exports=function(t,n){var i=function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(t){n=null}e[t]=n}return e[t]}(t);if(!i)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");i.appendChild(n)}},216:t=>{"use strict";t.exports=function(t){var e=document.createElement("style");return t.setAttributes(e,t.attributes),t.insert(e,t.options),e}},636:(t,e,n)=>{"use strict";t.exports=function(t){var e=n.nc;e&&t.setAttribute("nonce",e)}},795:t=>{"use strict";t.exports=function(t){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var e=t.insertStyleElement(t);return{update:function(n){!function(t,e,n){var i="";n.supports&&(i+="@supports (".concat(n.supports,") {")),n.media&&(i+="@media ".concat(n.media," {"));var o=void 0!==n.layer;o&&(i+="@layer".concat(n.layer.length>0?" ".concat(n.layer):""," {")),i+=n.css,o&&(i+="}"),n.media&&(i+="}"),n.supports&&(i+="}");var r=n.sourceMap;r&&"undefined"!=typeof btoa&&(i+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),e.styleTagTransform(i,t,e.options)}(e,t,n)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},589:t=>{"use strict";t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}},533:t=>{"use strict";t.exports=ReactDOM}},e={};function n(i){var o=e[i];if(void 0!==o)return o.exports;var r=e[i]={id:i,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var i in e)n.o(e,i)&&!n.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.nc=void 0,(()=>{"use strict";const t=React;var e=n.n(t),i=n(769);function o(t){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o(t)}function r(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n");return e}var m=n(208),_=["bounds","boundsOptions","center","children","className","id","placeholder","style","whenReady","zoom"];function v(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var i,o,r,a,s=[],l=!0,c=!1;try{if(r=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;l=!1}else for(;!(l=(i=r.call(n)).done)&&(s.push(i.value),s.length!==e);l=!0);}catch(t){c=!0,o=t}finally{try{if(!l&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return s}}(t,e)||function(t,e){if(t){if("string"==typeof t)return g(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?g(t,e):void 0}}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function g(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}(e,_),g=v((0,t.useState)({className:s,id:l,style:u}),1)[0],b=v((0,t.useState)(null),2),w=b[0],x=b[1];(0,t.useImperativeHandle)(n,(function(){var t;return null!==(t=null==w?void 0:w.map)&&void 0!==t?t:null}),[w]);var E=(0,t.useCallback)((function(t){if(null!==t&&null===w){var e=new m.Map(t,f);null!=r&&null!=d?e.setView(r,d):null!=i&&e.fitBounds(i,o),null!=h&&e.whenReady(h),x(function(t){return Object.freeze({__version:1,map:t})}(e))}}),[]);(0,t.useEffect)((function(){return function(){null==w||w.map.remove()}}),[w]);var L=w?t.createElement(p,{value:w},a):null!=c?c:null;return t.createElement("div",y({},g,{ref:E}),L)}var w=(0,t.forwardRef)(b),x=n(533);function E(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}(t,j);return T(new m.TileLayer(n,C(i,e)),e)},P(I(M(z,(function(t,e,n){!function(t,e,n){var i=e.opacity,o=e.zIndex;null!=i&&i!==n.opacity&&t.setOpacity(i),null!=o&&o!==n.zIndex&&t.setZIndex(o)}(t,e,n);var i=e.url;null!=i&&i!==n.url&&t.setUrl(i)}))))),D=(Z=function(t){return new m.Control.Zoom(t)},P((N=M((function(t,e){return T(Z(t),e)})),function(e){var n=f(),i=N(e,n),o=i.current.instance,r=(0,t.useRef)(e.position),a=e.position;return(0,t.useEffect)((function(){return o.addTo(n.map),function(){o.remove()}}),[n.map,o]),(0,t.useEffect)((function(){null!=a&&a!==r.current&&(o.setPosition(a),r.current=a)}),[o,a]),i})));function H(t){return H="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},H(t)}function F(){F=function(){return e};var t,e={},n=Object.prototype,i=n.hasOwnProperty,o=Object.defineProperty||function(t,e,n){t[e]=n.value},r="function"==typeof Symbol?Symbol:{},a=r.iterator||"@@iterator",s=r.asyncIterator||"@@asyncIterator",l=r.toStringTag||"@@toStringTag";function c(t,e,n){return Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{c({},"")}catch(t){c=function(t,e,n){return t[e]=n}}function u(t,e,n,i){var r=e&&e.prototype instanceof v?e:v,a=Object.create(r.prototype),s=new k(i||[]);return o(a,"_invoke",{value:T(t,n,s)}),a}function h(t,e,n){try{return{type:"normal",arg:t.call(e,n)}}catch(t){return{type:"throw",arg:t}}}e.wrap=u;var d="suspendedStart",p="suspendedYield",f="executing",m="completed",_={};function v(){}function g(){}function y(){}var b={};c(b,a,(function(){return this}));var w=Object.getPrototypeOf,x=w&&w(w(A([])));x&&x!==n&&i.call(x,a)&&(b=x);var E=y.prototype=v.prototype=Object.create(b);function L(t){["next","throw","return"].forEach((function(e){c(t,e,(function(t){return this._invoke(e,t)}))}))}function P(t,e){function n(o,r,a,s){var l=h(t[o],t,r);if("throw"!==l.type){var c=l.arg,u=c.value;return u&&"object"==H(u)&&i.call(u,"__await")?e.resolve(u.__await).then((function(t){n("next",t,a,s)}),(function(t){n("throw",t,a,s)})):e.resolve(u).then((function(t){c.value=t,a(c)}),(function(t){return n("throw",t,a,s)}))}s(l.arg)}var r;o(this,"_invoke",{value:function(t,i){function o(){return new e((function(e,o){n(t,i,e,o)}))}return r=r?r.then(o,o):o()}})}function T(e,n,i){var o=d;return function(r,a){if(o===f)throw new Error("Generator is already running");if(o===m){if("throw"===r)throw a;return{value:t,done:!0}}for(i.method=r,i.arg=a;;){var s=i.delegate;if(s){var l=M(s,i);if(l){if(l===_)continue;return l}}if("next"===i.method)i.sent=i._sent=i.arg;else if("throw"===i.method){if(o===d)throw o=m,i.arg;i.dispatchException(i.arg)}else"return"===i.method&&i.abrupt("return",i.arg);o=f;var c=h(e,n,i);if("normal"===c.type){if(o=i.done?m:p,c.arg===_)continue;return{value:c.arg,done:i.done}}"throw"===c.type&&(o=m,i.method="throw",i.arg=c.arg)}}}function M(e,n){var i=n.method,o=e.iterator[i];if(o===t)return n.delegate=null,"throw"===i&&e.iterator.return&&(n.method="return",n.arg=t,M(e,n),"throw"===n.method)||"return"!==i&&(n.method="throw",n.arg=new TypeError("The iterator does not provide a '"+i+"' method")),_;var r=h(o,e.iterator,n.arg);if("throw"===r.type)return n.method="throw",n.arg=r.arg,n.delegate=null,_;var a=r.arg;return a?a.done?(n[e.resultName]=a.value,n.next=e.nextLoc,"return"!==n.method&&(n.method="next",n.arg=t),n.delegate=null,_):a:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,_)}function O(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function S(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function k(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(O,this),this.reset(!0)}function A(e){if(e||""===e){var n=e[a];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,r=function n(){for(;++o=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var l=i.call(a,"catchLoc"),c=i.call(a,"finallyLoc");if(l&&c){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&i.call(o,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),S(n),_}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var i=n.completion;if("throw"===i.type){var o=i.arg;S(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,i){return this.delegate={iterator:A(e),resultName:n,nextLoc:i},"next"===this.method&&(this.arg=t),_}},e}function U(t,e,n,i,o,r,a){try{var s=t[r](a),l=s.value}catch(t){return void n(t)}s.done?e(l):Promise.resolve(l).then(i,o)}function W(t){return function(){var e=this,n=arguments;return new Promise((function(i,o){var r=t.apply(e,n);function a(t){U(r,i,o,a,s,"next",t)}function s(t){U(r,i,o,a,s,"throw",t)}a(void 0)}))}}var J=function(){var t=W(F().mark((function t(e,n){var i,o,r;return F().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return i="https://nominatim.openstreetmap.org/reverse?lat="+e+"&lon="+n+"&format=json",t.next=3,fetch(i);case 3:return o=t.sent,t.next=6,o.json();case 6:return r=t.sent,t.abrupt("return",r);case 8:case"end":return t.stop()}}),t)})));return function(e,n){return t.apply(this,arguments)}}(),V=function(){var t=W(F().mark((function t(e){var n,i,o;return F().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n="https://nominatim.openstreetmap.org/search?q="+e+"&format=json",t.next=3,fetch(n);case 3:return i=t.sent,t.next=6,i.json();case 6:return o=t.sent,t.abrupt("return",o);case 8:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}();function q(){return q=Object.assign?Object.assign.bind():function(t){for(var e=1;e=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var l=i.call(a,"catchLoc"),c=i.call(a,"finallyLoc");if(l&&c){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&i.call(o,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),S(n),_}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var i=n.completion;if("throw"===i.type){var o=i.arg;S(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,i){return this.delegate={iterator:A(e),resultName:n,nextLoc:i},"next"===this.method&&(this.arg=t),_}},e}function Q(t,e,n,i,o,r,a){try{var s=t[r](a),l=s.value}catch(t){return void n(t)}s.done?e(l):Promise.resolve(l).then(i,o)}function $(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var i,o,r,a,s=[],l=!0,c=!1;try{if(r=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;l=!1}else for(;!(l=(i=r.call(n)).done)&&(s.push(i.value),s.length!==e);l=!0);}catch(t){c=!0,o=t}finally{try{if(!l&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return s}}(t,e)||function(t,e){if(t){if("string"==typeof t)return tt(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?tt(t,e):void 0}}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function tt(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n0)){t.next=6;break}return t.next=3,V(x);case 3:null!=(e=t.sent)&&e.length&&M(e),b(!1);case 6:case"end":return t.stop()}}),t)})),function(){var e=this,n=arguments;return new Promise((function(i,o){var r=t.apply(e,n);function a(t){Q(r,i,o,a,s,"next",t)}function s(t){Q(r,i,o,a,s,"throw",t)}a(void 0)}))});return function(){return e.apply(this,arguments)}}();return(0,t.useEffect)((function(){if(S){if(""===x)return E(""),M([]),void b(!1);if(_){b(!0);var t=setTimeout(R,300);return function(){return clearTimeout(t)}}v(!0)}}),[x]),(0,t.useEffect)((function(){k(!0);var t=function(t){var e;t.stopPropagation(),A.current&&(null===(e=L)||void 0===e||null===(e=e.DomEvent)||void 0===e?void 0:e.disableClickPropagation)(A.current)};return A.current&&(A.current.addEventListener("mousewheel",t),A.current.addEventListener("mouseover",t),A.current.addEventListener("mouseout",t)),function(){A.current&&(A.current.removeEventListener("mousewheel",t),A.current.removeEventListener("mouseover",t),A.current.removeEventListener("mouseout",t))}}),[A]),React.createElement("div",{className:r,ref:A},React.createElement("div",{className:"tf-map-editor--app--search--box"},React.createElement("input",Y({type:"text","data-1p-ignore":!0,placeholder:p},s?s={id:s}:"",{autoComplete:"false",autoFocus:c,value:x,onChange:function(t){return E(t.target.value)},onPaste:function(t){v(!0)}})),T.length>0?React.createElement("svg",{onClick:function(){return E(""),M([]),void b(!1)},className:"tf-clear-search-term",xmlns:"http://www.w3.org/2000/svg",height:"24",viewBox:"0 -960 960 960",width:"24"},React.createElement("path",{d:"M256-213.847 213.847-256l224-224-224-224L256-746.153l224 224 224-224L746.153-704l-224 224 224 224L704-213.847l-224-224-224 224Z"})):y?React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",height:"20",viewBox:"0 -960 960 960",width:"20",className:"tf-loading-anim"},React.createElement("path",{d:"M479.878-100.001q-78.032 0-147.323-29.9-69.291-29.9-121.023-81.631-51.731-51.732-81.631-121.023t-29.9-147.323q0-78.984 29.962-147.976 29.961-68.991 81.576-120.607 51.616-51.615 121.044-81.576Q402.012-859.999 480-859.999q12.75 0 21.374 8.629 8.625 8.629 8.625 21.384 0 12.756-8.625 21.371Q492.75-800 480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160q133 0 226.5-93.5T800-480q0-12.769 8.629-21.384 8.628-8.615 21.384-8.615 12.755 0 21.37 8.625 8.616 8.624 8.616 21.374 0 77.988-29.962 147.417-29.961 69.428-81.576 121.044-51.616 51.615-120.607 81.576-68.992 29.962-147.976 29.962Z"})):React.createElement(K,{width:"24",height:"24"})),T.length>0&&React.createElement("div",{className:"tf-map-editor--app--search--results"},T.map((function(t,e){return React.createElement("div",{key:e,className:"tf-map-editor--app--search--results--item",onClick:function(){return function(t){E(h?null==t?void 0:t.display_name:""),v(!1),M([]),b(!1),f(t)}(t)}},t.display_name)}))))};function nt(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);nt.length)&&(e=t.length);for(var n=0,i=new Array(e);n0&&r.length===r.filter((function(t){return t.isSelected})).length,h=r.filter((function(t){return t.isSelected})).length>0;return React.createElement("div",{className:"tf-map-editor--app--sidebar--filters"},React.createElement("div",{className:"tf-map-editor--app--sidebar--filters--checks"},u?React.createElement(React.Fragment,null,React.createElement(_t,{className:"is-checked",onClick:function(){l()}})):React.createElement(gt,{onClick:function(){s()}}),!n&&h&&React.createElement(xt,{onClick:c})),!u&&!h&&React.createElement("div",{className:"tf-map-editor--app--sidebar--filters--searchbox"},React.createElement("input",{type:"text",value:i,onChange:function(t){return a(t.target.value)},placeholder:"Search"}),React.createElement(K,{width:"20",height:"20"})))};function Ot(){return Ot=Object.assign?Object.assign.bind():function(t){for(var e=1;e=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var l=i.call(a,"catchLoc"),c=i.call(a,"finallyLoc");if(l&&c){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&i.call(o,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),S(n),_}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var i=n.completion;if("throw"===i.type){var o=i.arg;S(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,i){return this.delegate={iterator:A(e),resultName:n,nextLoc:i},"next"===this.method&&(this.arg=t),_}},e}function Dt(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!n){if(Array.isArray(t)||(n=Ut(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var i=0,o=function(){};return{s:o,n:function(){return i>=t.length?{done:!0}:{done:!1,value:t[i++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,a=!0,s=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return a=t.done,t},e:function(t){s=!0,r=t},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw r}}}}function Ht(t,e,n,i,o,r,a){try{var s=t[r](a),l=s.value}catch(t){return void n(t)}s.done?e(l):Promise.resolve(l).then(i,o)}function Ft(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var i,o,r,a,s=[],l=!0,c=!1;try{if(r=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;l=!1}else for(;!(l=(i=r.call(n)).done)&&(s.push(i.value),s.length!==e);l=!0);}catch(t){c=!0,o=t}finally{try{if(!l&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return s}}(t,e)||Ut(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Ut(t,e){if(t){if("string"==typeof t)return Wt(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Wt(t,e):void 0}}function Wt(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n=s)){t.next=31;break}return t.abrupt("break",36);case 31:l.preventAutoPan=!0,Z({type:"ADD_MARKER",payload:l}),n++;case 34:t.next=9;break;case 36:t.next=41;break;case 38:t.prev=38,t.t0=t.catch(7),i.e(t.t0);case 41:return t.prev=41,i.f(),t.finish(41);case 44:g(!1),0===n?w(""===L?Joomla.JText._("NR_PLEASE_ENTER_LOCATIONS_TO_IMPORT"):Joomla.JText._("NR_COULDNT_IMPORT_LOCATIONS")):(P(""),I(!1)),m(F),Z({type:"FIT_BOUNDS",payload:!0}),O({total:e.length,added:n}),t.next=57;break;case 51:t.prev=51,t.t1=t.catch(0),console.error("Error:",t.t1),g(!1),w("An unexpected error occurred."),m(F);case 57:case"end":return t.stop()}var _}),t,null,[[0,51],[7,38,41,44]])})),function(){var e=this,n=arguments;return new Promise((function(i,o){var r=t.apply(e,n);function a(t){Ht(r,i,o,a,s,"next",t)}function s(t){Ht(r,i,o,a,s,"throw",t)}a(void 0)}))});return function(){return e.apply(this,arguments)}}(),q=(0,x.createPortal)(React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal"},React.createElement("div",{ref:H,className:"tf-map-editor--app--sidebar--markers--edit-modal--inner"},React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--top"},React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--top--title"},Joomla.JText._("NR_IMPORT_MARKERS")),React.createElement(Nt,{width:30,height:30,onClick:U})),b&&React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--error"},b),React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--content"},React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--content--field"},React.createElement("textarea",{autoFocus:!0,rows:12,onChange:function(t){return e=t.target.value,void P(e);var e},value:L}),React.createElement("div",{className:"tf-map-editor-example-formats"},React.createElement("div",null,Joomla.JText._("NR_IMPORT_LOCATIONS_DESC")),React.createElement("div",{className:"tf-map-editor-example-format"},React.createElement("div",null,"Kos, Greece"),React.createElement("div",null,"Crete, Greece"),React.createElement("div",null,"Athens, Greece")),React.createElement("div",{className:"tf-map-editor-example-format"},React.createElement("div",null,"36.89618297913001, 27.287810880738398"),React.createElement("div",null,"35.255194302351185, 24.785314037527865"),React.createElement("div",null,"37.987682611306575, 23.721270133629385")),React.createElement("div",null,Joomla.JText._("NR_IMPORT_LOCATIONS_DESC2"))))),React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--footer"},v?React.createElement("button",{disabled:!0},Joomla.JText._("NR_ADDING_MARKERS")):React.createElement("button",{onClick:W},Joomla.JText._("NR_IMPORT"))))),document.body),K=(0,x.createPortal)(React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal"},React.createElement("div",{ref:D,className:"tf-map-editor--app--sidebar--markers--edit-modal--inner"},React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--top"},React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--top--title"},Joomla.JText._("NR_EXPORT_MARKERS")),React.createElement(Nt,{width:30,height:30,onClick:U})),React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--content"},React.createElement("div",{className:"tf-map-editor--app--sidebar--markers--edit-modal--inner--content--field"},null!=i&&i.length?React.createElement("div",{className:"tf-map-editor-example-formats"},React.createElement("textarea",{rows:12,value:null==i?void 0:i.map((function(t){return[null==t?void 0:t.latitude,t.longitude].join(",")})).join("\n")}),React.createElement("div",null,Joomla.JText._("NR_EXPORT_MARKERS_DESC"))):React.createElement("div",null,Joomla.JText._("NR_THERE_ARE_NO_LOCATIONS_TO_EXPORT")))))),document.body);return(0,t.useEffect)((function(){function t(t){H.current&&!H.current.contains(t.target)&&I(!1),D.current&&!D.current.contains(t.target)&&A(!1),B.current&&!B.current.contains(t.target)&&N(!1)}return document.addEventListener("mousedown",t),function(){document.removeEventListener("mousedown",t)}}),[H,D,B]),React.createElement(React.Fragment,null,React.createElement("div",{className:"tf-map-editor--app--sidebar--top"},React.createElement("div",{className:"tf-map-editor--app--sidebar--top--title"},Joomla.JText._("NR_MARKERS"),null!==M&&React.createElement("div",{className:"tf-map-editor--app--sidebar--top--title--stats"},M.added,"/",M.total," ",Joomla.JText._("NR_LOCATIONS_IMPORTED"),React.createElement(Nt,{width:16,height:16,onClick:function(){return O(null)}}))),React.createElement("div",{className:"tf-map-editor--app--sidebar--top--icons"},!c&&React.createElement(At,{onClick:u}),c&&React.createElement(jt,null),!c&&f&&React.createElement("div",{className:"tf-map-editor--app--sidebar--top--icons--action",ref:B},React.createElement(Ct,{onClick:function(){r||N((function(t){return!t}))}}),z&&React.createElement("ul",{className:"tf-map-editor--app--sidebar--top--icons--action--list"},d?React.createElement(React.Fragment,null,React.createElement("li",{onClick:function(){I(!0),N(!1)}},Joomla.JText._("NR_IMPORT_MARKERS")),(null==i?void 0:i.length)>0&&React.createElement("li",{onClick:function(){A(!0),N(!1)}},Joomla.JText._("NR_EXPORT_MARKERS"))):React.createElement(React.Fragment,null,React.createElement("li",{className:"is-pro","data-pro-only":Joomla.JText._("NR_IMPORT_MARKERS")},Joomla.JText._("NR_IMPORT_MARKERS"),React.createElement(St,null)),React.createElement("li",{className:"is-pro","data-pro-only":Joomla.JText._("NR_EXPORT_MARKERS")},Joomla.JText._("NR_EXPORT_MARKERS"),React.createElement(St,null))))))),C&&q,k&&K)},Vt=function(){return React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",height:"22",viewBox:"0 -960 960 960",width:"22"},React.createElement("path",{d:"M252.309-619.999H600v-80q0-50-35-85t-85-35q-50 0-85 35t-35 85h-59.999q0-74.922 52.538-127.46Q405.078-879.997 480-879.997t127.461 52.538q52.538 52.538 52.538 127.46v80h47.692q29.923 0 51.115 21.193 21.193 21.192 21.193 51.115v375.382q0 29.923-21.193 51.115-21.192 21.193-51.115 21.193H252.309q-29.923 0-51.115-21.193-21.193-21.192-21.193-51.115v-375.382q0-29.923 21.193-51.115 21.192-21.193 51.115-21.193Zm0 459.999h455.382q5.385 0 8.847-3.462 3.462-3.462 3.462-8.847v-375.382q0-5.385-3.462-8.847-3.462-3.462-8.847-3.462H252.309q-5.385 0-8.847 3.462-3.462 3.462-3.462 8.847v375.382q0 5.385 3.462 8.847 3.462 3.462 8.847 3.462ZM480-290.001q29.154 0 49.576-20.423 20.423-20.422 20.423-49.576t-20.423-49.576Q509.154-429.999 480-429.999t-49.576 20.423Q410.001-389.154 410.001-360t20.423 49.576q20.422 20.423 49.576 20.423ZM240-160v-400 400Z",fill:"currentColor"}))};function qt(t){return qt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},qt(t)}function Kt(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,i)}return n}function Gt(t){for(var e=1;et.length)&&(e=t.length);for(var n=0,i=new Array(e);nt.length)&&(e=t.length);for(var n=0,i=new Array(e);n0&&React.createElement(Mt,{readonly:w,value:_,markers:u,onDeleteAllSelectedMarkers:function(){confirm(Joomla.JText._("NR_ARE_YOU_SURE_YOU_WANT_TO_DELETE_ALL_SELECTED_MARKERS"))&&(E({type:"DELETE_ALL_SELECTED_MARKERS"}),O(M))},onSelectAll:function(){E({type:"SELECT_ALL_MARKERS",payload:g.map((function(t){return t.id}))})},onUnselectAll:function(){E({type:"UNSELECT_ALL_MARKERS"})},onChange:function(t){v(t)}}),!b&&React.createElement(Tt,{markers:g,onSelectMarker:function(t,e){t.stopPropagation(),E({type:"SELECT_MARKER",payload:e})},onUnselectMarker:function(t,e){t.stopPropagation(),E({type:"UNSELECT_MARKER",payload:e})},onViewMarker:function(t){E({type:"VIEW_MARKER",payload:t})},onEditMarker:function(t,e){t.stopPropagation(),E({type:"EDIT_MARKER",payload:e})},onDeleteMarker:function(t,e){t.stopPropagation(),confirm(Joomla.JText._("NR_ARE_YOU_SURE_YOU_WANT_TO_DELETE_THIS_MARKER"))&&E({type:"DELETE_MARKER",payload:e})}}),!b&&!L&&React.createElement("a",{href:"#",className:"tf-map-editor--app--sidebar--pro-cta","data-pro-only":Joomla.JText._("NR_UNLIMITED_MARKERS")},React.createElement(St,null),React.createElement(Vt,null),Joomla.JText._("NR_ADD_MORE_MARKERS_UPGRADE_TO_PRO")),!b&&0!==l&&(null==u?void 0:u.length)>0&&React.createElement("div",{className:"tf-map-editor--app--sidebar--total-used"},(null==u||null===(i=u.filter((function(t){return!(null!=t&&t.isTemporary)})))||void 0===i?void 0:i.length)||0," ",Joomla.Text._("NR_OUT_OF")," ",l," ",Joomla.Text._("NR_MARKERS_ADDED"))),d&&React.createElement(Qt,{onCloseModal:a,onSaveMarker:function(t){""!==(null==t?void 0:t.latitude)||""!==(null==t?void 0:t.longitude)?E({type:"ADD_MARKER",payload:t}):E({type:"CLOSE_MARKER_MODAL"})}}))},ee=function(){var t=pt().readonly,e=ft();return React.createElement(et,{className:"tf-map-editor--app--search",onSelectResult:function(n){var i=n.lat,o=n.lon,r=n.display_name;t||e({type:"ADD_TMP_MARKER",payload:{latitude:i,longitude:o,address:r,description:""}})}})};var ne=["position"];var ie,oe=function(e){function n(n,i){var o=e(n).current,r=o.instance,a=o.context;return(0,t.useImperativeHandle)(i,(function(){return r})),null==n.children?null:t.createElement(p,{value:a},n.children)}return(0,t.forwardRef)(n)}(I(M((function(t,e){var n,i,o=t.position,r=function(t,e){if(null==t)return{};var n,i,o=function(t,e){if(null==t)return{};var n,i,o={},r=Object.keys(t);for(i=0;i=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}(t,ne),a=new m.Marker(o,r);return T(a,(n=e,i={overlayContainer:a},Object.freeze(h(h({},n),i))))}),(function(t,e,n){e.position!==n.position&&t.setLatLng(e.position),null!=e.icon&&e.icon!==n.icon&&t.setIcon(e.icon),null!=e.zIndexOffset&&e.zIndexOffset!==n.zIndexOffset&&t.setZIndexOffset(e.zIndexOffset),null!=e.opacity&&e.opacity!==n.opacity&&t.setOpacity(e.opacity),null!=t.dragging&&e.draggable!==n.draggable&&(!0===e.draggable?t.dragging.enable():t.dragging.disable())})))),re=(ie=function(t,e){return function(n,i){var o=f(),r=t(C(n,o),o);return O(o.map,n.attribution),S(r.current,n.eventHandlers),e(r.current,o,n,i),r}}(M((function(t,e){return T(new m.Popup(t,e.overlayContainer),e)})),(function(e,n,i,o){var r=i.position;(0,t.useEffect)((function(){var t=e.instance;function i(e){e.popup===t&&(t.update(),o(!0))}function a(e){e.popup===t&&o(!1)}return n.map.on({popupopen:i,popupclose:a}),null==n.overlayContainer?(null!=r&&t.setLatLng(r),t.openOn(n.map)):n.overlayContainer.bindPopup(t),function(){var e;n.map.off({popupopen:i,popupclose:a}),null===(e=n.overlayContainer)||void 0===e||e.unbindPopup(),n.map.removeLayer(t)}}),[e,n,o,r])})),function(e){function n(n,i){var o,r,a=(o=(0,t.useState)(!1),r=2,function(t){if(Array.isArray(t))return t}(o)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var i,o,r,a,s=[],l=!0,c=!1;try{if(r=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;l=!1}else for(;!(l=(i=r.call(n)).done)&&(s.push(i.value),s.length!==e);l=!0);}catch(t){c=!0,o=t}finally{try{if(!l&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return s}}(o,r)||function(t,e){if(t){if("string"==typeof t)return E(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?E(t,e):void 0}}(o,r)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),s=a[0],l=a[1],c=e(n,l).current.instance;(0,t.useImperativeHandle)(i,(function(){return c})),(0,t.useEffect)((function(){s&&c.update()}),[c,s,n.children]);var u=c._contentNode;return u?(0,x.createPortal)(n.children,u):null}return(0,t.forwardRef)(n)}(ie));const ae=function(){return React.createElement("svg",{width:"22",height:"22",viewBox:"0 0 22 22",fill:"none",xmlns:"http://www.w3.org/2000/svg"},React.createElement("mask",{id:"mask0_211_595",style:{maskType:"alpha"},maskUnits:"userSpaceOnUse",x:"0",y:"0",width:"22",height:"22"},React.createElement("rect",{width:"22",height:"22",fill:"#D9D9D9"})),React.createElement("g",{mask:"url(#mask0_211_595)"},React.createElement("path",{d:"M10.0834 11.9167H4.58337V10.0833H10.0834V4.58333H11.9167V10.0833H17.4167V11.9167H11.9167V17.4167H10.0834V11.9167Z",fill:"currentColor"})))};function se(){return se=Object.assign?Object.assign.bind():function(t){for(var e=1;e=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var l=i.call(a,"catchLoc"),c=i.call(a,"finallyLoc");if(l&&c){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&i.call(o,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),S(n),_}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var i=n.completion;if("throw"===i.type){var o=i.arg;S(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,i){return this.delegate={iterator:A(e),resultName:n,nextLoc:i},"next"===this.method&&(this.arg=t),_}},e}function fe(t,e,n,i,o,r,a){try{var s=t[r](a),l=s.value}catch(t){return void n(t)}s.done?e(l):Promise.resolve(l).then(i,o)}function me(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,i)}return n}function _e(t){for(var e=1;et.length)&&(e=t.length);for(var n=0,i=new Array(e);n=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var l=i.call(a,"catchLoc"),c=i.call(a,"finallyLoc");if(l&&c){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&i.call(o,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),S(n),_}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var i=n.completion;if("throw"===i.type){var o=i.arg;S(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,i){return this.delegate={iterator:A(e),resultName:n,nextLoc:i},"next"===this.method&&(this.arg=t),_}},e}function je(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,i)}return n}function ze(t){for(var e=1;e0){var t=p||!h?{padding:[0,40]}:{paddingTopLeft:[520,80],padding:[0,40]};v.fitBounds(function(t){if(t)return t.map((function(t){return[t.latitude,t.longitude]}))}(i),t)}else{var e=null!=r&&r.lat?8:2;v.setView({lat:r.lat,lng:r.lng},e)}};if(function(e){var n=ut();(0,t.useEffect)((function(){return n.on(e),function(){n.off(e)}}),[n,e])}({popupopen:function(t){return setTimeout((function(){return t.popup.options.autoPan=!1}),100)},popupclose:function(t){return t.popup.options.autoPan=!0},resize:function(t){return _()}}),!c){var v=ut();return null!=r&&r.readonly&&v.contextmenu.disable(),_(),null}};const Be=function(){var t=pt(),e=t.markers,n=t.tmpMarker,i=t.viewMarker,o=t.editingMarker,r=t.options,a=t.preventAutoPan,s=t.addNewMarkerModal,l=t.minimizedSidebar,c=t.showSidebar,u=ft(),h=function(t,e){var n=null;return null!=t&&t.length&&(n=t),e&&(n=null===n?[e]:n.concat(e)),n}(e,n),d=function(){var t,e=(t=Ie().mark((function t(e){var n,i,o;return Ie().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n={id:Math.random().toString(36).substring(2,15),address:"...",latitude:e.latlng.lat,longitude:e.latlng.lng,preventAutoPan:!0,viewMarker:!0},u({type:"ADD_MARKER",payload:n}),t.next=4,J(e.latlng.lat,e.latlng.lng);case 4:null!=(i=t.sent)&&i.display_name&&(o=ze(ze({},n),{},{address:i.display_name,preventAutoPan:!0}),u({type:"SAVE_MARKER",payload:{marker:o}}));case 6:case"end":return t.stop()}}),t)})),function(){var e=this,n=arguments;return new Promise((function(i,o){var r=t.apply(e,n);function a(t){Ne(r,i,o,a,s,"next",t)}function s(t){Ne(r,i,o,a,s,"throw",t)}a(void 0)}))});return function(t){return e.apply(this,arguments)}}(),p=function(t){""!==(null==t?void 0:t.latitude)||""!==(null==t?void 0:t.longitude)?u({type:"SAVE_MARKER",payload:{marker:t}}):u({type:"CLOSE_MARKER_MODAL"})},f=function(){u({type:"CLOSE_MARKER_MODAL"})},m=[{text:Joomla.JText._("NR_ADD_MARKER"),callback:d}],_=parseInt(r.maxMarkers);return React.createElement(React.Fragment,null,React.createElement(w,{draggable:!0,zoomControl:!1,style:{height:"100%",width:"100%"},contextmenu:!0,contextmenuItems:m,closePopupOnClick:!1},React.createElement(Ze,{viewMarker:i,showSidebar:c,minimizedSidebar:l,preventAutoPan:o||a,markers:h,options:r}),React.createElement(B,{attribution:'© OpenStreetMap contributors',url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}),React.createElement("div",{className:"tf-map-editor-overlay-controls"},null!=r&&r.showSidebar?React.createElement(te,{onSaveMarker:p,onCloseModal:f,maxMarkersAllowed:_,markers:h,addNewMarkerModal:s,showImport:0===r.maxMarkers||r.maxMarkers>1}):React.createElement("div",null,React.createElement("div",{className:"tf-map-editor-empty-space"})),React.createElement(ee,null),React.createElement("div",null,React.createElement("div",{className:"tf-map-editor-empty-space"}))),React.createElement(ge,{maxMarkersAllowed:_,markers:h,viewingMarker:i||n,markerImage:r.markerImage}),React.createElement(D,{position:"bottomright"})),o&&React.createElement(ct,{onCloseModal:f,marker:e.find((function(t){return t.id===o})),onSaveMarker:p}))};function De(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n0?JSON.parse(n.valueElement.value):null,options:n.options,center:[0,0]};1===(null===(s=u.options)||void 0===s?void 0:s.maxMarkers)&&1===(null===(c=u.markers)||void 0===c?void 0:c.length)&&(u.minimizedSidebar=!0);var h,d,p=(h=(0,t.useReducer)(l,u),d=2,function(t){if(Array.isArray(t))return t}(h)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var i,o,r,a,s=[],l=!0,c=!1;try{if(r=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;l=!1}else for(;!(l=(i=r.call(n)).done)&&(s.push(i.value),s.length!==e);l=!0);}catch(t){c=!0,o=t}finally{try{if(!l&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return s}}(h,d)||function(t,e){if(t){if("string"==typeof t)return De(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?De(t,e):void 0}}(h,d)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),f=p[0],m=p[1];return(0,t.useEffect)((function(){n.valueElement.value=null!=f&&f.markers?JSON.stringify(f.markers):null}),[f]),e().createElement(ht.Provider,{value:f},e().createElement(dt.Provider,{value:m},e().createElement(Be,null)))}document.addEventListener("DOMContentLoaded",(function(){var t=new IntersectionObserver((function(t,n){t.forEach((function(t){if(t.isIntersecting){var o,r,a=(0,i.s)(t.target.querySelector(".tf-map-editor--app")),s=t.target.dataset.options?JSON.parse(t.target.dataset.options):{},l={lat:s.lat||0,lng:s.long||0,pro:s.pro||!1,maxMarkers:s.maxMarkers||0,markerImage:s.markerImage,showSidebar:null===(o=s.showSidebar)||void 0===o||o,readonly:null!==(r=s.readonly)&&void 0!==r&&r},c=t.target.nextElementSibling.classList.contains("tf-map-editor--value")?t.target.nextElementSibling:t.target.querySelector(".tf-map-editor--value");a.render(e().createElement(He,{valueElement:c,options:l})),n.unobserve(t.target)}}))}),{rootMargin:"0px 0px 0px 0px"});document.querySelectorAll(".tf-map-editor").forEach((function(e){t.observe(e)}))}))})()})(); diff --git a/media/plg_system_nrframework/js/mapeditor.js.LICENSE.txt b/media/plg_system_nrframework/js/mapeditor.js.LICENSE.txt new file mode 100644 index 00000000..4c68eb7c --- /dev/null +++ b/media/plg_system_nrframework/js/mapeditor.js.LICENSE.txt @@ -0,0 +1,6 @@ +/* @preserve + * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ + +/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ diff --git a/media/plg_system_nrframework/js/notices.js b/media/plg_system_nrframework/js/notices.js new file mode 100644 index 00000000..d6f8f5d0 --- /dev/null +++ b/media/plg_system_nrframework/js/notices.js @@ -0,0 +1,2 @@ +var TF_Notices=function(){function t(){this.app_ajax_url="?option=com_ajax&format=raw&plugin=nrframework&task=Notices",this.notices=document.querySelector(".tf-notices"),this.root_url=this.notices.dataset.root,this.token=this.notices.dataset.token,this.ext_element=this.notices.dataset.extElement,this.ext_xml=this.notices.dataset.extXml,this.ext_type=this.notices.dataset.extType,this.exclude=JSON.parse(this.notices.dataset.exclude),this.current_url=this.notices.dataset.currentUrl,this.init()}var e=t.prototype;return e.init=function(){this.showNotices(),this.initEvents()},e.showNotices=function(){var e=this,t={ext_element:this.ext_element,ext_xml:this.ext_xml,ext_type:this.ext_type,exclude:this.exclude,current_url:this.current_url,action:"ajaxnotices"},o=["downloadkey","license","outdated","geolocation","update","upgradetopro","upgradetobundle","error"];this.call(t,function(t){for(index in o)t.notices[o[index]]&&(e.notices.innerHTML+=t.notices[o[index]]);!t.notices.license&&t.notices.rate&&(e.notices.innerHTML+=t.notices.rate),""===e.notices.innerHTML&&e.notices.remove()})},e.initEvents=function(){var e=this;document.addEventListener("click",function(t){e.onNoticeClose(t,"outdated",7),e.onNoticeClose(t,"rate",30),e.onAlreadyRatedClose(t),e.onNoticeClose(t,"update",1),e.onNoticeClose(t,"expired",7),e.onNoticeClose(t,"expiring",1),e.onNoticeClose(t,"geolocation",30),e.onNoticeClose(t,"upgradeToPro",30),e.onNoticeClose(t,"upgradeToBundle",30),e.updateDownloadKey(t)}),document.addEventListener("mouseover",function(t){e.onTooltipEnter(t)}),document.addEventListener("mouseout",function(t){e.onTooltipExit(t)}),window.addEventListener("resize",function(t){e.updateTooltipPosition()},!0)},e.onTooltipEnter=function(t){var t=t.target.closest(".notice-tooltip-icon");!t||(t=t.closest(".notice-tooltip-wrapper").querySelector(".notice-tooltip")).classList.contains("is-visible")||(this.hideOtherTooltips(),t.classList.add("is-visible"),this.elementInViewport(t)?t.classList.remove("leftPosition"):t.classList.add("leftPosition"))},e.onTooltipExit=function(t){t.relatedTarget&&!t.relatedTarget.closest(".notice-tooltip-wrapper")&&this.hideOtherTooltips()},e.hideOtherTooltips=function(){var t=document.querySelectorAll(".notice-tooltip.is-visible");t.length&&t.forEach(function(t){t.classList.remove("is-visible"),t.classList.remove("leftPosition")})},e.elementInViewport=function(t){t=t.getBoundingClientRect();return 0<=t.top&&0<=t.left&&20'),input_type=e.prop("nodeName").toLowerCase(),e.parent().addClass("is_"+input_type)})}function o(t){t=void 0===t?0:t;$el_tabs.find(".st_tab_content").hide().eq(t).show(),$el_nav.find("a").removeClass("active").eq(t).addClass("active")}function c(){s(".has-smarttags").removeClass("active"),s("body").removeClass("smarttags-active"),i.hide()}$el_box=i.find(".box"),$el_nav=i.find(".st_nav"),$el_tabs=i.find(".st_tabs"),$el_search=i.find(".st_input_search"),$active_element=null,input_selector=Joomla.getOptions("SmartTagsBox").selector,t(),i.on("update",function(){t()}),$el_search.on("keyup",function(){var e=s(this).val(),e=($el_tabs.find(".st_search").remove(),0==e.length&&o(),s.trim(e.toLowerCase())),a=[];$el_tabs.find(".st_tab_content:not(.st_nosearch) > .st_item").each(function(){var t=s(this);-1").parent().html(),a.push(t))}),$el_tabs.append(''),$el_tab_search=$el_tabs.find(".st_search"),0==a.length?$el_tab_search.html(Joomla.JText._("NR_SMARTTAGS_NOTFOUND")):$el_tab_search.html(a.join("")),o($el_tab_search.index())}),s(document).on("click","span.st_trigger",function(){var t,e,a,n;return t=s(this),(i.is(":visible")?c:($active_element=t,e=Joomla.getOptions("SmartTagsBox").tags,tags=s.extend({},e),s(document).trigger("smartTagsBoxBeforeRender",[tags,$active_element]),a="",s.each(tags,function(t){a+=''+t+""}),i.find(".st_container .st_nav").html(a),n="",s.each(tags,function(t,e){n+='
',s.each(e,function(t,e){if(!e)return!0;n+=''+e+" "+t+""}),n+="
"}),i.find(".st_container .st_tabs").html(n),e=t.parent(),container_height=e.outerHeight(),e.addClass("active"),s("body").addClass("smarttags-active"),t="bottom"==(310<(t=s(window).height()-(e.offset().top+e.height()))?"bottom":"top")?e.offset().top+container_height:e.offset().top-310,i.find(".st_box").css({top:t+"px",left:e.offset().left+"px"}).end().show(),o))(),!1}),s(document).on("click",".st_nav a",function(){return o(s(this).index()),!1}),s(document).on("click",".st_tabs a",function(){var t,e;return t=s(this).data("key"),e=$active_element.parent().find(input_selector),$active_element.parent().find(".mce-tinymce, .tox-tinymce").length?tinymce.get(e.attr("id")).execCommand("mceInsertContent",!1," "+t):(t=s.trim(e.val()+" "+t),e.val(t).trigger("change")),!1}),s(document).on("click",".st_overlay",function(){return c(),!1})}); + diff --git a/media/plg_system_nrframework/js/tf-video-input.js b/media/plg_system_nrframework/js/tf-video-input.js new file mode 100644 index 00000000..4adb3ecf --- /dev/null +++ b/media/plg_system_nrframework/js/tf-video-input.js @@ -0,0 +1,2 @@ +var TF_Video_Input_Previewer=function(){function e(){this.initAll(),this.initEvents()}var t=e.prototype;return t.initAll=function(){var e,i,t;window.IntersectionObserver&&(e=document.querySelectorAll(".tf-video-url-input-value"))&&(i=this,t=new IntersectionObserver(function(e,t){e.forEach(function(e){e.isIntersecting&&""!==(e=e.target).value&&!e.classList.contains("done")&&(e.classList.add("done"),i.update(e))})},{rootMargin:"0px 0px 0px 0px"}),e.forEach(function(e){t.observe(e)}))},t.initEvents=function(){document.addEventListener("click",function(e){this.onClear(e)}.bind(this)),document.addEventListener("change",function(e){this.onChange(e)}.bind(this)),document.addEventListener("focusout",function(e){this.onFinishTyping(e)}.bind(this))},t.onClear=function(e){var t,e=e.target.closest(".button-clear");e&&(e=e.closest(".controls"))&&e.querySelector(".tf-video-input-previewer-wrapper")&&(t=new CustomEvent("focusout",{bubbles:!0}),e.querySelector(".tf-video-url-input-value").dispatchEvent(t))},t.onChange=function(e){var t,e=e.target.closest(".controls");e&&e.querySelector(".tf-video-input-previewer-wrapper")&&(t=new CustomEvent("focusout",{bubbles:!0}),e.querySelector(".tf-video-url-input-value").dispatchEvent(t))},t.onFinishTyping=function(e){e=e.target.closest(".tf-video-url-input-value");e&&this.update(e)},t.update=function(e){var t=e.closest(".controls").querySelector(".tf-video-input-previewer-wrapper");if((t.innerHTML="")===e.value)t.classList.remove("is-visible");else{var i=t.dataset.provider,o=this.getVideoDetails(e.value);if(o.provider!==i.toLowerCase())t.classList.remove("is-visible");else switch(t.classList.add("is-visible"),o.provider){case"youtube":case"vimeo":case"dailymotion":t.innerHTML='';break;case"selfhostedvideo":var n=o.url.replace(/^\/|\/$/g,""),r=n;r.includes("http")||(r=[t.dataset.rootUrl,n].join("/")),t.innerHTML=''}}},t.getVideoDetails=function(e){var t,i={url:e,provider:"selfhostedvideo"},o=e.match(/^(?:https?:\/\/(?:m\.|www\.)?youtube\.com\/(?:shorts\/|[^/]+\/.+[=/]|watch[?/].*v=|embed\/)?([a-zA-Z0-9_-]{11})|https?:\/\/youtu\.be\/([a-zA-Z0-9_-]{11})).*/);return o&&(t=o?o[1]||o[2]:null,i.url="https://youtube.com/embed/"+t,i.provider="youtube"),(o=e.match(/^https?:\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(\d+)/))&&(t=o?o[1]:null,i.url="https://player.vimeo.com/video/"+t,i.provider="vimeo"),/^(?:(?:https?:)?\/\/)?(?:www\.)?facebook\.com\/(?:watch\/\?v=|[\w\.]+\/videos\/(?:[\w\.]+\/)?)?(\d+)/.test(e)&&(i.provider="facebook"),(o=e.match(/(?:dailymotion\.com\/(?:video|hub)\/|dai\.ly\/)([a-zA-Z0-9]+)/))&&(t=o?o[1]:null,i.url="https://www.dailymotion.com/embed/video/"+t,i.provider="dailymotion"),i},e}();document.addEventListener("DOMContentLoaded",function(){new TF_Video_Input_Previewer}); + diff --git a/media/plg_system_nrframework/js/tf_templates_library.js b/media/plg_system_nrframework/js/tf_templates_library.js new file mode 100644 index 00000000..9d6d6843 --- /dev/null +++ b/media/plg_system_nrframework/js/tf_templates_library.js @@ -0,0 +1,2 @@ +var TF_Templates_Library_Filters=function(){function e(e){this.instance=e,this.filtersPillsClass=".tf-library-selected-filters-pills",this.filterItemClass=".tf-library-filter-item",this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("click",function(e){this.initFilterItemToggle(e),this.onClearAllFilters(e),this.onRemoveFilterPillItem(e)}.bind(this)),document.addEventListener("change",function(e){this.onFilterItemSelect(e)}.bind(this))},t.initFilterItemToggle=function(e){var t=e.target.closest(".tf-library-filter-item-label");t&&(e.preventDefault(),t.closest(this.filterItemClass).classList.toggle("open"))},t.onFilterItemSelect=function(e){e.target.closest(".tf-library-filter-choice-item-checkbox")&&this.update()},t.update=function(){var e=this.instance.filters_sidebar.querySelectorAll(".tf-library-filter-item"),o=[],l=(e.forEach(function(t){var e=t.querySelectorAll('input[type="checkbox"]:checked');o[t.dataset.type]=[],e.forEach(function(e){o[t.dataset.type].push(e.value)})}),Object.values(o).every(function(e){return 0===e.length}));l?(this.setNoFilters(),o=[]):this.setHasFilters(),o&&this.instance.items.getCurrentItems().forEach(function(e){var t=e.dataset.filterCategory,i=e.dataset.filterSolution,r=e.dataset.filterGoal,s=e.dataset.filterCompatibility,a=e.dataset.filterTags,n=!!l;for(key in o)if(0!==o[key].length){if("category"===key&&(""===t&&(n=!1),!(n=o[key].includes(t)||t.includes(o[key]))))break;if("solution"===key&&(""===i&&(n=!1),!(n=o[key].includes(i))))break;if("goal"===key&&(""===r&&(n=!1),!(n=o[key].includes(r))))break;if("compatibility"===key&&(""===s&&(n=!1),!(n=o[key].includes(s))))break;if("tags"===key){for(tag in""===a&&(n=!1),o[key]){if(-1!==a.indexOf(o[key][tag])){n=!0;break}n=!1}if(!n)break}}n?e.classList.remove("is-hidden"):e.classList.add("is-hidden")}),this.instance.search.search(),this.instance.sorting.sort()},t.onUpdateSearchElements=function(){var i=this,r=(this.emptyFilterPills(),this.getFilterPillsWrapper());this.instance.filters_sidebar.querySelectorAll('input[type="checkbox"]:checked').forEach(function(e){var t=i.instance.library_wrapper.querySelector(".tf-library-filter-template").cloneNode(!0);t.querySelector(".filter").dataset.filter=e.value,t.querySelector(".filter .filter-label").innerHTML=e.value,r.appendChild(t.children[0])})},t.emptyFilterPills=function(){this.getFilterPillsWrapper().innerHTML=""},t.getFilterPillsWrapper=function(){return this.instance.library_wrapper.querySelector(this.filtersPillsClass)},t.onClearAllFilters=function(e){e.target.closest(".tf-library-filters-clear-all")&&(e.preventDefault(),this.instance.library_toolbar.querySelector(".tf-library-search input").value="",this.instance.filters_sidebar.querySelectorAll('input[type="checkbox"]:checked').forEach(function(e){e.checked=!1}),this.update())},t.onRemoveFilterPillItem=function(e){var t=e.target.closest(".tf-library-filter-pill-item-remove");t&&(e.preventDefault(),e=t.closest(".filter").dataset.filter,this.instance.filters_sidebar.querySelectorAll('input[type="checkbox"][value="'+e+'"]:checked').forEach(function(e){e.checked=!1}),this.update())},t.setHasFilters=function(){this.instance.library_wrapper.classList.add("has-filters")},t.setNoFilters=function(){this.instance.library_wrapper.classList.remove("has-filters")},e}(),TF_Templates_Info_Modal=function(){function e(e){this.instance=e,this.modal=document.querySelector("#tf-library-item-info-popup"),this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("click",function(e){this.onBeforeOpen(e)}.bind(this))},t.onBeforeOpen=function(e){var i,r,s,a=this,t=e.target.closest('[data-bs-target="#tf-library-item-info-popup"]'),n=e.target.closest(".tf-library-template-item-info-popup-trigger");(t||n)&&(t=t||n||!1)&&(n&&(e.preventDefault(),jQuery(this.modal).modal("show")),n=this.instance.getTemplateItem(t),i=n.querySelector(".info-popup-actions"),(r=this.modal.querySelector(".dependency-items")).innerHTML="",e=JSON.parse(n.dataset.capabilities),this.modal.querySelector(".modal-header > h3").innerHTML=n.querySelector(".template-label").innerHTML,this.modal.querySelector(".item-description").innerHTML=n.dataset.note,this.modal.querySelector(".template-details").querySelector(".category > .content").innerHTML=e.category.value||"-",this.modal.querySelector(".template-details").querySelector(".solution > .content").innerHTML=e.solution.value||"-",this.modal.querySelector(".template-details").querySelector(".goal > .content").innerHTML=e.goal.value||"-",(s=this.modal.querySelector(".dependency-item.template").cloneNode(!0)).classList.remove("template"),s.querySelector(".requirement").innerHTML=this.instance.getOption("project_name")+" "+("pro"===e.pro.requirement?this.instance.getOption("pro"):this.instance.getOption("lite")),s.querySelector(".detected").innerHTML=this.instance.getOption("project_name")+" "+this.instance.getOption(e.pro.detected),"pro"===e.pro.requirement&&"pro"!==e.pro.detected?s.querySelector(".value").appendChild(i.querySelector("a.pro").cloneNode(!0)):s.classList.add("pass"),r.appendChild(s),(s=this.modal.querySelector(".dependency-item.template").cloneNode(!0)).classList.remove("template"),s.querySelector(".requirement").innerHTML="Joomla! "+e.joomla.value+"+",s.querySelector(".detected").innerHTML="Joomla! "+e.joomla.detected,""!==e.joomla.icon?s.querySelector(".value").appendChild(i.querySelector("a.joomla").cloneNode(!0)):s.classList.add("pass"),r.appendChild(s),(s=this.modal.querySelector(".dependency-item.template").cloneNode(!0)).classList.remove("template"),s.querySelector(".requirement").innerHTML=this.instance.getOption("project_name")+" "+e.project.value+"+",s.querySelector(".detected").innerHTML=this.instance.getOption("project_name")+" "+e.project.detected,""!==e.project.icon?(s.querySelector(".value").appendChild(i.querySelector("a.project").cloneNode(!0)),s.querySelector(".value a .short-label").innerHTML=this.instance.getOption("update_extension")):s.classList.add("pass"),r.appendChild(s),e.third_party_dependencies.value&&e.third_party_dependencies.value.forEach(function(e,t){(s=a.modal.querySelector(".dependency-item.template").cloneNode(!0)).classList.remove("template"),s.querySelector(".requirement").innerHTML=e.name+" "+e.version+"+",s.querySelector(".detected").innerHTML="none"===e.detected?"-":e.name+" "+e.detected,e.valid?s.classList.add("pass"):(s.querySelector(".value").appendChild(i.querySelector("a.third_party_dependencies_"+t).cloneNode(!0)),s.querySelector(".value a .short-label").innerHTML="update"===e.icon?a.instance.getOption("update_extension"):a.instance.getOption("install_extension")),r.appendChild(s)}),""!==e.license_error.value)&&((s=this.modal.querySelector(".dependency-item.template").cloneNode(!0)).classList.remove("template"),s.querySelector(".requirement").innerHTML=this.instance.getOption("license_key"),s.querySelector(".detected").innerHTML="missing"===e.license_error.value?"-":this.instance.getOption("license"),s.querySelector(".value").appendChild(i.querySelector("a.license").cloneNode(!0)),r.appendChild(s))},e}(),TF_Templates_Library_Items=function(){function e(e){this.instance=e,this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("click",function(e){this.onCloseItemMessage(e),this.onFavorite(e),this.onInsert(e)}.bind(this))},t.onCloseItemMessage=function(e){e=e.target.closest(".fpf-library-messages-hide-btn");e&&e.closest(".tf-template-item-message").classList.add("is-hidden")},t.onFavorite=function(e){var i,r,s=e.target.closest(".tf-library-favorite-item");s&&(e.preventDefault(),s.classList.add("working"),i=e.target.closest(".tf-library-item").dataset.id,(r=this).instance.ajaxCall("favorites_toggle",{template_id:i},function(e){var e=Object.keys(e).includes(i),t=document.querySelector(".tf-library-item[data-id='"+i+"'] .tf-library-favorite-item");s.classList.remove("working"),e?t.classList.add("active"):(t.classList.remove("active"),r.instance.filters.update())}))},t.onInsert=function(e){var t,i,r,s=e.target.closest(".tf-library-item-insert-btn");s&&(t=s.dataset.templateId)&&(e.preventDefault(),i=s.closest(".tf-library-item"),document.body.classList.add("tf-templates-library-inserting"),i&&i.classList.add("inserting-template"),this.instance.hideMessageAlert(),s.classList.add("working"),(r=this).instance.ajaxCall("insert_template",{template_id:t},function(e){e.error?r.instance.previewModal.classList.contains("fade")&&(r.instance.previewModal.classList.contains("in")||r.instance.previewModal.classList.contains("show"))?(r.instance.showMessageAlert(e.message),r.instance.modal.querySelector(".tf-library-body").scrollTo({top:0,behavior:"smooth"}),jQuery(r.instance.previewModal).modal("hide")):r.instance.showTemplateMessage(i,e.message):window.location.href=e.redirect,document.body.classList.remove("tf-templates-library-inserting"),i&&i.classList.remove("inserting-template"),s.classList.remove("working")}))},t.getCurrentItems=function(e){var t=".tf-library-list > .tf-library-item:not(.blank_popup):not(.ignore)";return(e=void 0===e?!1:e)&&(t+=":not(.is-hidden)"),this.instance.library_wrapper.querySelectorAll(t)},e}(),TF_Templates_Library=function(){function e(){this.observer=null,this.ready=!1,this.JoomlaOptions=null,this.onReady()}var t=e.prototype;return t.onReady=function(){var t;window.MutationObserver&&((t=this).observer=new MutationObserver(function(e){if(e)for(m in e){if(t.ready)return;e[m].target.closest(".tf-templates-library")&&(t.init(),t.ready=!0)}}),this.observer.observe(document.body,{childList:!0,subtree:!0,attributes:!0}))},t.init=function(){this.observer.disconnect(),this.modal=document.querySelector(".tf-templates-library"),this.isJ4=this.modal.classList.contains("isJ4"),this.library_wrapper=document.querySelector(".tf-library-page"),this.library_noresults=document.querySelector(".tf-library-no-results"),this.library_list=document.querySelector(".tf-library-list"),this.library_toolbar=this.library_wrapper.querySelector(".tf-library-toolbar"),this.library_messages=this.library_wrapper.querySelector(".tf-library-messages"),this.sidebar_element=this.library_wrapper.querySelector(".tf-library-sidebar"),this.filters_sidebar=this.sidebar_element.querySelector(".tf-library-sidebar-filters"),this.previewModal=document.querySelector(".tf-templates-library-popup-preview"),this.modals=new TF_Templates_Library_Modals(this),this.items=new TF_Templates_Library_Items(this),this.toolbar=new TF_Templates_Library_Toolbar(this),this.filters=new TF_Templates_Library_Filters(this),this.search=new TF_Templates_Library_Search(this),this.sidebar=new TF_Templates_Library_Sidebar(this),this.info_modal=new TF_Templates_Info_Modal(this),this.sorting=new TF_Templates_Library_Sorting(this),this.preview_modal=new TF_Templates_Preview_Modal(this),this.prepare(),this.initEvents()},t.prepare=function(){this.isJ4&&document.body.classList.add("isJ4")},t.initEvents=function(){var e=this;jQuery(this.modal).on("show.bs.modal",function(){e.loadTemplates()}),document.addEventListener("click",function(e){this.handleRefreshTemplates(e),this.onMessageHide(e),this.toggleFullscreenMode(e)}.bind(this))},t.onMessageHide=function(e){e.target.closest(".tf-library-messages-hide-btn")&&(this.library_messages.classList.add("is-hidden"),this.library_messages.querySelector(".tf-library-messages-text").innerHTML="")},t.toggleFullscreenMode=function(e){var t=e.target.closest(".tf-templates-library-toggle-fullscreen");t&&(e.preventDefault(),this.modal.classList.toggle("fullscreen"),t.classList.toggle("fullscreen"))},t.getOption=function(e,t){t=void 0===t?"":t;var i=this.getJoomlaOption("tassos_framework");return i&&void 0!==i[e]?i[e]:t},t.getJoomlaOption=function(e,t){if(!this.JoomlaOptions){var i=document.querySelector(".joomla-script-options");if(!i)return;this.JoomlaOptions=JSON.parse(i.text||i.textContent)}return void 0!==this.JoomlaOptions[e]?this.JoomlaOptions[e]:t},t.ajaxCall=function(e,t,i){t.options=this.library_wrapper.dataset.options;e=this.getOption("templates_library_ajax_url")+"&action="+e+"&"+this.getOption("csrf_token")+"=1";fetch(e,{method:"post",body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(e){i(e)}).catch(function(e){console.log(e),alert(e)})},t.loadTemplates=function(){if(this.library_wrapper.classList.contains("loaded"))return!1;var t=this.modal.querySelector(".tf-templates-refresh-btn"),i=(t.classList.add("working"),this.resetTemplatesLayout(),this);this.ajaxCall("get_templates",{},function(e){i.updateLibraryLayout(e),t.classList.remove("working")})},t.handleRefreshTemplates=function(e){var t,i=e.target.closest(".tf-templates-refresh-btn");i&&(e.preventDefault(),t=this,i.classList.add("working"),this.resetTemplatesLayout(),this.ajaxCall("refresh_templates",{},function(e){t.updateLibraryLayout(e),i.classList.remove("working"),e.code||e.message||(i.classList.add("checkmark"),setTimeout(function(){i.classList.remove("checkmark")},2e3))}))},t.updateLibraryLayout=function(e){var t="";e?e.error&&e.message&&(t=e.message):t="Cannot load templates",t?(this.showMessageAlert(t),this.modal.querySelector(".tf-library-body").scrollTo({top:0,behavior:"smooth"})):(this.library_list.querySelectorAll(".tf-library-item:not(.blank_popup)").forEach(function(e){e.remove()}),this.library_wrapper.classList.add("loaded"),this.library_list.innerHTML+=e.templates,e.filters?(this.library_wrapper.classList.remove("no-sidebar"),this.filters_sidebar.innerHTML=e.filters):this.library_wrapper.classList.add("no-sidebar"),this.filters.update())},t.resetTemplatesLayout=function(){this.hideMessageAlert(),document.querySelector(".tf-library-list").classList.remove("is-hidden"),document.querySelector(".tf-library-no-results").classList.remove("is-visible")},t.hideMessageAlert=function(){this.library_messages.classList.add("is-hidden")},t.showTemplateMessage=function(e,t){e=e.querySelector(".tf-template-item-message");t&&(e.querySelector(".tf-template-item-message-text").innerHTML=t),e.classList.remove("is-hidden")},t.showMessageAlert=function(e){e&&(this.library_messages.querySelector(".tf-library-messages-text").innerHTML=e),this.library_messages.classList.remove("is-hidden")},t.getTemplateItem=function(e){var t=e.dataset.templateId;return t?this.library_list.querySelector('.tf-library-item[data-id="'+t+'"]'):e.closest(".tf-library-item")},e}();function _createForOfIteratorHelperLoose(e,t){var i,r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(r)return(r=r.call(e)).next.bind(r);if(Array.isArray(e)||(r=_unsupportedIterableToArray(e))||t&&e&&"number"==typeof e.length)return r&&(e=r),i=0,function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,t){var i;if(e)return"string"==typeof e?_arrayLikeToArray(e,t):"Map"===(i="Object"===(i=Object.prototype.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:i)||"Set"===i?Array.from(e):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?_arrayLikeToArray(e,t):void 0}function _arrayLikeToArray(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,r=new Array(t);ii&&(i=s.tfTabIndex,t=s)}t&&jQuery(t).modal("hide")}}},t.getAllVisibleModals=function(){var i=[];return this.popupsClasses.forEach(function(e){var t=document.querySelector("."+e+".fade.in");(t=t||document.querySelector("."+e+".fade.show"))&&i.push(t)}),i},e}(),TF_Templates_Preview_Modal=function(){function e(e){this.instance=e,this.previewing=!1,this.modal=document.querySelector("#tf-library-preview-popup"),this.initEvents()}var t=e.prototype;return t.initEvents=function(){window.addEventListener("hashchange",function(e){"#templates-library-previewer"===window.location.hash&&(this.previewing=!0),this.previewing&&""===window.location.hash&&jQuery(this.modal).modal("hide")}.bind(this)),jQuery(this.modal).on("hide.bs.modal",function(e){this.onBeforeClose()}.bind(this)),document.addEventListener("click",function(e){this.onBeforeOpen(e),this.toggleResponsiveViewport(e),this.refreshDemo(e)}.bind(this))},t.onBeforeOpen=function(e){var t,i,e=e.target.closest(".tf-library-preview-item");e&&(jQuery(this.modal).modal("show"),e=this.instance.getTemplateItem(e),this.modal.querySelector(".modal-title").innerHTML=e.querySelector(".template-label").innerHTML,i=e.querySelector(".tf-library-template-item-info").cloneNode(!0),this.modal.querySelector(".modal-header .tf-library-template-item-info")&&this.modal.querySelector(".modal-header .tf-library-template-item-info").remove(),e.classList.contains("has-errors")&&i.classList.add("has-errors"),this.modal.querySelector(".modal-header .modal-title-wrapper").appendChild(i),i=(i=this.instance.library_wrapper.dataset.previewUrl).replace("TEMPLATE_ID",e.dataset.id),this.modal.querySelector("iframe")?((t=this.modal.querySelector("iframe").cloneNode()).src=i,this.modal.querySelector("iframe").parentNode.replaceChild(t,this.modal.querySelector("iframe"))):((t=document.createElement("iframe")).className="tf-library-preview-iframe",t.src=i,this.modal.querySelector(".tf-library-preview-inner").appendChild(t)),this.modal.querySelector("iframe").addEventListener("load",this.onIframeLoaded.bind(this),!0),this.setViewport("desktop"),i=e.querySelector(".tf-library-item-actions a").cloneNode(!0),this.modal.querySelector(".modal-header .tf-library-preview-action")&&this.modal.querySelector(".modal-header .tf-library-preview-action").remove(),i.classList.add("tf-library-preview-action","tf-button","outline"),i.classList.contains("red")||(e.classList.contains("has-errors")?i.classList.add("orange"):i.classList.add("blue")),this.modal.querySelector(".modal-header .actions-wrapper").insertBefore(i,this.modal.querySelector(".modal-header .actions-wrapper").firstChild))},t.onIframeLoaded=function(){this.modal.classList.remove("refreshing")},t.onBeforeClose=function(){this.previewing=!1,window.location.hash="";var e=this.getIFrame();e&&(e.removeEventListener("load",this.onIframeLoaded),e.src="")},t.refreshDemo=function(e){var t;e.target.closest(".tf-templates-library-refresh-demo")&&(e.preventDefault(),this.modal.querySelector("iframe").removeEventListener("load",this.onIframeLoaded),this.modal.classList.add("refreshing"),(t=(e=this.getIFrame()).cloneNode()).addEventListener("load",this.onIframeLoaded.bind(this),!0),t.src=e.src,e.parentNode.replaceChild(t,e))},t.getIFrame=function(){return this.modal.querySelector("iframe")},t.toggleResponsiveViewport=function(e){e=e.target.closest(".tf-templates-library-preview-responsive-device");e&&this.setViewport(e.dataset.device)},t.setViewport=function(e){var t=this.getIFrame();this.modal.querySelector(".tf-templates-library-preview-responsive-device.active").classList.remove("active"),this.modal.querySelector('.tf-templates-library-preview-responsive-device[data-device="'+e+'"]').classList.add("active"),t.classList.remove("desktop","tablet","mobile"),t.classList.add(e)},e}(),TF_Templates_Library_Search=function(){function e(e){this.instance=e}var t=e.prototype;return t.search=function(){var i=this.getSearchTerm(),r=this.instance.library_toolbar.querySelector(".tf-library-view-favorites.active");this.instance.items.getCurrentItems(!0).forEach(function(e){var t=!0;i&&(t=!1,e.dataset.title.toLowerCase().includes(i)&&(t=!0),e.dataset.filterCategory.toLowerCase().includes(i)&&(t=!0),e.dataset.filterTags.toLowerCase().includes(i)&&(t=!0),e.dataset.filterSolution.toLowerCase().includes(i)&&(t=!0),e.dataset.filterGoal.toLowerCase().includes(i))&&(t=!0),(t=!r||e.querySelector(".tf-library-favorite-item")&&e.querySelector(".tf-library-favorite-item").classList.contains("active")?t:!1)?e.classList.remove("is-hidden"):e.classList.add("is-hidden")}),this.updateSearchElements()},t.updateSearchElements=function(){var e,t=this.instance.items.getCurrentItems(!0).length,t=(0===t?(this.instance.library_noresults.classList.add("is-visible"),this.instance.library_list.classList.add("is-hidden")):(this.instance.library_noresults.classList.remove("is-visible"),this.instance.library_list.classList.remove("is-hidden")),this.instance.library_wrapper.querySelector(".tf-showing-results-counter").innerHTML=t,this.instance.filters.onUpdateSearchElements(),this.getSearchTerm());t&&(this.instance.filters.setHasFilters(),(e=this.instance.library_wrapper.querySelector(".tf-library-filter-template").cloneNode(!0)).querySelector(".filter").classList.add("search-filter-pill"),e.querySelector(".filter svg").remove(),e.querySelector(".filter .filter-label").textContent='"'+t+'"',this.instance.filters.getFilterPillsWrapper().appendChild(e.children[0]))},t.getSearchTerm=function(){return this.instance.library_toolbar.querySelector("#tf_search_template").value.trim().toLowerCase()},e}(),TF_Templates_Library_Sidebar=function(){function e(e){this.instance=e,this.items=this.instance.items,this.toolbar=this.instance.toolbar,this.initEvents()}var t=e.prototype;return t.initEvents=function(){this.onPageLoadToggle(),document.addEventListener("click",function(e){this.initSidebarToggle(e)}.bind(this))},t.initSidebarToggle=function(e){e.target.closest(".tf-library-sidebar-toggle")&&(e.preventDefault(),this.instance.modal.classList.toggle("sidebar-open"),e=this.getLibraryID(),this.instance.modal.classList.contains("sidebar-open")?window.localStorage.setItem(e,"open"):(window.localStorage.removeItem(e),this.instance.sidebar_element.scrollTo({top:0,behavior:"smooth"})))},t.getLibraryID=function(){return"templates_library_"+this.instance.modal.id},t.onPageLoadToggle=function(){window.localStorage.getItem(this.getLibraryID())?this.instance.modal.classList.add("sidebar-open"):this.instance.modal.classList.remove("sidebar-open")},e}(),TF_Templates_Library_Sorting=function(){function e(e){this.instance=e,this.sortWrapper=this.instance.library_toolbar.querySelector(".sorting-selector-item"),this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("click",function(e){this.onSortUpdate(e),this.onSortHide(e)}.bind(this)),this.sortWrapper.addEventListener("mouseover",function(e){this.onSortingShow(e)}.bind(this)),this.sortWrapper.addEventListener("mouseout",function(e){this.onSortingMouseOutHide(e)}.bind(this))},t.onSortingMouseOutHide=function(e){(e.target.closest(".sorting-selected-label")||e.target.closest(".sorting-selector-items"))&&this.sortWrapper.classList.remove("visible")},t.onSortUpdate=function(e){var t;e.target.closest(".sorting-selector-items")&&(t=e.target.closest("li"))&&!t.classList.contains("selected")&&(e.preventDefault(),this.sortWrapper.classList.remove("visible"),this.sortWrapper.querySelector(".sorting-selector-items li.selected").classList.remove("selected"),t.classList.add("selected"),this.sortWrapper.querySelector(".sorting-selected-label .selected-label").innerHTML=t.innerHTML,this.sort())},t.sort=function(){var r=this,s=this.sortWrapper.querySelector(".sorting-selector-items li.selected").dataset.value,e=this.instance.items.getCurrentItems(!0);e.forEach(function(e){e.style.order=null});e=Array.from(e).sort(function(e,t){var i="sort"+r.capitalizeFirstLetter(s);return"date"===s?e.dataset[i].localeCompare(t.dataset[i]):parseInt(e.dataset[i],10)<=parseInt(t.dataset[i],10)?-1:1});var e=[].concat(e).reverse(),t=1;e.forEach(function(e){e.style.order=t,t++})},t.onSortHide=function(e){e.target.closest(".sorting-selector-item")||this.sortWrapper.classList.remove("visible")},t.onSortingShow=function(e){e.target.closest(".sorting-selector-item")&&this.sortWrapper.classList.add("visible")},t.capitalizeFirstLetter=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},e}(),TF_Templates_Library_Toolbar=function(){function e(e){this.instance=e,this.initEvents()}var t=e.prototype;return t.initEvents=function(){document.addEventListener("input",function(e){this.onInputSearch(e)}.bind(this)),document.addEventListener("click",function(e){this.onFavoritesView(e)}.bind(this))},t.onInputSearch=function(e){e.target.closest("#tf_search_template")&&(e.target.value.trim(),this.instance.filters.update())},t.onFavoritesView=function(e){var t=e.target.closest(".tf-library-view-favorites");t&&(t.classList.toggle("active"),this.instance.library_wrapper.classList.toggle("favorites-view"),this.instance.filters.update(),e.preventDefault())},e}(); + diff --git a/media/plg_system_nrframework/js/tffieldsvaluesapplier.js b/media/plg_system_nrframework/js/tffieldsvaluesapplier.js new file mode 100644 index 00000000..673cdf43 --- /dev/null +++ b/media/plg_system_nrframework/js/tffieldsvaluesapplier.js @@ -0,0 +1,2 @@ +var TF_Fields_Values_Applier=function(){function e(e,t){this.data=e,this.item=t,this.breakpoints=["desktop","tablet","mobile"],this.dimensions=["top","right","bottom","left"],this.border_radius_dimensions=["top_left","top_right","bottom_right","bottom_left"]}var t=e.prototype;return t.applyItemData=function(){this.data[this.item]&&(this.data=this.data[this.item],this.applyData())},t.applyData=function(){for(var e in this.data){var t=this.data[e];e.startsWith("[")||e.endsWith("]")||(e="["+e+"]"),t.responsive||t.isResponsive?t.dimensions?this.setDimensionsResponsiveValue(e,t):this.setResponsiveValue(e,t):this.setValue(e,t.type,t.value)}},t.setDimensionsResponsiveValue=function(e,t){var i=t.type,s=t.value,n=t.border_radius||!1,o=n?this.border_radius_dimensions:this.dimensions;if(null!==s&&"object"==typeof s)for(breakpoint in s){if(null!==s[breakpoint]&&"object"!=typeof s[breakpoint]){var a=s[breakpoint];if(s[breakpoint]={},n)for(index in this.border_radius_dimensions)s[breakpoint][this.border_radius_dimensions[index]]=a;else for(index in this.dimensions)s[breakpoint][this.dimensions[index]]=a}for(dimension in s[breakpoint]){var r=e+"["+breakpoint+"]["+dimension+"]";this.setValue(r,i,s[breakpoint][dimension])}}else for(breakpoint in this.breakpoints)for(dimension in o){var p=e+"["+this.breakpoints[breakpoint]+"]["+o[dimension]+"]";this.setValue(p,i,s)}},t.setResponsiveValue=function(e,t){var i=t.type,s=t.value,n="";if(t.isResponsive&&(n+="[value]"),null!==s&&"object"==typeof s)for(breakpoint in s){var o=e+"["+breakpoint+"]"+n;this.setValue(o,i,s[breakpoint])}else for(breakpoint in this.breakpoints){var a=e+"["+this.breakpoints[breakpoint]+"]"+n;this.setValue(a,i,s)}},t.setValue=function(e,t,i){switch(t){case"text":case"number":case"color":var s=document.querySelector('input[type="'+t+'"][name$="'+e+'"]');s.value=i,s.closest(".nr-responsive-control--item")&&s.dispatchEvent(new CustomEvent("input",{bubbles:!0})),s.parentElement&&s.parentElement.classList.contains("minicolors")&&jQuery(s).minicolors&&jQuery(s).minicolors("value",i);break;case"nrtoggle":s=document.querySelector('input[type="checkbox"][name$="'+e+'"]');s.checked=i,s.dispatchEvent(new CustomEvent("change",{bubbles:!0}));break;case"radio":s=document.querySelector('input[type="radio"][name$="'+e+'"][value="'+i+'"]');s.checked=!0,s.dispatchEvent(new CustomEvent("change",{bubbles:!0}));break;case"list":s=document.querySelector('select[name$="'+e+'"]');s.value=i,!Joomla.Modal&&s.classList.contains("hasChosen")&&(jQuery(s).chosen("destroy"),jQuery(s).chosen()),s.dispatchEvent(new CustomEvent("change",{bubbles:!0}))}},e}(); + diff --git a/media/plg_system_nrframework/js/tfinputrepeater.js b/media/plg_system_nrframework/js/tfinputrepeater.js new file mode 100644 index 00000000..20744c83 --- /dev/null +++ b/media/plg_system_nrframework/js/tfinputrepeater.js @@ -0,0 +1,2 @@ +!function(){"use strict";jQuery(document).on("subform-row-remove",".tf-input-repeater .subform-repeatable",function(e){1==e.target.querySelectorAll("tbody tr").length&&e.target.querySelector("thead .group-add").click()}),document.addEventListener("click",function(e){var t=e.target.closest(".tf-input-repeater-add");t&&(e.preventDefault(),t.closest(".tf-input-repeater").querySelector("table thead .group-add").click(),t.closest(".tf-input-repeater").querySelector(".tf-ajaxify-wrapper"))&&"function"==typeof TF_Ajaxify_Field&&new TF_Ajaxify_Field})}(); + diff --git a/media/plg_system_nrframework/js/treeselect.js b/media/plg_system_nrframework/js/treeselect.js new file mode 100644 index 00000000..9a0ec882 --- /dev/null +++ b/media/plg_system_nrframework/js/treeselect.js @@ -0,0 +1,2 @@ +!function(e,t){"use strict";var i={init:function(r){if(r)return jQuery(function(t){var e=r,n=t(r),i=n.find("div.nr_treeselect-controls"),l=n.find("ul.nr_treeselect-ul"),c=n.find("div.nr_treeselect-menu-block").html(),s=l.css("max-height");l.find("li").each(function(){var e=t(this),n=e.find("div.nr_treeselect-item:first");e.prepend(''),n.after('
'),e.find("ul.nr_treeselect-sub").length&&(e.find("span.icon-").addClass("nr_treeselect-toggle icon-minus"),n.find("label:first").after(c),e.find("ul.nr_treeselect-sub ul.nr_treeselect-sub").length||e.find("div.nr_treeselect-menu-expand").remove())}),l.find("span.nr_treeselect-toggle").on("click",function(){var e=t(this);e.parent().find("ul.nr_treeselect-sub").is(":visible")?(e.removeClass("icon-minus").addClass("icon-plus"),e.parent().find("ul.nr_treeselect-sub").hide(),e.parent().find("ul.nr_treeselect-sub span.nr_treeselect-toggle").removeClass("icon-minus").addClass("icon-plus")):(e.removeClass("icon-plus").addClass("icon-minus"),e.parent().find("ul.nr_treeselect-sub").show(),e.parent().find("ul.nr_treeselect-sub span.nr_treeselect-toggle").removeClass("icon-plus").addClass("icon-minus"))}),i.find("input.nr_treeselect-filter").on("keyup",function(){var n=t(this).val().toLowerCase();l.find("li").each(function(){var e=t(this);-1==e.text().toLowerCase().indexOf(n)?e.hide():e.show()})}),i.find("a.nr_treeselect-checkall").on("click",function(){l.find("input").prop("checked",!0)}),i.find("a.nr_treeselect-uncheckall").on("click",function(){l.find("input").prop("checked",!1)}),i.find("a.nr_treeselect-toggleall").on("click",function(){l.find("input").each(function(){var e=t(this);e.prop("checked")?e.prop("checked",!1):e.prop("checked",!0)})}),i.find("a.nr_treeselect-expandall").on("click",function(){l.find("ul.nr_treeselect-sub").show(),l.find("span.nr_treeselect-toggle").removeClass("icon-plus").addClass("icon-minus")}),i.find("a.nr_treeselect-collapseall").on("click",function(){l.find("ul.nr_treeselect-sub").hide(),l.find("span.nr_treeselect-toggle").removeClass("icon-minus").addClass("icon-plus")}),i.find("a.nr_treeselect-showall").on("click",function(){l.find("li").show()}),i.find("a.nr_treeselect-showselected").on("click",function(){l.find("li").each(function(){var e=t(this),n=!0;e.find("input").each(function(){if(t(this).prop("checked"))return n=!1}),n?e.hide():e.show()})}),i.find("a.nr_treeselect-maximize").on("click",function(){l.css("max-height",""),i.find("a.nr_treeselect-maximize").hide(),i.find("a.nr_treeselect-minimize").show()}),i.find("a.nr_treeselect-minimize").on("click",function(){l.css("max-height",s),i.find("a.nr_treeselect-minimize").hide(),i.find("a.nr_treeselect-maximize").show()}),e.querySelectorAll(".checkall, .uncheckall").forEach(function(e){e.addEventListener("click",function(){var n=!!this.classList.contains("checkall");this.closest(".nr_treeselect-item").parentNode.querySelectorAll(":scope .nr_treeselect-sub input").forEach(function(e){e.checked=n})})}),e.querySelectorAll(".expandall, .collapseall").forEach(function(e){e.addEventListener("click",function(){var n=!!this.classList.contains("expandall"),e=this.closest(".nr_treeselect-item").parentNode,e=(e.querySelectorAll("ul.nr_treeselect-sub").forEach(function(e){e.style.display=n?"block":"none"}),e.querySelector("ul.nr_treeselect-sub span.nr_treeselect-toggle"));e.classList.remove(n?"icon-plus":"icon-minus"),e.classList.add(n?"icon-minus":"icon-plus")})})}),!0;throw new Error("Invalid Element")}};t.addEventListener("DOMContentLoaded",function(){for(var e=t.querySelectorAll(".nr_treeselect"),n=0;n=0?this._store.getGroupById(r):null;return this._store.dispatch((0,l.highlightItem)(i,!0)),t&&this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:i,value:o,label:c,groupValue:h&&h.value?h.value:null}),this},e.prototype.unhighlightItem=function(e){if(!e||!e.id)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,r=e.value,s=void 0===r?"":r,o=e.label,a=void 0===o?"":o,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch((0,l.highlightItem)(t,!1)),this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:t,value:s,label:a,groupValue:c&&c.value?c.value:null}),this},e.prototype.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(d.EVENTS.showDropdown,{})})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(d.EVENTS.hideDropdown,{})})),this):this},e.prototype.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},e.prototype.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},e.prototype.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement||(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this},e.prototype.setChoices=function(e,t,i,n){var r=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===i&&(i="label"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(n&&this.clearChoices(),"function"==typeof e){var s=e(this);if("function"==typeof Promise&&s instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return r._handleLoadingState(!0)})).then((function(){return s})).then((function(e){return r.setChoices(e,t,i,n)})).catch((function(e){r.config.silent||console.error(e)})).then((function(){return r._handleLoadingState(!1)})).then((function(){return r}));if(!Array.isArray(s))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof s));return this.setChoices(s,t,i,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){if(e.choices)r._addGroup({id:e.id?parseInt("".concat(e.id),10):null,group:e,valueKey:t,labelKey:i});else{var n=e;r._addChoice({value:n[t],label:n[i],isSelected:!!n.selected,isDisabled:!!n.disabled,placeholder:!!n.placeholder,customProperties:n.customProperties})}})),this._stopLoading(),this},e.prototype.clearChoices=function(){return this._store.dispatch((0,a.clearChoices)()),this},e.prototype.clearStore=function(){return this._store.dispatch((0,h.clearAll)()),this},e.prototype.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))),this},e.prototype._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},e.prototype._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,r=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var s=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));s.length>=1&&(r=this._createChoicesFragment(s,r)),r=this._createGroupsFragment(i,n,r)}else n.length>=1&&(r=this._createChoicesFragment(n,r));if(r.childNodes&&r.childNodes.length>0){var o=this._store.activeItems,a=this._canAddItem(o,this.input.value);if(a.response)this.choiceList.append(r),this._highlightChoice();else{var c=this._getTemplate("notice",a.notice);this.choiceList.append(c)}}else{var l=void 0;c=void 0,this._isSearching?(c="function"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,l=this._getTemplate("notice",c,"no-results")):(c="function"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,l=this._getTemplate("notice",c,"no-choices")),this.choiceList.append(l)}},e.prototype._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;return void 0===i&&(i=document.createDocumentFragment()),this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var r=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(r.length>=1){var s=n._getTemplate("choiceGroup",e);i.appendChild(s),n._createChoicesFragment(r,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var r=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var s=this.config,o=s.renderSelectedChoices,a=s.searchResultLimit,c=s.renderChoiceLimit,l=this._isSearching?f.sortByScore:this.config.sorter,h=function(e){if("auto"!==o||r._isSelectOneElement||!e.selected){var i=r._getTemplate("choice",e,r.config.itemSelectText);t.appendChild(i)}},u=e;"auto"!==o||this._isSelectOneElement||(u=e.filter((function(e){return!e.selected})));var d=u.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),p=d.placeholderChoices,m=d.normalChoices;(this.config.shouldSort||this._isSearching)&&m.sort(l);var v=u.length,g=this._isSelectOneElement?n(n([],p,!0),m,!0):m;this._isSearching?v=a:c&&c>0&&!i&&(v=c);for(var _=0;_=n){var o=r?this._searchChoices(e):0;this.passedElement.triggerEvent(d.EVENTS.search,{value:e,resultCount:o})}else s&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0)))}},e.prototype._canAddItem=function(e,t){var i=!0,n="function"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var r=(0,f.existsInArray)(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n="function"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&r&&i&&(i=!1,n="function"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&"function"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n="function"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},e.prototype._searchChoices=function(e){var t="string"==typeof e?e.trim():e,i="string"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t==="".concat(i," "))return 0;var r=this._store.searchableChoices,s=t,c=Object.assign(this.config.fuseOptions,{keys:n([],this.config.searchFields,!0),includeMatches:!0}),l=new o.default(r,c).search(s);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch((0,a.filterChoices)(l)),l.length},e.prototype._addEventListeners=function(){var e=document.documentElement;e.addEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.addEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener("focus",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener("blur",this._onBlur,{passive:!0})),this.input.element.addEventListener("keyup",this._onKeyUp,{passive:!0}),this.input.element.addEventListener("focus",this._onFocus,{passive:!0}),this.input.element.addEventListener("blur",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener("focus",this._onFocus),this.containerOuter.element.removeEventListener("blur",this._onBlur)),this.input.element.removeEventListener("keyup",this._onKeyUp),this.input.element.removeEventListener("focus",this._onFocus),this.input.element.removeEventListener("blur",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.activeItems,n=this.input.isFocussed,r=this.dropdown.isActive,s=this.itemList.hasChildren(),o=String.fromCharCode(t),a=/[^\x00-\x1F]/.test(o),c=d.KEY_CODES.BACK_KEY,l=d.KEY_CODES.DELETE_KEY,h=d.KEY_CODES.ENTER_KEY,u=d.KEY_CODES.A_KEY,p=d.KEY_CODES.ESC_KEY,f=d.KEY_CODES.UP_KEY,m=d.KEY_CODES.DOWN_KEY,v=d.KEY_CODES.PAGE_UP_KEY,g=d.KEY_CODES.PAGE_DOWN_KEY;switch(this._isTextElement||r||!a||(this.showDropdown(),this.input.isFocussed||(this.input.value+=e.key.toLowerCase())),t){case u:return this._onSelectKey(e,s);case h:return this._onEnterKey(e,i,r);case p:return this._onEscapeKey(r);case f:case v:case m:case g:return this._onDirectionKey(e,r);case l:case c:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,r=this._store.activeItems,s=this._canAddItem(r,n),o=d.KEY_CODES.BACK_KEY,c=d.KEY_CODES.DELETE_KEY;if(this._isTextElement)if(s.notice&&n){var l=this._getTemplate("notice",s.notice);this.dropdown.element.innerHTML=l.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0);else{var h=(i===o||i===c)&&t&&!t.value,u=!this._isTextElement&&this._isSearching,p=this._canSearch&&s.response;h&&u?(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))):p&&this._handleSearch(this.input.rawValue)}this._canSearch=this.config.searchEnabled},e.prototype._onSelectKey=function(e,t){var i=e.ctrlKey,n=e.metaKey;(i||n)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t,i){var n=e.target,r=d.KEY_CODES.ENTER_KEY,s=n&&n.hasAttribute("data-button");if(this._isTextElement&&n&&n.value){var o=this.input.value;this._canAddItem(t,o).response&&(this.hideDropdown(!0),this._addItem({value:o}),this._triggerChange(o),this.clearInput())}if(s&&(this._handleButtonAction(t,n),e.preventDefault()),i){var a=this.dropdown.getChild(".".concat(this.config.classNames.highlightedState));a&&(t[0]&&(t[0].keyCode=r),this._handleChoiceAction(t,a)),e.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),e.preventDefault())},e.prototype._onEscapeKey=function(e){e&&(this.hideDropdown(!0),this.containerOuter.focus())},e.prototype._onDirectionKey=function(e,t){var i=e.keyCode,n=e.metaKey,r=d.KEY_CODES.DOWN_KEY,s=d.KEY_CODES.PAGE_UP_KEY,o=d.KEY_CODES.PAGE_DOWN_KEY;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=i===r||i===o?1:-1,c="[data-choice-selectable]",l=void 0;if(n||i===o||i===s)l=a>0?this.dropdown.element.querySelector("".concat(c,":last-of-type")):this.dropdown.element.querySelector(c);else{var h=this.dropdown.element.querySelector(".".concat(this.config.classNames.highlightedState));l=h?(0,f.getAdjacentEl)(h,c,a):this.dropdown.element.querySelector(c)}l&&((0,f.isScrolledIntoView)(l,this.choiceList.element,a)||this.choiceList.scrollToChildElement(l,a),this._highlightChoice(l)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){var n=e.target;this._isSelectOneElement||n.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation()),this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(_&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},e.prototype._onFocus=function(e){var t,i=this,n=e.target;n&&this.containerOuter.element.contains(n)&&((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},e.prototype._onBlur=function(e){var t,i=this,n=e.target;if(n&&this.containerOuter.element.contains(n)&&!this._isScrollingOnIe){var r=this._store.activeItems.some((function(e){return e.highlighted}));((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),r&&i.unhighlightAll(),i.hideDropdown(!0))},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.removeFocusState(),(n===i.input.element||n===i.containerOuter.element&&!i._canSearch)&&i.hideDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),i.hideDropdown(!0),r&&i.unhighlightAll())},t)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},e.prototype._onFormReset=function(){this._store.dispatch((0,h.resetTo)(this._initialState))},e.prototype._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll("[data-choice-selectable]"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll(".".concat(this.config.classNames.highlightedState))).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute("aria-selected","true"),this.passedElement.triggerEvent(d.EVENTS.highlightChoice,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},e.prototype._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.choiceId,s=void 0===r?-1:r,o=e.groupId,a=void 0===o?-1:o,c=e.customProperties,h=void 0===c?{}:c,u=e.placeholder,p=void 0!==u&&u,f=e.keyCode,m=void 0===f?-1:f,v="string"==typeof t?t.trim():t,g=this._store.items,_=n||v,y=s||-1,E=a>=0?this._store.getGroupById(a):null,b=g?g.length+1:1;this.config.prependValue&&(v=this.config.prependValue+v.toString()),this.config.appendValue&&(v+=this.config.appendValue.toString()),this._store.dispatch((0,l.addItem)({value:v,label:_,id:b,choiceId:y,groupId:a,customProperties:h,placeholder:p,keyCode:m})),this._isSelectOneElement&&this.removeActiveItems(b),this.passedElement.triggerEvent(d.EVENTS.addItem,{id:b,value:v,label:_,customProperties:h,groupValue:E&&E.value?E.value:null,keyCode:m})},e.prototype._removeItem=function(e){var t=e.id,i=e.value,n=e.label,r=e.customProperties,s=e.choiceId,o=e.groupId,a=o&&o>=0?this._store.getGroupById(o):null;t&&s&&(this._store.dispatch((0,l.removeItem)(t,s)),this.passedElement.triggerEvent(d.EVENTS.removeItem,{id:t,value:i,label:n,customProperties:r,groupValue:a&&a.value?a.value:null}))},e.prototype._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.isSelected,s=void 0!==r&&r,o=e.isDisabled,c=void 0!==o&&o,l=e.groupId,h=void 0===l?-1:l,u=e.customProperties,d=void 0===u?{}:u,p=e.placeholder,f=void 0!==p&&p,m=e.keyCode,v=void 0===m?-1:m;if(null!=t){var g=this._store.choices,_=n||t,y=g?g.length+1:1,E="".concat(this._baseId,"-").concat(this._idNames.itemChoice,"-").concat(y);this._store.dispatch((0,a.addChoice)({id:y,groupId:h,elementId:E,value:t,label:_,disabled:c,customProperties:d,placeholder:f,keyCode:v})),s&&this._addItem({value:t,label:_,choiceId:y,customProperties:d,placeholder:f,keyCode:v})}},e.prototype._addGroup=function(e){var t=this,i=e.group,n=e.id,r=e.valueKey,s=void 0===r?"value":r,o=e.labelKey,a=void 0===o?"label":o,l=(0,f.isType)("Object",i)?i.choices:Array.from(i.getElementsByTagName("OPTION")),h=n||Math.floor((new Date).valueOf()*Math.random()),u=!!i.disabled&&i.disabled;l?(this._store.dispatch((0,c.addGroup)({value:i.label,id:h,active:!0,disabled:u})),l.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[s],label:(0,f.isType)("Object",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:h,customProperties:e.customProperties,placeholder:e.placeholder})}))):this._store.dispatch((0,c.addGroup)({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},e.prototype._getTemplate=function(e){for(var t,i=[],r=1;r0?this.element.scrollTop+o-r:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t,r=n>1?n:1;this.element.scrollTop=e+r},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t,r=n>1?n:1;this.element.scrollTop=e-r},e.prototype._animateScroll=function(e,t){var i=this,r=n.SCROLLING_SPEED,s=this.element.scrollTop,o=!1;t>0?(this._scrollDown(s,r,e),se&&(o=!0)),o&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();t.default=r},730:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(799),r=function(){function e(e){var t=e.element,i=e.classNames;if(this.element=t,this.classNames=i,!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement))throw new TypeError("Invalid element passed");this.isDisabled=!1}return Object.defineProperty(e.prototype,"isActive",{get:function(){return"active"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dir",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){this.element.classList.add(this.classNames.input),this.element.hidden=!0,this.element.tabIndex=-1;var e=this.element.getAttribute("style");e&&this.element.setAttribute("data-choice-orig-style",e),this.element.setAttribute("data-choice","active")},e.prototype.reveal=function(){this.element.classList.remove(this.classNames.input),this.element.hidden=!1,this.element.removeAttribute("tabindex");var e=this.element.getAttribute("data-choice-orig-style");e?(this.element.removeAttribute("data-choice-orig-style"),this.element.setAttribute("style",e)):this.element.removeAttribute("style"),this.element.removeAttribute("data-choice"),this.element.value=this.element.value},e.prototype.enable=function(){this.element.removeAttribute("disabled"),this.element.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute("disabled",""),this.element.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){(0,n.dispatchEvent)(this.element,e,t)},e}();t.default=r},541:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.delimiter,s=e.call(this,{element:i,classNames:n})||this;return s.delimiter=r,s}return r(t,e),Object.defineProperty(t.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!1,configurable:!0}),t}(s(i(730)).default);t.default=o},982:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.template,s=e.call(this,{element:i,classNames:n})||this;return s.template=r,s}return r(t,e),Object.defineProperty(t.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"optionGroups",{get:function(){return Array.from(this.element.getElementsByTagName("OPTGROUP"))},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"options",{get:function(){return Array.from(this.element.options)},set:function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){return n=e,r=t.template(n),void i.appendChild(r);var n,r})),this.appendDocFragment(i)},enumerable:!1,configurable:!0}),t.prototype.appendDocFragment=function(e){this.element.innerHTML="",this.element.appendChild(e)},t}(s(i(730)).default);t.default=o},883:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.SCROLLING_SPEED=t.SELECT_MULTIPLE_TYPE=t.SELECT_ONE_TYPE=t.TEXT_TYPE=t.KEY_CODES=t.ACTION_TYPES=t.EVENTS=void 0,t.EVENTS={showDropdown:"showDropdown",hideDropdown:"hideDropdown",change:"change",choice:"choice",search:"search",addItem:"addItem",removeItem:"removeItem",highlightItem:"highlightItem",highlightChoice:"highlightChoice",unhighlightItem:"unhighlightItem"},t.ACTION_TYPES={ADD_CHOICE:"ADD_CHOICE",FILTER_CHOICES:"FILTER_CHOICES",ACTIVATE_CHOICES:"ACTIVATE_CHOICES",CLEAR_CHOICES:"CLEAR_CHOICES",ADD_GROUP:"ADD_GROUP",ADD_ITEM:"ADD_ITEM",REMOVE_ITEM:"REMOVE_ITEM",HIGHLIGHT_ITEM:"HIGHLIGHT_ITEM",CLEAR_ALL:"CLEAR_ALL",RESET_TO:"RESET_TO",SET_IS_LOADING:"SET_IS_LOADING"},t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},t.TEXT_TYPE="text",t.SELECT_ONE_TYPE="select-one",t.SELECT_MULTIPLE_TYPE="select-multiple",t.SCROLLING_SPEED=4},789:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CONFIG=t.DEFAULT_CLASSNAMES=void 0;var n=i(799);t.DEFAULT_CLASSNAMES={containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",selectedState:"is-selected",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"},t.DEFAULT_CONFIG={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilter:null,removeItems:!0,removeItemButton:!1,editItems:!1,allowHTML:!0,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'.concat((0,n.sanitise)(e),'"')},maxItemText:function(e){return"Only ".concat(e," values can be added")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:"",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:t.DEFAULT_CLASSNAMES}},18:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},978:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},948:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},359:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},285:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},533:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},187:function(e,t,i){var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,r)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||n(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),r(i(18),t),r(i(978),t),r(i(948),t),r(i(359),t),r(i(285),t),r(i(533),t),r(i(287),t),r(i(132),t),r(i(837),t),r(i(598),t),r(i(369),t),r(i(37),t),r(i(47),t),r(i(923),t),r(i(876),t)},287:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},132:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},837:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},598:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},37:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},369:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},47:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},923:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},876:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},799:function(e,t){var i;Object.defineProperty(t,"__esModule",{value:!0}),t.parseCustomProperties=t.diff=t.cloneObject=t.existsInArray=t.dispatchEvent=t.sortByScore=t.sortByAlpha=t.strToEl=t.sanitise=t.isScrolledIntoView=t.getAdjacentEl=t.wrap=t.isType=t.getType=t.generateId=t.generateChars=t.getRandomNumber=void 0,t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.generateChars=function(e){return Array.from({length:e},(function(){return(0,t.getRandomNumber)(0,36).toString(36)})).join("")},t.generateId=function(e,i){var n=e.id||e.name&&"".concat(e.name,"-").concat((0,t.generateChars)(2))||(0,t.generateChars)(4);return n=n.replace(/(:|\.|\[|\]|,)/g,""),"".concat(i,"-").concat(n)},t.getType=function(e){return Object.prototype.toString.call(e).slice(8,-1)},t.isType=function(e,i){return null!=i&&(0,t.getType)(i)===e},t.wrap=function(e,t){return void 0===t&&(t=document.createElement("div")),e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t)),t.appendChild(e)},t.getAdjacentEl=function(e,t,i){void 0===i&&(i=1);for(var n="".concat(i>0?"next":"previous","ElementSibling"),r=e[n];r;){if(r.matches(t))return r;r=r[n]}return r},t.isScrolledIntoView=function(e,t,i){return void 0===i&&(i=1),!!e&&(i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop)},t.sanitise=function(e){return"string"!=typeof e?e:e.replace(/&/g,"&").replace(/>/g,">").replace(/-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(o.choiceId),10)&&(t.selected=!0),t})):e;case"REMOVE_ITEM":var a=n;return a.choiceId&&a.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(a.choiceId),10)&&(t.selected=!1),t})):e;case"FILTER_CHOICES":var c=n;return e.map((function(e){var t=e;return t.active=c.results.some((function(e){var i=e.item,n=e.score;return i.id===t.id&&(t.score=n,!0)})),t}));case"ACTIVATE_CHOICES":var l=n;return e.map((function(e){var t=e;return t.active=l.active,t}));case"CLEAR_CHOICES":return t.defaultState;default:return e}}},871:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r0?"treeitem":"option"),Object.assign(E.dataset,{choice:"",id:d,value:p,selectText:i}),g?(E.classList.add(h),E.dataset.choiceDisabled="",E.setAttribute("aria-disabled","true")):(E.classList.add(c),E.dataset.choiceSelectable=""),E},input:function(e,t){var i=e.classNames,n=i.input,r=i.inputCloned,s=Object.assign(document.createElement("input"),{type:"search",name:"search_terms",className:"".concat(n," ").concat(r),autocomplete:"off",autocapitalize:"off",spellcheck:!1});return s.setAttribute("role","textbox"),s.setAttribute("aria-autocomplete","list"),s.setAttribute("aria-label",t),s},dropdown:function(e){var t=e.classNames,i=t.list,n=t.listDropdown,r=document.createElement("div");return r.classList.add(i,n),r.setAttribute("aria-expanded","false"),r},notice:function(e,t,i){var n,r=e.allowHTML,s=e.classNames,o=s.item,a=s.itemChoice,c=s.noResults,l=s.noChoices;void 0===i&&(i="");var h=[o,a];return"no-choices"===i?h.push(l):"no-results"===i&&h.push(c),Object.assign(document.createElement("div"),((n={})[r?"innerHTML":"innerText"]=t,n.className=h.join(" "),n))},option:function(e){var t=e.label,i=e.value,n=e.customProperties,r=e.active,s=e.disabled,o=new Option(t,i,!1,r);return n&&(o.dataset.customProperties="".concat(n)),o.disabled=!!s,o}};t.default=i},996:function(e){var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===i}(e)}(e)},i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function n(e,t){return!1!==t.clone&&t.isMergeableObject(e)?a((i=e,Array.isArray(i)?[]:{}),e,t):e;var i}function r(e,t,i){return e.concat(t).map((function(e){return n(e,i)}))}function s(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return e.propertyIsEnumerable(t)})):[]}(e))}function o(e,t){try{return t in e}catch(e){return!1}}function a(e,i,c){(c=c||{}).arrayMerge=c.arrayMerge||r,c.isMergeableObject=c.isMergeableObject||t,c.cloneUnlessOtherwiseSpecified=n;var l=Array.isArray(i);return l===Array.isArray(e)?l?c.arrayMerge(e,i,c):function(e,t,i){var r={};return i.isMergeableObject(e)&&s(e).forEach((function(t){r[t]=n(e[t],i)})),s(t).forEach((function(s){(function(e,t){return o(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,s)||(o(e,s)&&i.isMergeableObject(t[s])?r[s]=function(e,t){if(!t.customMerge)return a;var i=t.customMerge(e);return"function"==typeof i?i:a}(s,i)(e[s],t[s],i):r[s]=n(t[s],i))})),r}(e,i,c):n(i,c)}a.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,i){return a(e,i,t)}),{})};var c=a;e.exports=c},221:function(e,t,i){function n(e){return Array.isArray?Array.isArray(e):"[object Array]"===l(e)}function r(e){return"string"==typeof e}function s(e){return"number"==typeof e}function o(e){return"object"==typeof e}function a(e){return null!=e}function c(e){return!e.trim().length}function l(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}i.r(t),i.d(t,{default:function(){return R}});const h=Object.prototype.hasOwnProperty;class u{constructor(e){this._keys=[],this._keyMap={};let t=0;e.forEach((e=>{let i=d(e);t+=i.weight,this._keys.push(i),this._keyMap[i.id]=i,t+=i.weight})),this._keys.forEach((e=>{e.weight/=t}))}get(e){return this._keyMap[e]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function d(e){let t=null,i=null,s=null,o=1,a=null;if(r(e)||n(e))s=e,t=p(e),i=f(e);else{if(!h.call(e,"name"))throw new Error("Missing name property in key");const n=e.name;if(s=n,h.call(e,"weight")&&(o=e.weight,o<=0))throw new Error((e=>`Property 'weight' in key '${e}' must be a positive integer`)(n));t=p(n),i=f(n),a=e.getFn}return{path:t,id:i,weight:o,src:s,getFn:a}}function p(e){return n(e)?e:e.split(".")}function f(e){return n(e)?e.join("."):e}var m={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(e,t)=>e.score===t.score?e.idx{if(a(e))if(t[u]){const d=e[t[u]];if(!a(d))return;if(u===t.length-1&&(r(d)||s(d)||function(e){return!0===e||!1===e||function(e){return o(e)&&null!==e}(e)&&"[object Boolean]"==l(e)}(d)))i.push(function(e){return null==e?"":function(e){if("string"==typeof e)return e;let t=e+"";return"0"==t&&1/e==-1/0?"-0":t}(e)}(d));else if(n(d)){c=!0;for(let e=0,i=d.length;e{this._keysMap[e.id]=t}))}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,r(this.docs[0])?this.docs.forEach(((e,t)=>{this._addString(e,t)})):this.docs.forEach(((e,t)=>{this._addObject(e,t)})),this.norm.clear())}add(e){const t=this.size();r(e)?this._addString(e,t):this._addObject(e,t)}removeAt(e){this.records.splice(e,1);for(let t=e,i=this.size();t{let o=t.getFn?t.getFn(e):this.getFn(e,t.path);if(a(o))if(n(o)){let e=[];const t=[{nestedArrIndex:-1,value:o}];for(;t.length;){const{nestedArrIndex:i,value:s}=t.pop();if(a(s))if(r(s)&&!c(s)){let t={v:s,i:i,n:this.norm.get(s)};e.push(t)}else n(s)&&s.forEach(((e,i)=>{t.push({nestedArrIndex:i,value:e})}))}i.$[s]=e}else if(r(o)&&!c(o)){let e={v:o,n:this.norm.get(o)};i.$[s]=e}})),this.records.push(i)}toJSON(){return{keys:this.keys,records:this.records}}}function _(e,t,{getFn:i=m.getFn,fieldNormWeight:n=m.fieldNormWeight}={}){const r=new g({getFn:i,fieldNormWeight:n});return r.setKeys(e.map(d)),r.setSources(t),r.create(),r}function y(e,{errors:t=0,currentLocation:i=0,expectedLocation:n=0,distance:r=m.distance,ignoreLocation:s=m.ignoreLocation}={}){const o=t/e.length;if(s)return o;const a=Math.abs(n-i);return r?o+a/r:a?1:o}const E=32;function b(e){let t={};for(let i=0,n=e.length;i{this.chunks.push({pattern:e,alphabet:b(e),startIndex:t})},h=this.pattern.length;if(h>E){let e=0;const t=h%E,i=h-t;for(;e{const{isMatch:f,score:v,indices:g}=function(e,t,i,{location:n=m.location,distance:r=m.distance,threshold:s=m.threshold,findAllMatches:o=m.findAllMatches,minMatchCharLength:a=m.minMatchCharLength,includeMatches:c=m.includeMatches,ignoreLocation:l=m.ignoreLocation}={}){if(t.length>E)throw new Error("Pattern length exceeds max of 32.");const h=t.length,u=e.length,d=Math.max(0,Math.min(n,u));let p=s,f=d;const v=a>1||c,g=v?Array(u):[];let _;for(;(_=e.indexOf(t,f))>-1;){let e=y(t,{currentLocation:_,expectedLocation:d,distance:r,ignoreLocation:l});if(p=Math.min(e,p),f=_+h,v){let e=0;for(;e=c;s-=1){let o=s-1,a=i[e.charAt(o)];if(v&&(g[o]=+!!a),_[s]=(_[s+1]<<1|1)&a,n&&(_[s]|=(b[s+1]|b[s])<<1|1|b[s+1]),_[s]&I&&(S=y(t,{errors:n,currentLocation:o,expectedLocation:d,distance:r,ignoreLocation:l}),S<=p)){if(p=S,f=o,f<=d)break;c=Math.max(1,2*d-f)}}if(y(t,{errors:n+1,currentLocation:d,expectedLocation:d,distance:r,ignoreLocation:l})>p)break;b=_}const C={isMatch:f>=0,score:Math.max(.001,S)};if(v){const e=function(e=[],t=m.minMatchCharLength){let i=[],n=-1,r=-1,s=0;for(let o=e.length;s=t&&i.push([n,r]),n=-1)}return e[s-1]&&s-n>=t&&i.push([n,s-1]),i}(g,a);e.length?c&&(C.indices=e):C.isMatch=!1}return C}(e,t,d,{location:n+p,distance:r,threshold:s,findAllMatches:o,minMatchCharLength:a,includeMatches:i,ignoreLocation:c});f&&(u=!0),h+=v,f&&g&&(l=[...l,...g])}));let d={isMatch:u,score:u?h/this.chunks.length:1};return u&&i&&(d.indices=l),d}}class O{constructor(e){this.pattern=e}static isMultiMatch(e){return I(e,this.multiRegex)}static isSingleMatch(e){return I(e,this.singleRegex)}search(){}}function I(e,t){const i=e.match(t);return i?i[1]:null}class C extends O{constructor(e,{location:t=m.location,threshold:i=m.threshold,distance:n=m.distance,includeMatches:r=m.includeMatches,findAllMatches:s=m.findAllMatches,minMatchCharLength:o=m.minMatchCharLength,isCaseSensitive:a=m.isCaseSensitive,ignoreLocation:c=m.ignoreLocation}={}){super(e),this._bitapSearch=new S(e,{location:t,threshold:i,distance:n,includeMatches:r,findAllMatches:s,minMatchCharLength:o,isCaseSensitive:a,ignoreLocation:c})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(e){return this._bitapSearch.searchIn(e)}}class T extends O{constructor(e){super(e)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(e){let t,i=0;const n=[],r=this.pattern.length;for(;(t=e.indexOf(this.pattern,i))>-1;)i=t+r,n.push([t,i-1]);const s=!!n.length;return{isMatch:s,score:s?0:1,indices:n}}}const L=[class extends O{constructor(e){super(e)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(e){const t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},T,class extends O{constructor(e){super(e)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(e){const t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(e){const t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(e){const t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(e){const t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(e){const t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},C],w=L.length,A=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,M=new Set([C.type,T.type]);const P=[];function x(e,t){for(let i=0,n=P.length;i!(!e.$and&&!e.$or),j=e=>({[N]:Object.keys(e).map((t=>({[t]:e[t]})))});function F(e,t,{auto:i=!0}={}){const s=e=>{let a=Object.keys(e);const c=(e=>!!e.$path)(e);if(!c&&a.length>1&&!D(e))return s(j(e));if((e=>!n(e)&&o(e)&&!D(e))(e)){const n=c?e.$path:a[0],s=c?e.$val:e[n];if(!r(s))throw new Error((e=>`Invalid value for key ${e}`)(n));const o={keyId:f(n),pattern:s};return i&&(o.searcher=x(s,t)),o}let l={children:[],operator:a[0]};return a.forEach((t=>{const i=e[t];n(i)&&i.forEach((e=>{l.children.push(s(e))}))})),l};return D(e)||(e=j(e)),s(e)}function k(e,t){const i=e.matches;t.matches=[],a(i)&&i.forEach((e=>{if(!a(e.indices)||!e.indices.length)return;const{indices:i,value:n}=e;let r={indices:i,value:n};e.key&&(r.key=e.key.src),e.idx>-1&&(r.refIndex=e.idx),t.matches.push(r)}))}function K(e,t){t.score=e.score}class R{constructor(e,t={},i){this.options={...m,...t},this.options.useExtendedSearch,this._keyStore=new u(this.options.keys),this.setCollection(e,i)}setCollection(e,t){if(this._docs=e,t&&!(t instanceof g))throw new Error("Incorrect 'index' type");this._myIndex=t||_(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(e){a(e)&&(this._docs.push(e),this._myIndex.add(e))}remove(e=(()=>!1)){const t=[];for(let i=0,n=this._docs.length;i{let i=1;e.matches.forEach((({key:e,norm:n,score:r})=>{const s=e?e.weight:null;i*=Math.pow(0===r&&s?Number.EPSILON:r,(s||1)*(t?1:n))})),e.score=i}))}(l,{ignoreFieldNorm:c}),o&&l.sort(a),s(t)&&t>-1&&(l=l.slice(0,t)),function(e,t,{includeMatches:i=m.includeMatches,includeScore:n=m.includeScore}={}){const r=[];return i&&r.push(k),n&&r.push(K),e.map((e=>{const{idx:i}=e,n={item:t[i],refIndex:i};return r.length&&r.forEach((t=>{t(e,n)})),n}))}(l,this._docs,{includeMatches:i,includeScore:n})}_searchStringList(e){const t=x(e,this.options),{records:i}=this._myIndex,n=[];return i.forEach((({v:e,i:i,n:r})=>{if(!a(e))return;const{isMatch:s,score:o,indices:c}=t.searchIn(e);s&&n.push({item:e,idx:i,matches:[{score:o,value:e,norm:r,indices:c}]})})),n}_searchLogical(e){const t=F(e,this.options),i=(e,t,n)=>{if(!e.children){const{keyId:i,searcher:r}=e,s=this._findMatches({key:this._keyStore.get(i),value:this._myIndex.getValueForItemAtKeyId(t,i),searcher:r});return s&&s.length?[{idx:n,item:t,matches:s}]:[]}const r=[];for(let s=0,o=e.children.length;s{if(a(e)){let o=i(t,e,n);o.length&&(r[n]||(r[n]={idx:n,item:e,matches:[]},s.push(r[n])),o.forEach((({matches:e})=>{r[n].matches.push(...e)})))}})),s}_searchObjectList(e){const t=x(e,this.options),{keys:i,records:n}=this._myIndex,r=[];return n.forEach((({$:e,i:n})=>{if(!a(e))return;let s=[];i.forEach(((i,n)=>{s.push(...this._findMatches({key:i,value:e[n],searcher:t}))})),s.length&&r.push({idx:n,item:e,matches:s})})),r}_findMatches({key:e,value:t,searcher:i}){if(!a(t))return[];let r=[];if(n(t))t.forEach((({v:t,i:n,n:s})=>{if(!a(t))return;const{isMatch:o,score:c,indices:l}=i.searchIn(t);o&&r.push({score:c,key:e,value:t,idx:n,norm:s,indices:l})}));else{const{v:n,n:s}=t,{isMatch:o,score:a,indices:c}=i.searchIn(n);o&&r.push({score:a,key:e,value:n,norm:s,indices:c})}return r}}R.version="6.6.2",R.createIndex=_,R.parseIndex=function(e,{getFn:t=m.getFn,fieldNormWeight:i=m.fieldNormWeight}={}){const{keys:n,records:r}=e,s=new g({getFn:t,fieldNormWeight:i});return s.setKeys(n),s.setIndexRecords(r),s},R.config=m,R.parseQuery=F,function(...e){P.push(...e)}(class{constructor(e,{isCaseSensitive:t=m.isCaseSensitive,includeMatches:i=m.includeMatches,minMatchCharLength:n=m.minMatchCharLength,ignoreLocation:r=m.ignoreLocation,findAllMatches:s=m.findAllMatches,location:o=m.location,threshold:a=m.threshold,distance:c=m.distance}={}){this.query=null,this.options={isCaseSensitive:t,includeMatches:i,minMatchCharLength:n,findAllMatches:s,ignoreLocation:r,location:o,threshold:a,distance:c},this.pattern=t?e:e.toLowerCase(),this.query=function(e,t={}){return e.split("|").map((e=>{let i=e.trim().split(A).filter((e=>e&&!!e.trim())),n=[];for(let e=0,r=i.length;e1)return 1;for(var i=t,r=0;r<8;r++){var n=this.sampleCurveX(i)-t;if(Math.abs(n)n?o=i:a=i,i=.5*(a-o)+o;return i},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}};const n=r(.25,.1,.25,1);function s(t,e,i){return Math.min(i,Math.max(e,t))}function o(t,e,i){const r=i-e,n=((t-e)%r+r)%r+e;return n===e?i:n}function a(t,...e){for(const i of e)for(const e in i)t[e]=i[e];return t}let l=1;function c(t,e){t.forEach((t=>{e[t]&&(e[t]=e[t].bind(e))}))}function h(t,e,i){const r={};for(const n in t)r[n]=e.call(i||this,t[n],n,t);return r}function u(t,e,i){const r={};for(const n in t)e.call(i||this,t[n],n,t)&&(r[n]=t[n]);return r}function p(t){return Array.isArray(t)?t.map(p):"object"==typeof t&&t?h(t,p):t}const d={};function m(t){d[t]||("undefined"!=typeof console&&console.warn(t),d[t]=!0)}function f(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}function g(t){let e=0;for(let i,r,n=0,s=t.length,o=s-1;n@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\]|\\.)*)\")))?/g,((t,i,r,n)=>{const s=r||n;return e[i]=!s||s.toLowerCase(),""})),e["max-age"]){const t=parseInt(e["max-age"],10);isNaN(t)?delete e["max-age"]:e["max-age"]=t}return e}let x,v,b=null;function w(t){if(null==b){const e=t.navigator?t.navigator.userAgent:null;b=!!t.safari||!(!e||!(/\b(iPad|iPhone|iPod)\b/.test(e)||e.match("Safari")&&!e.match("Chrome")))}return b}function T(t){return"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap}const E={now:"undefined"!=typeof performance&&performance&&performance.now?performance.now.bind(performance):Date.now.bind(Date),frame(t){const e=requestAnimationFrame(t);return{cancel:()=>cancelAnimationFrame(e)}},getImageData(t,e=0){const i=window.document.createElement("canvas"),r=i.getContext("2d");if(!r)throw new Error("failed to create canvas 2d context");return i.width=t.width,i.height=t.height,r.drawImage(t,0,0,t.width,t.height),r.getImageData(-e,-e,t.width+2*e,t.height+2*e)},resolveURL:t=>(x||(x=document.createElement("a")),x.href=t,x.href),hardwareConcurrency:"undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,get prefersReducedMotion(){return!!matchMedia&&(null==v&&(v=matchMedia("(prefers-reduced-motion: reduce)")),v.matches)}};var S=I;function I(t,e){this.x=t,this.y=e}I.prototype={clone:function(){return new I(this.x,this.y)},add:function(t){return this.clone()._add(t)},sub:function(t){return this.clone()._sub(t)},multByPoint:function(t){return this.clone()._multByPoint(t)},divByPoint:function(t){return this.clone()._divByPoint(t)},mult:function(t){return this.clone()._mult(t)},div:function(t){return this.clone()._div(t)},rotate:function(t){return this.clone()._rotate(t)},rotateAround:function(t,e){return this.clone()._rotateAround(t,e)},matMult:function(t){return this.clone()._matMult(t)},unit:function(){return this.clone()._unit()},perp:function(){return this.clone()._perp()},round:function(){return this.clone()._round()},mag:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals:function(t){return this.x===t.x&&this.y===t.y},dist:function(t){return Math.sqrt(this.distSqr(t))},distSqr:function(t){var e=t.x-this.x,i=t.y-this.y;return e*e+i*i},angle:function(){return Math.atan2(this.y,this.x)},angleTo:function(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith:function(t){return this.angleWithSep(t.x,t.y)},angleWithSep:function(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult:function(t){var e=t[2]*this.x+t[3]*this.y;return this.x=t[0]*this.x+t[1]*this.y,this.y=e,this},_add:function(t){return this.x+=t.x,this.y+=t.y,this},_sub:function(t){return this.x-=t.x,this.y-=t.y,this},_mult:function(t){return this.x*=t,this.y*=t,this},_div:function(t){return this.x/=t,this.y/=t,this},_multByPoint:function(t){return this.x*=t.x,this.y*=t.y,this},_divByPoint:function(t){return this.x/=t.x,this.y/=t.y,this},_unit:function(){return this._div(this.mag()),this},_perp:function(){var t=this.y;return this.y=this.x,this.x=-t,this},_rotate:function(t){var e=Math.cos(t),i=Math.sin(t),r=i*this.x+e*this.y;return this.x=e*this.x-i*this.y,this.y=r,this},_rotateAround:function(t,e){var i=Math.cos(t),r=Math.sin(t),n=e.y+r*(this.x-e.x)+i*(this.y-e.y);return this.x=e.x+i*(this.x-e.x)-r*(this.y-e.y),this.y=n,this},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}},I.convert=function(t){return t instanceof I?t:Array.isArray(t)?new I(t[0],t[1]):t};const A={MAX_PARALLEL_IMAGE_REQUESTS:16,REGISTERED_PROTOCOLS:{}},z="mapbox-tiles";let C,M,k=500,P=50;function D(){"undefined"==typeof caches||C||(C=caches.open(z))}let L=1/0;const B={supported:!1,testSupport:function(t){!O&&F&&(U?V(t):R=t)}};let R,F,O=!1,U=!1;function V(t){const e=t.createTexture();t.bindTexture(t.TEXTURE_2D,e);try{if(t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,F),t.isContextLost())return;B.supported=!0}catch(t){}t.deleteTexture(e),O=!0}"undefined"!=typeof document&&(F=document.createElement("img"),F.onload=function(){R&&V(R),R=null,U=!0},F.onerror=function(){O=!0,R=null},F.src="data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=");const N={Unknown:"Unknown",Style:"Style",Source:"Source",Tile:"Tile",Glyphs:"Glyphs",SpriteImage:"SpriteImage",SpriteJSON:"SpriteJSON",Image:"Image"};"function"==typeof Object.freeze&&Object.freeze(N);class G extends Error{constructor(t,e,i,r){super(`AJAXError: ${e} (${t}): ${i}`),this.status=t,this.statusText=e,this.url=i,this.body=r}}const $=_()?()=>self.worker&&self.worker.referrer:()=>("blob:"===window.location.protocol?window.parent:window).location.href;function q(t,e){const i=new AbortController,r=new Request(t.url,{method:t.method||"GET",body:t.body,credentials:t.credentials,headers:t.headers,referrer:$(),signal:i.signal});let n=!1,s=!1;return"json"===t.type&&r.headers.set("Accept","application/json"),((i,o,a)=>{if(s)return;const l=Date.now();fetch(r).then((i=>i.ok?((i,o,a)=>{("arrayBuffer"===t.type?i.arrayBuffer():"json"===t.type?i.json():i.text()).then((t=>{s||(o&&a&&function(t,e,i){if(D(),!C)return;const r={status:e.status,statusText:e.statusText,headers:new Headers};e.headers.forEach(((t,e)=>r.headers.set(e,t)));const n=y(e.headers.get("Cache-Control")||"");n["no-store"]||(n["max-age"]&&r.headers.set("Expires",new Date(i+1e3*n["max-age"]).toUTCString()),new Date(r.headers.get("Expires")).getTime()-i<42e4||function(t,e){if(void 0===M)try{new Response(new ReadableStream),M=!0}catch(t){M=!1}M?e(t.body):t.blob().then(e)}(e,(e=>{const i=new Response(e,r);D(),C&&C.then((e=>e.put(function(t){const e=t.indexOf("?");return e<0?t:t.slice(0,e)}(t.url),i))).catch((t=>m(t.message)))})))}(r,o,a),n=!0,e(null,t,i.headers.get("Cache-Control"),i.headers.get("Expires")))})).catch((t=>{s||e(new Error(t.message))}))})(i,null,l):i.blob().then((r=>e(new G(i.status,i.statusText,t.url,r)))))).catch((t=>{20!==t.code&&e(new Error(t.message))}))})(),{cancel:()=>{s=!0,n||i.abort()}}}const j=function(t,e){if(/:\/\//.test(t.url)&&!/^https?:|^file:/.test(t.url)){if(_()&&self.worker&&self.worker.actor)return self.worker.actor.send("getResource",t,e);if(!_()){const i=t.url.substring(0,t.url.indexOf("://"));return(A.REGISTERED_PROTOCOLS[i]||q)(t,e)}}if(!(/^file:/.test(i=t.url)||/^file:/.test($())&&!/^\w+:/.test(i))){if(fetch&&Request&&AbortController&&Object.prototype.hasOwnProperty.call(Request.prototype,"signal"))return q(t,e);if(_()&&self.worker&&self.worker.actor)return self.worker.actor.send("getResource",t,e,void 0,!0)}var i;return function(t,e){const i=new XMLHttpRequest;i.open(t.method||"GET",t.url,!0),"arrayBuffer"===t.type&&(i.responseType="arraybuffer");for(const e in t.headers)i.setRequestHeader(e,t.headers[e]);return"json"===t.type&&(i.responseType="text",i.setRequestHeader("Accept","application/json")),i.withCredentials="include"===t.credentials,i.onerror=()=>{e(new Error(i.statusText))},i.onload=()=>{if((i.status>=200&&i.status<300||0===i.status)&&null!==i.response){let r=i.response;if("json"===t.type)try{r=JSON.parse(i.response)}catch(t){return e(t)}e(null,r,i.getResponseHeader("Cache-Control"),i.getResponseHeader("Expires"))}else{const r=new Blob([i.response],{type:i.getResponseHeader("Content-Type")});e(new G(i.status,i.statusText,t.url,r))}},i.send(t.body),{cancel:()=>i.abort()}}(t,e)},Z=function(t,e){return j(a(t,{type:"arrayBuffer"}),e)};function X(t){const e=window.document.createElement("a");return e.href=t,e.protocol===window.document.location.protocol&&e.host===window.document.location.host}const W="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=";let H,K;H=[],K=0;const J=function(t,e){if(B.supported&&(t.headers||(t.headers={}),t.headers.accept="image/webp,*/*"),K>=A.MAX_PARALLEL_IMAGE_REQUESTS){const i={requestParameters:t,callback:e,cancelled:!1,cancel(){this.cancelled=!0}};return H.push(i),i}K++;let i=!1;const r=()=>{if(!i)for(i=!0,K--;H.length&&K{r(),t?e(t):i&&function(t,e){"function"==typeof createImageBitmap?function(t,e){const i=new Blob([new Uint8Array(t)],{type:"image/png"});createImageBitmap(i).then((t=>{e(null,t)})).catch((t=>{e(new Error(`Could not load image because of ${t.message}. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.`))}))}(t,e):function(t,e){const i=new Image;i.onload=()=>{e(null,i),URL.revokeObjectURL(i.src),i.onload=null,window.requestAnimationFrame((()=>{i.src=W}))},i.onerror=()=>e(new Error("Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported."));const r=new Blob([new Uint8Array(t)],{type:"image/png"});i.src=t.byteLength?URL.createObjectURL(r):W}(t,e)}(i,((t,i)=>{null!=t?e(t):null!=i&&e(null,i,{cacheControl:n,expires:s})}))}));return{cancel:()=>{n.cancel(),r()}}};function Y(t,e,i){i[t]&&-1!==i[t].indexOf(e)||(i[t]=i[t]||[],i[t].push(e))}function Q(t,e,i){if(i&&i[t]){const r=i[t].indexOf(e);-1!==r&&i[t].splice(r,1)}}class tt{constructor(t,e={}){a(this,e),this.type=t}}class et extends tt{constructor(t,e={}){super("error",a({error:t},e))}}class it{on(t,e){return this._listeners=this._listeners||{},Y(t,e,this._listeners),this}off(t,e){return Q(t,e,this._listeners),Q(t,e,this._oneTimeListeners),this}once(t,e){return this._oneTimeListeners=this._oneTimeListeners||{},Y(t,e,this._oneTimeListeners),this}fire(t,e){"string"==typeof t&&(t=new tt(t,e||{}));const i=t.type;if(this.listens(i)){t.target=this;const e=this._listeners&&this._listeners[i]?this._listeners[i].slice():[];for(const i of e)i.call(this,t);const r=this._oneTimeListeners&&this._oneTimeListeners[i]?this._oneTimeListeners[i].slice():[];for(const e of r)Q(i,e,this._oneTimeListeners),e.call(this,t);const n=this._eventedParent;n&&(a(t,"function"==typeof this._eventedParentData?this._eventedParentData():this._eventedParentData),n.fire(t))}else t instanceof et&&console.error(t.error);return this}listens(t){return this._listeners&&this._listeners[t]&&this._listeners[t].length>0||this._oneTimeListeners&&this._oneTimeListeners[t]&&this._oneTimeListeners[t].length>0||this._eventedParent&&this._eventedParent.listens(t)}setEventedParent(t,e){return this._eventedParent=t,this._eventedParentData=e,this}}var rt={$version:8,$root:{version:{required:!0,type:"enum",values:[8]},name:{type:"string"},metadata:{type:"*"},center:{type:"array",value:"number"},zoom:{type:"number"},bearing:{type:"number",default:0,period:360,units:"degrees"},pitch:{type:"number",default:0,units:"degrees"},light:{type:"light"},terrain:{type:"terrain"},sources:{required:!0,type:"sources"},sprite:{type:"string"},glyphs:{type:"string"},transition:{type:"transition"},layers:{required:!0,type:"array",value:"layer"}},sources:{"*":{type:"source"}},source:["source_vector","source_raster","source_raster_dem","source_geojson","source_video","source_image"],source_vector:{type:{required:!0,type:"enum",values:{vector:{}}},url:{type:"string"},tiles:{type:"array",value:"string"},bounds:{type:"array",value:"number",length:4,default:[-180,-85.051129,180,85.051129]},scheme:{type:"enum",values:{xyz:{},tms:{}},default:"xyz"},minzoom:{type:"number",default:0},maxzoom:{type:"number",default:22},attribution:{type:"string"},promoteId:{type:"promoteId"},volatile:{type:"boolean",default:!1},"*":{type:"*"}},source_raster:{type:{required:!0,type:"enum",values:{raster:{}}},url:{type:"string"},tiles:{type:"array",value:"string"},bounds:{type:"array",value:"number",length:4,default:[-180,-85.051129,180,85.051129]},minzoom:{type:"number",default:0},maxzoom:{type:"number",default:22},tileSize:{type:"number",default:512,units:"pixels"},scheme:{type:"enum",values:{xyz:{},tms:{}},default:"xyz"},attribution:{type:"string"},volatile:{type:"boolean",default:!1},"*":{type:"*"}},source_raster_dem:{type:{required:!0,type:"enum",values:{"raster-dem":{}}},url:{type:"string"},tiles:{type:"array",value:"string"},bounds:{type:"array",value:"number",length:4,default:[-180,-85.051129,180,85.051129]},minzoom:{type:"number",default:0},maxzoom:{type:"number",default:22},tileSize:{type:"number",default:512,units:"pixels"},attribution:{type:"string"},encoding:{type:"enum",values:{terrarium:{},mapbox:{}},default:"mapbox"},volatile:{type:"boolean",default:!1},"*":{type:"*"}},source_geojson:{type:{required:!0,type:"enum",values:{geojson:{}}},data:{type:"*"},maxzoom:{type:"number",default:18},attribution:{type:"string"},buffer:{type:"number",default:128,maximum:512,minimum:0},filter:{type:"*"},tolerance:{type:"number",default:.375},cluster:{type:"boolean",default:!1},clusterRadius:{type:"number",default:50,minimum:0},clusterMaxZoom:{type:"number"},clusterMinPoints:{type:"number"},clusterProperties:{type:"*"},lineMetrics:{type:"boolean",default:!1},generateId:{type:"boolean",default:!1},promoteId:{type:"promoteId"}},source_video:{type:{required:!0,type:"enum",values:{video:{}}},urls:{required:!0,type:"array",value:"string"},coordinates:{required:!0,type:"array",length:4,value:{type:"array",length:2,value:"number"}}},source_image:{type:{required:!0,type:"enum",values:{image:{}}},url:{required:!0,type:"string"},coordinates:{required:!0,type:"array",length:4,value:{type:"array",length:2,value:"number"}}},layer:{id:{type:"string",required:!0},type:{type:"enum",values:{fill:{},line:{},symbol:{},circle:{},heatmap:{},"fill-extrusion":{},raster:{},hillshade:{},background:{}},required:!0},metadata:{type:"*"},source:{type:"string"},"source-layer":{type:"string"},minzoom:{type:"number",minimum:0,maximum:24},maxzoom:{type:"number",minimum:0,maximum:24},filter:{type:"filter"},layout:{type:"layout"},paint:{type:"paint"}},layout:["layout_fill","layout_line","layout_circle","layout_heatmap","layout_fill-extrusion","layout_symbol","layout_raster","layout_hillshade","layout_background"],layout_background:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_fill:{"fill-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_circle:{"circle-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_heatmap:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},"layout_fill-extrusion":{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_line:{"line-cap":{type:"enum",values:{butt:{},round:{},square:{}},default:"butt",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"line-join":{type:"enum",values:{bevel:{},round:{},miter:{}},default:"miter",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"line-miter-limit":{type:"number",default:2,requires:[{"line-join":"miter"}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"line-round-limit":{type:"number",default:1.05,requires:[{"line-join":"round"}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"line-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_symbol:{"symbol-placement":{type:"enum",values:{point:{},line:{},"line-center":{}},default:"point",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"symbol-spacing":{type:"number",default:250,minimum:1,units:"pixels",requires:[{"symbol-placement":"line"}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"symbol-avoid-edges":{type:"boolean",default:!1,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"symbol-sort-key":{type:"number",expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"symbol-z-order":{type:"enum",values:{auto:{},"viewport-y":{},source:{}},default:"auto",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-allow-overlap":{type:"boolean",default:!1,requires:["icon-image",{"!":"icon-overlap"}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-overlap":{type:"enum",values:{never:{},always:{},cooperative:{}},requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-ignore-placement":{type:"boolean",default:!1,requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-optional":{type:"boolean",default:!1,requires:["icon-image","text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-rotation-alignment":{type:"enum",values:{map:{},viewport:{},auto:{}},default:"auto",requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-size":{type:"number",default:1,minimum:0,units:"factor of the original icon size",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-text-fit":{type:"enum",values:{none:{},width:{},height:{},both:{}},default:"none",requires:["icon-image","text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-text-fit-padding":{type:"array",value:"number",length:4,default:[0,0,0,0],units:"pixels",requires:["icon-image","text-field",{"icon-text-fit":["both","width","height"]}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"icon-image":{type:"resolvedImage",tokens:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-rotate":{type:"number",default:0,period:360,units:"degrees",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-padding":{type:"padding",default:[2],units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-keep-upright":{type:"boolean",default:!1,requires:["icon-image",{"icon-rotation-alignment":"map"},{"symbol-placement":["line","line-center"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"icon-offset":{type:"array",value:"number",length:2,default:[0,0],requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-anchor":{type:"enum",values:{center:{},left:{},right:{},top:{},bottom:{},"top-left":{},"top-right":{},"bottom-left":{},"bottom-right":{}},default:"center",requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"icon-pitch-alignment":{type:"enum",values:{map:{},viewport:{},auto:{}},default:"auto",requires:["icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-pitch-alignment":{type:"enum",values:{map:{},viewport:{},auto:{}},default:"auto",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-rotation-alignment":{type:"enum",values:{map:{},viewport:{},"viewport-glyph":{},auto:{}},default:"auto",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-field":{type:"formatted",default:"",tokens:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-font":{type:"array",value:"string",default:["Open Sans Regular","Arial Unicode MS Regular"],requires:["text-field"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-size":{type:"number",default:16,minimum:0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-max-width":{type:"number",default:10,minimum:0,units:"ems",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-line-height":{type:"number",default:1.2,units:"ems",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-letter-spacing":{type:"number",default:0,units:"ems",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-justify":{type:"enum",values:{auto:{},left:{},center:{},right:{}},default:"center",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-radial-offset":{type:"number",units:"ems",default:0,requires:["text-field"],"property-type":"data-driven",expression:{interpolated:!0,parameters:["zoom","feature"]}},"text-variable-anchor":{type:"array",value:"enum",values:{center:{},left:{},right:{},top:{},bottom:{},"top-left":{},"top-right":{},"bottom-left":{},"bottom-right":{}},requires:["text-field",{"symbol-placement":["point"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-anchor":{type:"enum",values:{center:{},left:{},right:{},top:{},bottom:{},"top-left":{},"top-right":{},"bottom-left":{},"bottom-right":{}},default:"center",requires:["text-field",{"!":"text-variable-anchor"}],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-max-angle":{type:"number",default:45,units:"degrees",requires:["text-field",{"symbol-placement":["line","line-center"]}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-writing-mode":{type:"array",value:"enum",values:{horizontal:{},vertical:{}},requires:["text-field",{"symbol-placement":["point"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-rotate":{type:"number",default:0,period:360,units:"degrees",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-padding":{type:"number",default:2,minimum:0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-keep-upright":{type:"boolean",default:!0,requires:["text-field",{"text-rotation-alignment":"map"},{"symbol-placement":["line","line-center"]}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-transform":{type:"enum",values:{none:{},uppercase:{},lowercase:{}},default:"none",requires:["text-field"],expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-offset":{type:"array",value:"number",units:"ems",length:2,default:[0,0],requires:["text-field",{"!":"text-radial-offset"}],expression:{interpolated:!0,parameters:["zoom","feature"]},"property-type":"data-driven"},"text-allow-overlap":{type:"boolean",default:!1,requires:["text-field",{"!":"text-overlap"}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-overlap":{type:"enum",values:{never:{},always:{},cooperative:{}},requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-ignore-placement":{type:"boolean",default:!1,requires:["text-field"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-optional":{type:"boolean",default:!1,requires:["text-field","icon-image"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_raster:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},layout_hillshade:{visibility:{type:"enum",values:{visible:{},none:{}},default:"visible","property-type":"constant"}},filter:{type:"array",value:"*"},filter_operator:{type:"enum",values:{"==":{},"!=":{},">":{},">=":{},"<":{},"<=":{},in:{},"!in":{},all:{},any:{},none:{},has:{},"!has":{},within:{}}},geometry_type:{type:"enum",values:{Point:{},LineString:{},Polygon:{}}},function:{expression:{type:"expression"},stops:{type:"array",value:"function_stop"},base:{type:"number",default:1,minimum:0},property:{type:"string",default:"$zoom"},type:{type:"enum",values:{identity:{},exponential:{},interval:{},categorical:{}},default:"exponential"},colorSpace:{type:"enum",values:{rgb:{},lab:{},hcl:{}},default:"rgb"},default:{type:"*",required:!1}},function_stop:{type:"array",minimum:0,maximum:24,value:["number","color"],length:2},expression:{type:"array",value:"*",minimum:1},light:{anchor:{type:"enum",default:"viewport",values:{map:{},viewport:{}},"property-type":"data-constant",transition:!1,expression:{interpolated:!1,parameters:["zoom"]}},position:{type:"array",default:[1.15,210,30],length:3,value:"number","property-type":"data-constant",transition:!0,expression:{interpolated:!0,parameters:["zoom"]}},color:{type:"color","property-type":"data-constant",default:"#ffffff",expression:{interpolated:!0,parameters:["zoom"]},transition:!0},intensity:{type:"number","property-type":"data-constant",default:.5,minimum:0,maximum:1,expression:{interpolated:!0,parameters:["zoom"]},transition:!0}},terrain:{source:{type:"string",required:!0},exaggeration:{type:"number",minimum:0,default:1},elevationOffset:{type:"number",default:450}},paint:["paint_fill","paint_line","paint_circle","paint_heatmap","paint_fill-extrusion","paint_symbol","paint_raster","paint_hillshade","paint_background"],paint_fill:{"fill-antialias":{type:"boolean",default:!0,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"fill-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"fill-pattern"}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-outline-color":{type:"color",transition:!0,requires:[{"!":"fill-pattern"},{"fill-antialias":!0}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"fill-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["fill-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"fill-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"}},"paint_fill-extrusion":{"fill-extrusion-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"fill-extrusion-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"fill-extrusion-pattern"}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-extrusion-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"fill-extrusion-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["fill-extrusion-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"fill-extrusion-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"},"fill-extrusion-height":{type:"number",default:0,minimum:0,units:"meters",transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-extrusion-base":{type:"number",default:0,minimum:0,units:"meters",transition:!0,requires:["fill-extrusion-height"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"fill-extrusion-vertical-gradient":{type:"boolean",default:!0,transition:!1,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"}},paint_line:{"line-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"line-pattern"}],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"line-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["line-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"line-width":{type:"number",default:1,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-gap-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-offset":{type:"number",default:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-blur":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"line-dasharray":{type:"array",value:"number",minimum:0,transition:!0,units:"line widths",requires:[{"!":"line-pattern"}],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"cross-faded"},"line-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom","feature"]},"property-type":"cross-faded-data-driven"},"line-gradient":{type:"color",transition:!1,requires:[{"!":"line-dasharray"},{"!":"line-pattern"},{source:"geojson",has:{lineMetrics:!0}}],expression:{interpolated:!0,parameters:["line-progress"]},"property-type":"color-ramp"}},paint_circle:{"circle-radius":{type:"number",default:5,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-blur":{type:"number",default:0,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"circle-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["circle-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"circle-pitch-scale":{type:"enum",values:{map:{},viewport:{}},default:"map",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"circle-pitch-alignment":{type:"enum",values:{map:{},viewport:{}},default:"viewport",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"circle-stroke-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-stroke-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"circle-stroke-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"}},paint_heatmap:{"heatmap-radius":{type:"number",default:30,minimum:1,transition:!0,units:"pixels",expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"heatmap-weight":{type:"number",default:1,minimum:0,transition:!1,expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"heatmap-intensity":{type:"number",default:1,minimum:0,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"heatmap-color":{type:"color",default:["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",.1,"royalblue",.3,"cyan",.5,"lime",.7,"yellow",1,"red"],transition:!1,expression:{interpolated:!0,parameters:["heatmap-density"]},"property-type":"color-ramp"},"heatmap-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},paint_symbol:{"icon-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-color":{type:"color",default:"#000000",transition:!0,requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-halo-color":{type:"color",default:"rgba(0, 0, 0, 0)",transition:!0,requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-halo-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-halo-blur":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"icon-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",requires:["icon-image"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"icon-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["icon-image","icon-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"text-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-color":{type:"color",default:"#000000",transition:!0,overridable:!0,requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-halo-color":{type:"color",default:"rgba(0, 0, 0, 0)",transition:!0,requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-halo-width":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-halo-blur":{type:"number",default:0,minimum:0,transition:!0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom","feature","feature-state"]},"property-type":"data-driven"},"text-translate":{type:"array",value:"number",length:2,default:[0,0],transition:!0,units:"pixels",requires:["text-field"],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"text-translate-anchor":{type:"enum",values:{map:{},viewport:{}},default:"map",requires:["text-field","text-translate"],expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"}},paint_raster:{"raster-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-hue-rotate":{type:"number",default:0,period:360,transition:!0,units:"degrees",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-brightness-min":{type:"number",default:0,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-brightness-max":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-saturation":{type:"number",default:0,minimum:-1,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-contrast":{type:"number",default:0,minimum:-1,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"raster-resampling":{type:"enum",values:{linear:{},nearest:{}},default:"linear",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"raster-fade-duration":{type:"number",default:300,minimum:0,transition:!1,units:"milliseconds",expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},paint_hillshade:{"hillshade-illumination-direction":{type:"number",default:335,minimum:0,maximum:359,transition:!1,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-illumination-anchor":{type:"enum",values:{map:{},viewport:{}},default:"viewport",expression:{interpolated:!1,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-exaggeration":{type:"number",default:.5,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-shadow-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-highlight-color":{type:"color",default:"#FFFFFF",transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"hillshade-accent-color":{type:"color",default:"#000000",transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},paint_background:{"background-color":{type:"color",default:"#000000",transition:!0,requires:[{"!":"background-pattern"}],expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"},"background-pattern":{type:"resolvedImage",transition:!0,expression:{interpolated:!1,parameters:["zoom"]},"property-type":"cross-faded"},"background-opacity":{type:"number",default:1,minimum:0,maximum:1,transition:!0,expression:{interpolated:!0,parameters:["zoom"]},"property-type":"data-constant"}},transition:{duration:{type:"number",default:300,minimum:0,units:"milliseconds"},delay:{type:"number",default:0,minimum:0,units:"milliseconds"}},"property-type":{"data-driven":{type:"property-type"},"cross-faded":{type:"property-type"},"cross-faded-data-driven":{type:"property-type"},"color-ramp":{type:"property-type"},"data-constant":{type:"property-type"},constant:{type:"property-type"}},promoteId:{"*":{type:"string"}}};class nt{constructor(t,e,i,r){this.message=(t?`${t}: `:"")+i,r&&(this.identifier=r),null!=e&&e.__line__&&(this.line=e.__line__)}}function st(t){const e=t.value;return e?[new nt(t.key,e,"constants have been deprecated as of v8")]:[]}function ot(t,...e){for(const i of e)for(const e in i)t[e]=i[e];return t}function at(t){return t instanceof Number||t instanceof String||t instanceof Boolean?t.valueOf():t}function lt(t){if(Array.isArray(t))return t.map(lt);if(t instanceof Object&&!(t instanceof Number||t instanceof String||t instanceof Boolean)){const e={};for(const i in t)e[i]=lt(t[i]);return e}return at(t)}class ct extends Error{constructor(t,e){super(e),this.message=e,this.key=t}}class ht{constructor(t,e=[]){this.parent=t,this.bindings={};for(const[t,i]of e)this.bindings[t]=i}concat(t){return new ht(this,t)}get(t){if(this.bindings[t])return this.bindings[t];if(this.parent)return this.parent.get(t);throw new Error(`${t} not found in scope.`)}has(t){return!!this.bindings[t]||!!this.parent&&this.parent.has(t)}}const ut={kind:"null"},pt={kind:"number"},dt={kind:"string"},mt={kind:"boolean"},ft={kind:"color"},gt={kind:"object"},_t={kind:"value"},yt={kind:"collator"},xt={kind:"formatted"},vt={kind:"padding"},bt={kind:"resolvedImage"};function wt(t,e){return{kind:"array",itemType:t,N:e}}function Tt(t){if("array"===t.kind){const e=Tt(t.itemType);return"number"==typeof t.N?`array<${e}, ${t.N}>`:"value"===t.itemType.kind?"array":`array<${e}>`}return t.kind}const Et=[ut,pt,dt,mt,ft,xt,gt,wt(_t),vt,bt];function St(t,e){if("error"===e.kind)return null;if("array"===t.kind){if("array"===e.kind&&(0===e.N&&"value"===e.itemType.kind||!St(t.itemType,e.itemType))&&("number"!=typeof t.N||t.N===e.N))return null}else{if(t.kind===e.kind)return null;if("value"===t.kind)for(const t of Et)if(!St(t,e))return null}return`Expected ${Tt(t)} but found ${Tt(e)} instead.`}function It(t,e){return e.some((e=>e.kind===t.kind))}function At(t,e){return e.some((e=>"null"===e?null===t:"array"===e?Array.isArray(t):"object"===e?t&&!Array.isArray(t)&&"object"==typeof t:e===typeof t))}var zt,Ct={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],rebeccapurple:[102,51,153,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function Mt(t){return(t=Math.round(t))<0?0:t>255?255:t}function kt(t){return Mt("%"===t[t.length-1]?parseFloat(t)/100*255:parseInt(t))}function Pt(t){return(e="%"===t[t.length-1]?parseFloat(t)/100:parseFloat(t))<0?0:e>1?1:e;var e}function Dt(t,e,i){return i<0?i+=1:i>1&&(i-=1),6*i<1?t+(e-t)*i*6:2*i<1?e:3*i<2?t+(e-t)*(2/3-i)*6:t}try{zt={}.parseCSSColor=function(t){var e,i=t.replace(/ /g,"").toLowerCase();if(i in Ct)return Ct[i].slice();if("#"===i[0])return 4===i.length?(e=parseInt(i.substr(1),16))>=0&&e<=4095?[(3840&e)>>4|(3840&e)>>8,240&e|(240&e)>>4,15&e|(15&e)<<4,1]:null:7===i.length&&(e=parseInt(i.substr(1),16))>=0&&e<=16777215?[(16711680&e)>>16,(65280&e)>>8,255&e,1]:null;var r=i.indexOf("("),n=i.indexOf(")");if(-1!==r&&n+1===i.length){var s=i.substr(0,r),o=i.substr(r+1,n-(r+1)).split(","),a=1;switch(s){case"rgba":if(4!==o.length)return null;a=Pt(o.pop());case"rgb":return 3!==o.length?null:[kt(o[0]),kt(o[1]),kt(o[2]),a];case"hsla":if(4!==o.length)return null;a=Pt(o.pop());case"hsl":if(3!==o.length)return null;var l=(parseFloat(o[0])%360+360)%360/360,c=Pt(o[1]),h=Pt(o[2]),u=h<=.5?h*(c+1):h+c-h*c,p=2*h-u;return[Mt(255*Dt(p,u,l+1/3)),Mt(255*Dt(p,u,l)),Mt(255*Dt(p,u,l-1/3)),a];default:return null}}return null}}catch(t){}class Lt{constructor(t,e,i,r=1){this.r=t,this.g=e,this.b=i,this.a=r}static parse(t){if(!t)return;if(t instanceof Lt)return t;if("string"!=typeof t)return;const e=zt(t);return e?new Lt(e[0]/255*e[3],e[1]/255*e[3],e[2]/255*e[3],e[3]):void 0}toString(){const[t,e,i,r]=this.toArray();return`rgba(${Math.round(t)},${Math.round(e)},${Math.round(i)},${r})`}toArray(){const{r:t,g:e,b:i,a:r}=this;return 0===r?[0,0,0,0]:[255*t/r,255*e/r,255*i/r,r]}}Lt.black=new Lt(0,0,0,1),Lt.white=new Lt(1,1,1,1),Lt.transparent=new Lt(0,0,0,0),Lt.red=new Lt(1,0,0,1);class Bt{constructor(t,e,i){this.sensitivity=t?e?"variant":"case":e?"accent":"base",this.locale=i,this.collator=new Intl.Collator(this.locale?this.locale:[],{sensitivity:this.sensitivity,usage:"search"})}compare(t,e){return this.collator.compare(t,e)}resolvedLocale(){return new Intl.Collator(this.locale?this.locale:[]).resolvedOptions().locale}}class Rt{constructor(t,e,i,r,n){this.text=t,this.image=e,this.scale=i,this.fontStack=r,this.textColor=n}}class Ft{constructor(t){this.sections=t}static fromString(t){return new Ft([new Rt(t,null,null,null,null)])}isEmpty(){return 0===this.sections.length||!this.sections.some((t=>0!==t.text.length||t.image&&0!==t.image.name.length))}static factory(t){return t instanceof Ft?t:Ft.fromString(t)}toString(){return 0===this.sections.length?"":this.sections.map((t=>t.text)).join("")}}class Ot{constructor(t){this.values=t.slice()}static parse(t){if(t instanceof Ot)return t;if("number"==typeof t)return new Ot([t,t,t,t]);if(Array.isArray(t)&&!(t.length<1||t.length>4)){for(const e of t)if("number"!=typeof e)return;switch(t.length){case 1:t=[t[0],t[0],t[0],t[0]];break;case 2:t=[t[0],t[1],t[0],t[1]];break;case 3:t=[t[0],t[1],t[2],t[1]]}return new Ot(t)}}toString(){return JSON.stringify(this.values)}}class Ut{constructor(t){this.name=t.name,this.available=t.available}toString(){return this.name}static fromString(t){return t?new Ut({name:t,available:!1}):null}}function Vt(t,e,i,r){return"number"==typeof t&&t>=0&&t<=255&&"number"==typeof e&&e>=0&&e<=255&&"number"==typeof i&&i>=0&&i<=255?void 0===r||"number"==typeof r&&r>=0&&r<=1?null:`Invalid rgba value [${[t,e,i,r].join(", ")}]: 'a' must be between 0 and 1.`:`Invalid rgba value [${("number"==typeof r?[t,e,i,r]:[t,e,i]).join(", ")}]: 'r', 'g', and 'b' must be between 0 and 255.`}function Nt(t){if(null===t)return!0;if("string"==typeof t)return!0;if("boolean"==typeof t)return!0;if("number"==typeof t)return!0;if(t instanceof Lt)return!0;if(t instanceof Bt)return!0;if(t instanceof Ft)return!0;if(t instanceof Ot)return!0;if(t instanceof Ut)return!0;if(Array.isArray(t)){for(const e of t)if(!Nt(e))return!1;return!0}if("object"==typeof t){for(const e in t)if(!Nt(t[e]))return!1;return!0}return!1}function Gt(t){if(null===t)return ut;if("string"==typeof t)return dt;if("boolean"==typeof t)return mt;if("number"==typeof t)return pt;if(t instanceof Lt)return ft;if(t instanceof Bt)return yt;if(t instanceof Ft)return xt;if(t instanceof Ot)return vt;if(t instanceof Ut)return bt;if(Array.isArray(t)){const e=t.length;let i;for(const e of t){const t=Gt(e);if(i){if(i===t)continue;i=_t;break}i=t}return wt(i||_t,e)}return gt}function $t(t){const e=typeof t;return null===t?"":"string"===e||"number"===e||"boolean"===e?String(t):t instanceof Lt||t instanceof Ft||t instanceof Ot||t instanceof Ut?t.toString():JSON.stringify(t)}class qt{constructor(t,e){this.type=t,this.value=e}static parse(t,e){if(2!==t.length)return e.error(`'literal' expression requires exactly one argument, but found ${t.length-1} instead.`);if(!Nt(t[1]))return e.error("invalid value");const i=t[1];let r=Gt(i);const n=e.expectedType;return"array"!==r.kind||0!==r.N||!n||"array"!==n.kind||"number"==typeof n.N&&0!==n.N||(r=n),new qt(r,i)}evaluate(){return this.value}eachChild(){}outputDefined(){return!0}}class jt{constructor(t){this.name="ExpressionEvaluationError",this.message=t}toJSON(){return this.message}}const Zt={string:dt,number:pt,boolean:mt,object:gt};class Xt{constructor(t,e){this.type=t,this.args=e}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");let i,r=1;const n=t[0];if("array"===n){let n,s;if(t.length>2){const i=t[1];if("string"!=typeof i||!(i in Zt)||"object"===i)return e.error('The item type argument of "array" must be one of string, number, boolean',1);n=Zt[i],r++}else n=_t;if(t.length>3){if(null!==t[2]&&("number"!=typeof t[2]||t[2]<0||t[2]!==Math.floor(t[2])))return e.error('The length argument to "array" must be a positive integer literal',2);s=t[2],r++}i=wt(n,s)}else i=Zt[n];const s=[];for(;rt.outputDefined()))}}const Wt={"to-boolean":mt,"to-color":ft,"to-number":pt,"to-string":dt};class Ht{constructor(t,e){this.type=t,this.args=e}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");const i=t[0];if(("to-boolean"===i||"to-string"===i)&&2!==t.length)return e.error("Expected one argument.");const r=Wt[i],n=[];for(let i=1;i4?`Invalid rbga value ${JSON.stringify(e)}: expected an array containing either three or four numeric values.`:Vt(e[0],e[1],e[2],e[3]),!i))return new Lt(e[0]/255,e[1]/255,e[2]/255,e[3])}throw new jt(i||`Could not parse color from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}if("padding"===this.type.kind){let e;for(const i of this.args){e=i.evaluate(t);const r=Ot.parse(e);if(r)return r}throw new jt(`Could not parse padding from value '${"string"==typeof e?e:JSON.stringify(e)}'`)}if("number"===this.type.kind){let e=null;for(const i of this.args){if(e=i.evaluate(t),null===e)return 0;const r=Number(e);if(!isNaN(r))return r}throw new jt(`Could not convert ${JSON.stringify(e)} to number.`)}return"formatted"===this.type.kind?Ft.fromString($t(this.args[0].evaluate(t))):"resolvedImage"===this.type.kind?Ut.fromString($t(this.args[0].evaluate(t))):$t(this.args[0].evaluate(t))}eachChild(t){this.args.forEach(t)}outputDefined(){return this.args.every((t=>t.outputDefined()))}}const Kt=["Unknown","Point","LineString","Polygon"];class Jt{constructor(){this.globals=null,this.feature=null,this.featureState=null,this.formattedSection=null,this._parseColorCache={},this.availableImages=null,this.canonical=null}id(){return this.feature&&"id"in this.feature?this.feature.id:null}geometryType(){return this.feature?"number"==typeof this.feature.type?Kt[this.feature.type]:this.feature.type:null}geometry(){return this.feature&&"geometry"in this.feature?this.feature.geometry:null}canonicalID(){return this.canonical}properties(){return this.feature&&this.feature.properties||{}}parseColor(t){let e=this._parseColorCache[t];return e||(e=this._parseColorCache[t]=Lt.parse(t)),e}}class Yt{constructor(t,e,i,r){this.name=t,this.type=e,this._evaluate=i,this.args=r}evaluate(t){return this._evaluate(t,this.args)}eachChild(t){this.args.forEach(t)}outputDefined(){return!1}static parse(t,e){const i=t[0],r=Yt.definitions[i];if(!r)return e.error(`Unknown expression "${i}". If you wanted a literal array, use ["literal", [...]].`,0);const n=Array.isArray(r)?r[0]:r.type,s=Array.isArray(r)?[[r[1],r[2]]]:r.overloads,o=s.filter((([e])=>!Array.isArray(e)||e.length===t.length-1));let a=null;for(const[r,s]of o){a=new be(e.registry,e.path,null,e.scope);const o=[];let l=!1;for(let e=1;e{return e=t,Array.isArray(e)?`(${e.map(Tt).join(", ")})`:`(${Tt(e.type)}...)`;var e})).join(" | "),r=[];for(let i=1;i=e[2]||t[1]<=e[1]||t[3]>=e[3])}function re(t,e){const i=(180+t[0])/360,r=(180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t[1]*Math.PI/360)))/360,n=Math.pow(2,e.z);return[Math.round(i*n*te),Math.round(r*n*te)]}function ne(t,e,i){const r=t[0]-e[0],n=t[1]-e[1],s=t[0]-i[0],o=t[1]-i[1];return r*o-s*n==0&&r*s<=0&&n*o<=0}function se(t,e){let i=!1;for(let o=0,a=e.length;o(r=t)[1]!=(s=a[e+1])[1]>r[1]&&r[0]<(s[0]-n[0])*(r[1]-n[1])/(s[1]-n[1])+n[0]&&(i=!i)}}var r,n,s;return i}function oe(t,e){for(let i=0;i0&&a<0||o<0&&a>0}function le(t,e,i){for(const c of i)for(let i=0;ii[2]){const e=.5*r;let n=t[0]-i[0]>e?-r:i[0]-t[0]>e?r:0;0===n&&(n=t[0]-i[2]>e?-r:i[2]-t[0]>e?r:0),t[0]+=n}ee(e,t)}function me(t,e,i,r){const n=Math.pow(2,r.z)*te,s=[r.x*te,r.y*te],o=[];for(const r of t)for(const t of r){const r=[t.x+s[0],t.y+s[1]];de(r,e,i,n),o.push(r)}return o}function fe(t,e,i,r){const n=Math.pow(2,r.z)*te,s=[r.x*te,r.y*te],o=[];for(const i of t){const t=[];for(const r of i){const i=[r.x+s[0],r.y+s[1]];ee(e,i),t.push(i)}o.push(t)}if(e[2]-e[0]<=n/2){(a=e)[0]=a[1]=1/0,a[2]=a[3]=-1/0;for(const t of o)for(const r of t)de(r,e,i,n)}var a;return o}class ge{constructor(t,e){this.type=mt,this.geojson=t,this.geometries=e}static parse(t,e){if(2!==t.length)return e.error(`'within' expression requires exactly one argument, but found ${t.length-1} instead.`);if(Nt(t[1])){const e=t[1];if("FeatureCollection"===e.type)for(let t=0;t{e&&!_e(t)&&(e=!1)})),e}function ye(t){if(t instanceof Yt&&"feature-state"===t.name)return!1;let e=!0;return t.eachChild((t=>{e&&!ye(t)&&(e=!1)})),e}function xe(t,e){if(t instanceof Yt&&e.indexOf(t.name)>=0)return!1;let i=!0;return t.eachChild((t=>{i&&!xe(t,e)&&(i=!1)})),i}class ve{constructor(t,e){this.type=e.type,this.name=t,this.boundExpression=e}static parse(t,e){if(2!==t.length||"string"!=typeof t[1])return e.error("'var' expression requires exactly one string literal argument.");const i=t[1];return e.scope.has(i)?new ve(i,e.scope.get(i)):e.error(`Unknown variable "${i}". Make sure "${i}" has been bound in an enclosing "let" expression before using it.`,1)}evaluate(t){return this.boundExpression.evaluate(t)}eachChild(){}outputDefined(){return!1}}class be{constructor(t,e=[],i,r=new ht,n=[]){this.registry=t,this.path=e,this.key=e.map((t=>`[${t}]`)).join(""),this.scope=r,this.errors=n,this.expectedType=i}parse(t,e,i,r,n={}){return e?this.concat(e,i,r)._parse(t,n):this._parse(t,n)}_parse(t,e){function i(t,e,i){return"assert"===i?new Xt(e,[t]):"coerce"===i?new Ht(e,[t]):t}if(null!==t&&"string"!=typeof t&&"boolean"!=typeof t&&"number"!=typeof t||(t=["literal",t]),Array.isArray(t)){if(0===t.length)return this.error('Expected an array with at least one element. If you wanted a literal array, use ["literal", []].');const r=t[0];if("string"!=typeof r)return this.error(`Expression name must be a string, but found ${typeof r} instead. If you wanted a literal array, use ["literal", [...]].`,0),null;const n=this.registry[r];if(n){let r=n.parse(t,this);if(!r)return null;if(this.expectedType){const t=this.expectedType,n=r.type;if("string"!==t.kind&&"number"!==t.kind&&"boolean"!==t.kind&&"object"!==t.kind&&"array"!==t.kind||"value"!==n.kind)if("color"!==t.kind&&"formatted"!==t.kind&&"resolvedImage"!==t.kind||"value"!==n.kind&&"string"!==n.kind)if("padding"!==t.kind||"value"!==n.kind&&"number"!==n.kind&&"array"!==n.kind){if(this.checkSubtype(t,n))return null}else r=i(r,t,e.typeAnnotation||"coerce");else r=i(r,t,e.typeAnnotation||"coerce");else r=i(r,t,e.typeAnnotation||"assert")}if(!(r instanceof qt)&&"resolvedImage"!==r.type.kind&&we(r)){const e=new Jt;try{r=new qt(r.type,r.evaluate(e))}catch(t){return this.error(t.message),null}}return r}return this.error(`Unknown expression "${r}". If you wanted a literal array, use ["literal", [...]].`,0)}return this.error(void 0===t?"'undefined' value invalid. Use null instead.":"object"==typeof t?'Bare objects invalid. Use ["literal", {...}] instead.':`Expected an array, but found ${typeof t} instead.`)}concat(t,e,i){const r="number"==typeof t?this.path.concat(t):this.path,n=i?this.scope.concat(i):this.scope;return new be(this.registry,r,e||null,n,this.errors)}error(t,...e){const i=`${this.key}${e.map((t=>`[${t}]`)).join("")}`;this.errors.push(new ct(i,t))}checkSubtype(t,e){const i=St(t,e);return i&&this.error(i),i}}function we(t){if(t instanceof ve)return we(t.boundExpression);if(t instanceof Yt&&"error"===t.name)return!1;if(t instanceof Qt)return!1;if(t instanceof ge)return!1;const e=t instanceof Ht||t instanceof Xt;let i=!0;return t.eachChild((t=>{i=e?i&&we(t):i&&t instanceof qt})),!!i&&_e(t)&&xe(t,["zoom","heatmap-density","line-progress","accumulated","is-supported-script"])}function Te(t,e){const i=t.length-1;let r,n,s=0,o=i,a=0;for(;s<=o;)if(a=Math.floor((s+o)/2),r=t[a],n=t[a+1],r<=e){if(a===i||ee))throw new jt("Input is not a number.");o=a-1}return 0}class Ee{constructor(t,e,i){this.type=t,this.input=e,this.labels=[],this.outputs=[];for(const[t,e]of i)this.labels.push(t),this.outputs.push(e)}static parse(t,e){if(t.length-1<4)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if((t.length-1)%2!=0)return e.error("Expected an even number of arguments.");const i=e.parse(t[1],1,pt);if(!i)return null;const r=[];let n=null;e.expectedType&&"value"!==e.expectedType.kind&&(n=e.expectedType);for(let i=1;i=s)return e.error('Input/output pairs for "step" expressions must be arranged with input values in strictly ascending order.',a);const c=e.parse(o,l,n);if(!c)return null;n=n||c.type,r.push([s,c])}return new Ee(n,i,r)}evaluate(t){const e=this.labels,i=this.outputs;if(1===e.length)return i[0].evaluate(t);const r=this.input.evaluate(t);if(r<=e[0])return i[0].evaluate(t);const n=e.length;return r>=e[n-1]?i[n-1].evaluate(t):i[Te(e,r)].evaluate(t)}eachChild(t){t(this.input);for(const e of this.outputs)t(e)}outputDefined(){return this.outputs.every((t=>t.outputDefined()))}}function Se(t,e,i){return t*(1-i)+e*i}var Ie=Object.freeze({__proto__:null,number:Se,color:function(t,e,i){return new Lt(Se(t.r,e.r,i),Se(t.g,e.g,i),Se(t.b,e.b,i),Se(t.a,e.a,i))},array:function(t,e,i){return t.map(((t,r)=>Se(t,e[r],i)))},padding:function(t,e,i){const r=t.values,n=e.values;return new Ot([Se(r[0],n[0],i),Se(r[1],n[1],i),Se(r[2],n[2],i),Se(r[3],n[3],i)])}});const Ae=.95047,ze=1.08883,Ce=4/29,Me=6/29,ke=3*Me*Me,Pe=Math.PI/180,De=180/Math.PI;function Le(t){return t>.008856451679035631?Math.pow(t,1/3):t/ke+Ce}function Be(t){return t>Me?t*t*t:ke*(t-Ce)}function Re(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Fe(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Oe(t){const e=Fe(t.r),i=Fe(t.g),r=Fe(t.b),n=Le((.4124564*e+.3575761*i+.1804375*r)/Ae),s=Le((.2126729*e+.7151522*i+.072175*r)/1);return{l:116*s-16,a:500*(n-s),b:200*(s-Le((.0193339*e+.119192*i+.9503041*r)/ze)),alpha:t.a}}function Ue(t){let e=(t.l+16)/116,i=isNaN(t.a)?e:e+t.a/500,r=isNaN(t.b)?e:e-t.b/200;return e=1*Be(e),i=Ae*Be(i),r=ze*Be(r),new Lt(Re(3.2404542*i-1.5371385*e-.4985314*r),Re(-.969266*i+1.8760108*e+.041556*r),Re(.0556434*i-.2040259*e+1.0572252*r),t.alpha)}function Ve(t,e,i){const r=e-t;return t+i*(r>180||r<-180?r-360*Math.round(r/360):r)}const Ne={forward:Oe,reverse:Ue,interpolate:function(t,e,i){return{l:Se(t.l,e.l,i),a:Se(t.a,e.a,i),b:Se(t.b,e.b,i),alpha:Se(t.alpha,e.alpha,i)}}},Ge={forward:function(t){const{l:e,a:i,b:r}=Oe(t),n=Math.atan2(r,i)*De;return{h:n<0?n+360:n,c:Math.sqrt(i*i+r*r),l:e,alpha:t.a}},reverse:function(t){const e=t.h*Pe,i=t.c;return Ue({l:t.l,a:Math.cos(e)*i,b:Math.sin(e)*i,alpha:t.alpha})},interpolate:function(t,e,i){return{h:Ve(t.h,e.h,i),c:Se(t.c,e.c,i),l:Se(t.l,e.l,i),alpha:Se(t.alpha,e.alpha,i)}}};var $e=Object.freeze({__proto__:null,lab:Ne,hcl:Ge});class qe{constructor(t,e,i,r,n){this.type=t,this.operator=e,this.interpolation=i,this.input=r,this.labels=[],this.outputs=[];for(const[t,e]of n)this.labels.push(t),this.outputs.push(e)}static interpolationFactor(t,i,r,n){let s=0;if("exponential"===t.name)s=je(i,t.base,r,n);else if("linear"===t.name)s=je(i,1,r,n);else if("cubic-bezier"===t.name){const o=t.controlPoints;s=new e(o[0],o[1],o[2],o[3]).solve(je(i,1,r,n))}return s}static parse(t,e){let[i,r,n,...s]=t;if(!Array.isArray(r)||0===r.length)return e.error("Expected an interpolation type expression.",1);if("linear"===r[0])r={name:"linear"};else if("exponential"===r[0]){const t=r[1];if("number"!=typeof t)return e.error("Exponential interpolation requires a numeric base.",1,1);r={name:"exponential",base:t}}else{if("cubic-bezier"!==r[0])return e.error(`Unknown interpolation type ${String(r[0])}`,1,0);{const t=r.slice(1);if(4!==t.length||t.some((t=>"number"!=typeof t||t<0||t>1)))return e.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.",1);r={name:"cubic-bezier",controlPoints:t}}}if(t.length-1<4)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if((t.length-1)%2!=0)return e.error("Expected an even number of arguments.");if(n=e.parse(n,2,pt),!n)return null;const o=[];let a=null;"interpolate-hcl"===i||"interpolate-lab"===i?a=ft:e.expectedType&&"value"!==e.expectedType.kind&&(a=e.expectedType);for(let t=0;t=i)return e.error('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.',n);const c=e.parse(r,l,a);if(!c)return null;a=a||c.type,o.push([i,c])}return"number"===a.kind||"color"===a.kind||"padding"===a.kind||"array"===a.kind&&"number"===a.itemType.kind&&"number"==typeof a.N?new qe(a,i,r,n,o):e.error(`Type ${Tt(a)} is not interpolatable.`)}evaluate(t){const e=this.labels,i=this.outputs;if(1===e.length)return i[0].evaluate(t);const r=this.input.evaluate(t);if(r<=e[0])return i[0].evaluate(t);const n=e.length;if(r>=e[n-1])return i[n-1].evaluate(t);const s=Te(e,r),o=qe.interpolationFactor(this.interpolation,r,e[s],e[s+1]),a=i[s].evaluate(t),l=i[s+1].evaluate(t);return"interpolate"===this.operator?Ie[this.type.kind.toLowerCase()](a,l,o):"interpolate-hcl"===this.operator?Ge.reverse(Ge.interpolate(Ge.forward(a),Ge.forward(l),o)):Ne.reverse(Ne.interpolate(Ne.forward(a),Ne.forward(l),o))}eachChild(t){t(this.input);for(const e of this.outputs)t(e)}outputDefined(){return this.outputs.every((t=>t.outputDefined()))}}function je(t,e,i,r){const n=r-i,s=t-i;return 0===n?0:1===e?s/n:(Math.pow(e,s)-1)/(Math.pow(e,n)-1)}class Ze{constructor(t,e){this.type=t,this.args=e}static parse(t,e){if(t.length<2)return e.error("Expectected at least one argument.");let i=null;const r=e.expectedType;r&&"value"!==r.kind&&(i=r);const n=[];for(const r of t.slice(1)){const t=e.parse(r,1+n.length,i,void 0,{typeAnnotation:"omit"});if(!t)return null;i=i||t.type,n.push(t)}const s=r&&n.some((t=>St(r,t.type)));return new Ze(s?_t:i,n)}evaluate(t){let e,i=null,r=0;for(const n of this.args)if(r++,i=n.evaluate(t),i&&i instanceof Ut&&!i.available&&(e||(e=i.name),i=null,r===this.args.length&&(i=e)),null!==i)break;return i}eachChild(t){this.args.forEach(t)}outputDefined(){return this.args.every((t=>t.outputDefined()))}}class Xe{constructor(t,e){this.type=e.type,this.bindings=[].concat(t),this.result=e}evaluate(t){return this.result.evaluate(t)}eachChild(t){for(const e of this.bindings)t(e[1]);t(this.result)}static parse(t,e){if(t.length<4)return e.error(`Expected at least 3 arguments, but found ${t.length-1} instead.`);const i=[];for(let r=1;r=i.length)throw new jt(`Array index out of bounds: ${e} > ${i.length-1}.`);if(e!==Math.floor(e))throw new jt(`Array index must be an integer, but found ${e} instead.`);return i[e]}eachChild(t){t(this.index),t(this.input)}outputDefined(){return!1}}class He{constructor(t,e){this.type=mt,this.needle=t,this.haystack=e}static parse(t,e){if(3!==t.length)return e.error(`Expected 2 arguments, but found ${t.length-1} instead.`);const i=e.parse(t[1],1,_t),r=e.parse(t[2],2,_t);return i&&r?It(i.type,[mt,dt,pt,ut,_t])?new He(i,r):e.error(`Expected first argument to be of type boolean, string, number or null, but found ${Tt(i.type)} instead`):null}evaluate(t){const e=this.needle.evaluate(t),i=this.haystack.evaluate(t);if(!i)return!1;if(!At(e,["boolean","string","number","null"]))throw new jt(`Expected first argument to be of type boolean, string, number or null, but found ${Tt(Gt(e))} instead.`);if(!At(i,["string","array"]))throw new jt(`Expected second argument to be of type array or string, but found ${Tt(Gt(i))} instead.`);return i.indexOf(e)>=0}eachChild(t){t(this.needle),t(this.haystack)}outputDefined(){return!0}}class Ke{constructor(t,e,i){this.type=pt,this.needle=t,this.haystack=e,this.fromIndex=i}static parse(t,e){if(t.length<=2||t.length>=5)return e.error(`Expected 3 or 4 arguments, but found ${t.length-1} instead.`);const i=e.parse(t[1],1,_t),r=e.parse(t[2],2,_t);if(!i||!r)return null;if(!It(i.type,[mt,dt,pt,ut,_t]))return e.error(`Expected first argument to be of type boolean, string, number or null, but found ${Tt(i.type)} instead`);if(4===t.length){const n=e.parse(t[3],3,pt);return n?new Ke(i,r,n):null}return new Ke(i,r)}evaluate(t){const e=this.needle.evaluate(t),i=this.haystack.evaluate(t);if(!At(e,["boolean","string","number","null"]))throw new jt(`Expected first argument to be of type boolean, string, number or null, but found ${Tt(Gt(e))} instead.`);if(!At(i,["string","array"]))throw new jt(`Expected second argument to be of type array or string, but found ${Tt(Gt(i))} instead.`);if(this.fromIndex){const r=this.fromIndex.evaluate(t);return i.indexOf(e,r)}return i.indexOf(e)}eachChild(t){t(this.needle),t(this.haystack),this.fromIndex&&t(this.fromIndex)}outputDefined(){return!1}}class Je{constructor(t,e,i,r,n,s){this.inputType=t,this.type=e,this.input=i,this.cases=r,this.outputs=n,this.otherwise=s}static parse(t,e){if(t.length<5)return e.error(`Expected at least 4 arguments, but found only ${t.length-1}.`);if(t.length%2!=1)return e.error("Expected an even number of arguments.");let i,r;e.expectedType&&"value"!==e.expectedType.kind&&(r=e.expectedType);const n={},s=[];for(let o=2;oNumber.MAX_SAFE_INTEGER)return c.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`);if("number"==typeof t&&Math.floor(t)!==t)return c.error("Numeric branch labels must be integer values.");if(i){if(c.checkSubtype(i,Gt(t)))return null}else i=Gt(t);if(void 0!==n[String(t)])return c.error("Branch labels must be unique.");n[String(t)]=s.length}const h=e.parse(l,o,r);if(!h)return null;r=r||h.type,s.push(h)}const o=e.parse(t[1],1,_t);if(!o)return null;const a=e.parse(t[t.length-1],t.length-1,r);return a?"value"!==o.type.kind&&e.concat(1).checkSubtype(i,o.type)?null:new Je(i,r,o,n,s,a):null}evaluate(t){const e=this.input.evaluate(t);return(Gt(e)===this.inputType&&this.outputs[this.cases[e]]||this.otherwise).evaluate(t)}eachChild(t){t(this.input),this.outputs.forEach(t),t(this.otherwise)}outputDefined(){return this.outputs.every((t=>t.outputDefined()))&&this.otherwise.outputDefined()}}class Ye{constructor(t,e,i){this.type=t,this.branches=e,this.otherwise=i}static parse(t,e){if(t.length<4)return e.error(`Expected at least 3 arguments, but found only ${t.length-1}.`);if(t.length%2!=0)return e.error("Expected an odd number of arguments.");let i;e.expectedType&&"value"!==e.expectedType.kind&&(i=e.expectedType);const r=[];for(let n=1;ne.outputDefined()))&&this.otherwise.outputDefined()}}class Qe{constructor(t,e,i,r){this.type=t,this.input=e,this.beginIndex=i,this.endIndex=r}static parse(t,e){if(t.length<=2||t.length>=5)return e.error(`Expected 3 or 4 arguments, but found ${t.length-1} instead.`);const i=e.parse(t[1],1,_t),r=e.parse(t[2],2,pt);if(!i||!r)return null;if(!It(i.type,[wt(_t),dt,_t]))return e.error(`Expected first argument to be of type array or string, but found ${Tt(i.type)} instead`);if(4===t.length){const n=e.parse(t[3],3,pt);return n?new Qe(i.type,i,r,n):null}return new Qe(i.type,i,r)}evaluate(t){const e=this.input.evaluate(t),i=this.beginIndex.evaluate(t);if(!At(e,["string","array"]))throw new jt(`Expected first argument to be of type array or string, but found ${Tt(Gt(e))} instead.`);if(this.endIndex){const r=this.endIndex.evaluate(t);return e.slice(i,r)}return e.slice(i)}eachChild(t){t(this.input),t(this.beginIndex),this.endIndex&&t(this.endIndex)}outputDefined(){return!1}}function ti(t,e){return"=="===t||"!="===t?"boolean"===e.kind||"string"===e.kind||"number"===e.kind||"null"===e.kind||"value"===e.kind:"string"===e.kind||"number"===e.kind||"value"===e.kind}function ei(t,e,i,r){return 0===r.compare(e,i)}function ii(t,e,i){const r="=="!==t&&"!="!==t;return class n{constructor(t,e,i){this.type=mt,this.lhs=t,this.rhs=e,this.collator=i,this.hasUntypedArgument="value"===t.type.kind||"value"===e.type.kind}static parse(t,e){if(3!==t.length&&4!==t.length)return e.error("Expected two or three arguments.");const i=t[0];let s=e.parse(t[1],1,_t);if(!s)return null;if(!ti(i,s.type))return e.concat(1).error(`"${i}" comparisons are not supported for type '${Tt(s.type)}'.`);let o=e.parse(t[2],2,_t);if(!o)return null;if(!ti(i,o.type))return e.concat(2).error(`"${i}" comparisons are not supported for type '${Tt(o.type)}'.`);if(s.type.kind!==o.type.kind&&"value"!==s.type.kind&&"value"!==o.type.kind)return e.error(`Cannot compare types '${Tt(s.type)}' and '${Tt(o.type)}'.`);r&&("value"===s.type.kind&&"value"!==o.type.kind?s=new Xt(o.type,[s]):"value"!==s.type.kind&&"value"===o.type.kind&&(o=new Xt(s.type,[o])));let a=null;if(4===t.length){if("string"!==s.type.kind&&"string"!==o.type.kind&&"value"!==s.type.kind&&"value"!==o.type.kind)return e.error("Cannot use collator to compare non-string types.");if(a=e.parse(t[3],3,yt),!a)return null}return new n(s,o,a)}evaluate(n){const s=this.lhs.evaluate(n),o=this.rhs.evaluate(n);if(r&&this.hasUntypedArgument){const e=Gt(s),i=Gt(o);if(e.kind!==i.kind||"string"!==e.kind&&"number"!==e.kind)throw new jt(`Expected arguments for "${t}" to be (string, string) or (number, number), but found (${e.kind}, ${i.kind}) instead.`)}if(this.collator&&!r&&this.hasUntypedArgument){const t=Gt(s),i=Gt(o);if("string"!==t.kind||"string"!==i.kind)return e(n,s,o)}return this.collator?i(n,s,o,this.collator.evaluate(n)):e(n,s,o)}eachChild(t){t(this.lhs),t(this.rhs),this.collator&&t(this.collator)}outputDefined(){return!0}}}const ri=ii("==",(function(t,e,i){return e===i}),ei),ni=ii("!=",(function(t,e,i){return e!==i}),(function(t,e,i,r){return!ei(0,e,i,r)})),si=ii("<",(function(t,e,i){return e",(function(t,e,i){return e>i}),(function(t,e,i,r){return r.compare(e,i)>0})),ai=ii("<=",(function(t,e,i){return e<=i}),(function(t,e,i,r){return r.compare(e,i)<=0})),li=ii(">=",(function(t,e,i){return e>=i}),(function(t,e,i,r){return r.compare(e,i)>=0}));class ci{constructor(t,e,i,r,n){this.type=dt,this.number=t,this.locale=e,this.currency=i,this.minFractionDigits=r,this.maxFractionDigits=n}static parse(t,e){if(3!==t.length)return e.error("Expected two arguments.");const i=e.parse(t[1],1,pt);if(!i)return null;const r=t[2];if("object"!=typeof r||Array.isArray(r))return e.error("NumberFormat options argument must be an object.");let n=null;if(r.locale&&(n=e.parse(r.locale,1,dt),!n))return null;let s=null;if(r.currency&&(s=e.parse(r.currency,1,dt),!s))return null;let o=null;if(r["min-fraction-digits"]&&(o=e.parse(r["min-fraction-digits"],1,pt),!o))return null;let a=null;return r["max-fraction-digits"]&&(a=e.parse(r["max-fraction-digits"],1,pt),!a)?null:new ci(i,n,s,o,a)}evaluate(t){return new Intl.NumberFormat(this.locale?this.locale.evaluate(t):[],{style:this.currency?"currency":"decimal",currency:this.currency?this.currency.evaluate(t):void 0,minimumFractionDigits:this.minFractionDigits?this.minFractionDigits.evaluate(t):void 0,maximumFractionDigits:this.maxFractionDigits?this.maxFractionDigits.evaluate(t):void 0}).format(this.number.evaluate(t))}eachChild(t){t(this.number),this.locale&&t(this.locale),this.currency&&t(this.currency),this.minFractionDigits&&t(this.minFractionDigits),this.maxFractionDigits&&t(this.maxFractionDigits)}outputDefined(){return!1}}class hi{constructor(t){this.type=xt,this.sections=t}static parse(t,e){if(t.length<2)return e.error("Expected at least one argument.");const i=t[1];if(!Array.isArray(i)&&"object"==typeof i)return e.error("First argument must be an image or text section.");const r=[];let n=!1;for(let i=1;i<=t.length-1;++i){const s=t[i];if(n&&"object"==typeof s&&!Array.isArray(s)){n=!1;let t=null;if(s["font-scale"]&&(t=e.parse(s["font-scale"],1,pt),!t))return null;let i=null;if(s["text-font"]&&(i=e.parse(s["text-font"],1,wt(dt)),!i))return null;let o=null;if(s["text-color"]&&(o=e.parse(s["text-color"],1,ft),!o))return null;const a=r[r.length-1];a.scale=t,a.font=i,a.textColor=o}else{const s=e.parse(t[i],1,_t);if(!s)return null;const o=s.type.kind;if("string"!==o&&"value"!==o&&"null"!==o&&"resolvedImage"!==o)return e.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");n=!0,r.push({content:s,scale:null,font:null,textColor:null})}}return new hi(r)}evaluate(t){return new Ft(this.sections.map((e=>{const i=e.content.evaluate(t);return Gt(i)===bt?new Rt("",i,null,null,null):new Rt($t(i),null,e.scale?e.scale.evaluate(t):null,e.font?e.font.evaluate(t).join(","):null,e.textColor?e.textColor.evaluate(t):null)})))}eachChild(t){for(const e of this.sections)t(e.content),e.scale&&t(e.scale),e.font&&t(e.font),e.textColor&&t(e.textColor)}outputDefined(){return!1}}class ui{constructor(t){this.type=bt,this.input=t}static parse(t,e){if(2!==t.length)return e.error("Expected two arguments.");const i=e.parse(t[1],1,dt);return i?new ui(i):e.error("No image name provided.")}evaluate(t){const e=this.input.evaluate(t),i=Ut.fromString(e);return i&&t.availableImages&&(i.available=t.availableImages.indexOf(e)>-1),i}eachChild(t){t(this.input)}outputDefined(){return!1}}class pi{constructor(t){this.type=pt,this.input=t}static parse(t,e){if(2!==t.length)return e.error(`Expected 1 argument, but found ${t.length-1} instead.`);const i=e.parse(t[1],1);return i?"array"!==i.type.kind&&"string"!==i.type.kind&&"value"!==i.type.kind?e.error(`Expected argument of type string or array, but found ${Tt(i.type)} instead.`):new pi(i):null}evaluate(t){const e=this.input.evaluate(t);if("string"==typeof e)return e.length;if(Array.isArray(e))return e.length;throw new jt(`Expected value to be of type string or array, but found ${Tt(Gt(e))} instead.`)}eachChild(t){t(this.input)}outputDefined(){return!1}}const di={"==":ri,"!=":ni,">":oi,"<":si,">=":li,"<=":ai,array:Xt,at:We,boolean:Xt,case:Ye,coalesce:Ze,collator:Qt,format:hi,image:ui,in:He,"index-of":Ke,interpolate:qe,"interpolate-hcl":qe,"interpolate-lab":qe,length:pi,let:Xe,literal:qt,match:Je,number:Xt,"number-format":ci,object:Xt,slice:Qe,step:Ee,string:Xt,"to-boolean":Ht,"to-color":Ht,"to-number":Ht,"to-string":Ht,var:ve,within:ge};function mi(t,[e,i,r,n]){e=e.evaluate(t),i=i.evaluate(t),r=r.evaluate(t);const s=n?n.evaluate(t):1,o=Vt(e,i,r,s);if(o)throw new jt(o);return new Lt(e/255*s,i/255*s,r/255*s,s)}function fi(t,e){return t in e}function gi(t,e){const i=e[t];return void 0===i?null:i}function _i(t){return{type:t}}function yi(t){return{result:"success",value:t}}function xi(t){return{result:"error",value:t}}function vi(t){return"data-driven"===t["property-type"]||"cross-faded-data-driven"===t["property-type"]}function bi(t){return!!t.expression&&t.expression.parameters.indexOf("zoom")>-1}function wi(t){return!!t.expression&&t.expression.interpolated}function Ti(t){return t instanceof Number?"number":t instanceof String?"string":t instanceof Boolean?"boolean":Array.isArray(t)?"array":null===t?"null":typeof t}function Ei(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function Si(t){return t}function Ii(t,e){const i="color"===e.type,r=t.stops&&"object"==typeof t.stops[0][0],n=r||!(r||void 0!==t.property),s=t.type||(wi(e)?"exponential":"interval");if(i||"padding"===e.type){const r=i?Lt.parse:Ot.parse;(t=ot({},t)).stops&&(t.stops=t.stops.map((t=>[t[0],r(t[1])]))),t.default=r(t.default?t.default:e.default)}if(t.colorSpace&&"rgb"!==t.colorSpace&&!$e[t.colorSpace])throw new Error(`Unknown color space: ${t.colorSpace}`);let o,a,l;if("exponential"===s)o=Mi;else if("interval"===s)o=Ci;else if("categorical"===s){o=zi,a=Object.create(null);for(const e of t.stops)a[e[0]]=e[1];l=typeof t.stops[0][0]}else{if("identity"!==s)throw new Error(`Unknown function type "${s}"`);o=ki}if(r){const i={},r=[];for(let e=0;et[0])),evaluate:({zoom:i},r)=>Mi({stops:n,base:t.base},e,i).evaluate(i,r)}}if(n){const i="exponential"===s?{name:"exponential",base:void 0!==t.base?t.base:1}:null;return{kind:"camera",interpolationType:i,interpolationFactor:qe.interpolationFactor.bind(void 0,i),zoomStops:t.stops.map((t=>t[0])),evaluate:({zoom:i})=>o(t,e,i,a,l)}}return{kind:"source",evaluate(i,r){const n=r&&r.properties?r.properties[t.property]:void 0;return void 0===n?Ai(t.default,e.default):o(t,e,n,a,l)}}}function Ai(t,e,i){return void 0!==t?t:void 0!==e?e:void 0!==i?i:void 0}function zi(t,e,i,r,n){return Ai(typeof i===n?r[i]:void 0,t.default,e.default)}function Ci(t,e,i){if("number"!==Ti(i))return Ai(t.default,e.default);const r=t.stops.length;if(1===r)return t.stops[0][1];if(i<=t.stops[0][0])return t.stops[0][1];if(i>=t.stops[r-1][0])return t.stops[r-1][1];const n=Te(t.stops.map((t=>t[0])),i);return t.stops[n][1]}function Mi(t,e,i){const r=void 0!==t.base?t.base:1;if("number"!==Ti(i))return Ai(t.default,e.default);const n=t.stops.length;if(1===n)return t.stops[0][1];if(i<=t.stops[0][0])return t.stops[0][1];if(i>=t.stops[n-1][0])return t.stops[n-1][1];const s=Te(t.stops.map((t=>t[0])),i),o=function(t,e,i,r){const n=r-i,s=t-i;return 0===n?0:1===e?s/n:(Math.pow(e,s)-1)/(Math.pow(e,n)-1)}(i,r,t.stops[s][0],t.stops[s+1][0]),a=t.stops[s][1],l=t.stops[s+1][1];let c=Ie[e.type]||Si;if(t.colorSpace&&"rgb"!==t.colorSpace){const e=$e[t.colorSpace];c=(t,i)=>e.reverse(e.interpolate(e.forward(t),e.forward(i),o))}return"function"==typeof a.evaluate?{evaluate(...t){const e=a.evaluate.apply(void 0,t),i=l.evaluate.apply(void 0,t);if(void 0!==e&&void 0!==i)return c(e,i,o)}}:c(a,l,o)}function ki(t,e,i){switch(e.type){case"color":i=Lt.parse(i);break;case"formatted":i=Ft.fromString(i.toString());break;case"resolvedImage":i=Ut.fromString(i.toString());break;case"padding":i=Ot.parse(i);break;default:Ti(i)===e.type||"enum"===e.type&&e.values[i]||(i=void 0)}return Ai(i,t.default,e.default)}Yt.register(di,{error:[{kind:"error"},[dt],(t,[e])=>{throw new jt(e.evaluate(t))}],typeof:[dt,[_t],(t,[e])=>Tt(Gt(e.evaluate(t)))],"to-rgba":[wt(pt,4),[ft],(t,[e])=>e.evaluate(t).toArray()],rgb:[ft,[pt,pt,pt],mi],rgba:[ft,[pt,pt,pt,pt],mi],has:{type:mt,overloads:[[[dt],(t,[e])=>fi(e.evaluate(t),t.properties())],[[dt,gt],(t,[e,i])=>fi(e.evaluate(t),i.evaluate(t))]]},get:{type:_t,overloads:[[[dt],(t,[e])=>gi(e.evaluate(t),t.properties())],[[dt,gt],(t,[e,i])=>gi(e.evaluate(t),i.evaluate(t))]]},"feature-state":[_t,[dt],(t,[e])=>gi(e.evaluate(t),t.featureState||{})],properties:[gt,[],t=>t.properties()],"geometry-type":[dt,[],t=>t.geometryType()],id:[_t,[],t=>t.id()],zoom:[pt,[],t=>t.globals.zoom],"heatmap-density":[pt,[],t=>t.globals.heatmapDensity||0],"line-progress":[pt,[],t=>t.globals.lineProgress||0],accumulated:[_t,[],t=>void 0===t.globals.accumulated?null:t.globals.accumulated],"+":[pt,_i(pt),(t,e)=>{let i=0;for(const r of e)i+=r.evaluate(t);return i}],"*":[pt,_i(pt),(t,e)=>{let i=1;for(const r of e)i*=r.evaluate(t);return i}],"-":{type:pt,overloads:[[[pt,pt],(t,[e,i])=>e.evaluate(t)-i.evaluate(t)],[[pt],(t,[e])=>-e.evaluate(t)]]},"/":[pt,[pt,pt],(t,[e,i])=>e.evaluate(t)/i.evaluate(t)],"%":[pt,[pt,pt],(t,[e,i])=>e.evaluate(t)%i.evaluate(t)],ln2:[pt,[],()=>Math.LN2],pi:[pt,[],()=>Math.PI],e:[pt,[],()=>Math.E],"^":[pt,[pt,pt],(t,[e,i])=>Math.pow(e.evaluate(t),i.evaluate(t))],sqrt:[pt,[pt],(t,[e])=>Math.sqrt(e.evaluate(t))],log10:[pt,[pt],(t,[e])=>Math.log(e.evaluate(t))/Math.LN10],ln:[pt,[pt],(t,[e])=>Math.log(e.evaluate(t))],log2:[pt,[pt],(t,[e])=>Math.log(e.evaluate(t))/Math.LN2],sin:[pt,[pt],(t,[e])=>Math.sin(e.evaluate(t))],cos:[pt,[pt],(t,[e])=>Math.cos(e.evaluate(t))],tan:[pt,[pt],(t,[e])=>Math.tan(e.evaluate(t))],asin:[pt,[pt],(t,[e])=>Math.asin(e.evaluate(t))],acos:[pt,[pt],(t,[e])=>Math.acos(e.evaluate(t))],atan:[pt,[pt],(t,[e])=>Math.atan(e.evaluate(t))],min:[pt,_i(pt),(t,e)=>Math.min(...e.map((e=>e.evaluate(t))))],max:[pt,_i(pt),(t,e)=>Math.max(...e.map((e=>e.evaluate(t))))],abs:[pt,[pt],(t,[e])=>Math.abs(e.evaluate(t))],round:[pt,[pt],(t,[e])=>{const i=e.evaluate(t);return i<0?-Math.round(-i):Math.round(i)}],floor:[pt,[pt],(t,[e])=>Math.floor(e.evaluate(t))],ceil:[pt,[pt],(t,[e])=>Math.ceil(e.evaluate(t))],"filter-==":[mt,[dt,_t],(t,[e,i])=>t.properties()[e.value]===i.value],"filter-id-==":[mt,[_t],(t,[e])=>t.id()===e.value],"filter-type-==":[mt,[dt],(t,[e])=>t.geometryType()===e.value],"filter-<":[mt,[dt,_t],(t,[e,i])=>{const r=t.properties()[e.value],n=i.value;return typeof r==typeof n&&r{const i=t.id(),r=e.value;return typeof i==typeof r&&i":[mt,[dt,_t],(t,[e,i])=>{const r=t.properties()[e.value],n=i.value;return typeof r==typeof n&&r>n}],"filter-id->":[mt,[_t],(t,[e])=>{const i=t.id(),r=e.value;return typeof i==typeof r&&i>r}],"filter-<=":[mt,[dt,_t],(t,[e,i])=>{const r=t.properties()[e.value],n=i.value;return typeof r==typeof n&&r<=n}],"filter-id-<=":[mt,[_t],(t,[e])=>{const i=t.id(),r=e.value;return typeof i==typeof r&&i<=r}],"filter->=":[mt,[dt,_t],(t,[e,i])=>{const r=t.properties()[e.value],n=i.value;return typeof r==typeof n&&r>=n}],"filter-id->=":[mt,[_t],(t,[e])=>{const i=t.id(),r=e.value;return typeof i==typeof r&&i>=r}],"filter-has":[mt,[_t],(t,[e])=>e.value in t.properties()],"filter-has-id":[mt,[],t=>null!==t.id()&&void 0!==t.id()],"filter-type-in":[mt,[wt(dt)],(t,[e])=>e.value.indexOf(t.geometryType())>=0],"filter-id-in":[mt,[wt(_t)],(t,[e])=>e.value.indexOf(t.id())>=0],"filter-in-small":[mt,[dt,wt(_t)],(t,[e,i])=>i.value.indexOf(t.properties()[e.value])>=0],"filter-in-large":[mt,[dt,wt(_t)],(t,[e,i])=>function(t,e,i,r){for(;i<=r;){const n=i+r>>1;if(e[n]===t)return!0;e[n]>t?r=n-1:i=n+1}return!1}(t.properties()[e.value],i.value,0,i.value.length-1)],all:{type:mt,overloads:[[[mt,mt],(t,[e,i])=>e.evaluate(t)&&i.evaluate(t)],[_i(mt),(t,e)=>{for(const i of e)if(!i.evaluate(t))return!1;return!0}]]},any:{type:mt,overloads:[[[mt,mt],(t,[e,i])=>e.evaluate(t)||i.evaluate(t)],[_i(mt),(t,e)=>{for(const i of e)if(i.evaluate(t))return!0;return!1}]]},"!":[mt,[mt],(t,[e])=>!e.evaluate(t)],"is-supported-script":[mt,[dt],(t,[e])=>{const i=t.globals&&t.globals.isSupportedScript;return!i||i(e.evaluate(t))}],upcase:[dt,[dt],(t,[e])=>e.evaluate(t).toUpperCase()],downcase:[dt,[dt],(t,[e])=>e.evaluate(t).toLowerCase()],concat:[dt,_i(_t),(t,e)=>e.map((e=>$t(e.evaluate(t)))).join("")],"resolved-locale":[dt,[yt],(t,[e])=>e.evaluate(t).resolvedLocale()]});class Pi{constructor(t,e){this.expression=t,this._warningHistory={},this._evaluator=new Jt,this._defaultValue=e?function(t){return"color"===t.type&&Ei(t.default)?new Lt(0,0,0,0):"color"===t.type?Lt.parse(t.default)||null:"padding"===t.type?Ot.parse(t.default)||null:void 0===t.default?null:t.default}(e):null,this._enumValues=e&&"enum"===e.type?e.values:null}evaluateWithoutErrorHandling(t,e,i,r,n,s){return this._evaluator.globals=t,this._evaluator.feature=e,this._evaluator.featureState=i,this._evaluator.canonical=r,this._evaluator.availableImages=n||null,this._evaluator.formattedSection=s,this.expression.evaluate(this._evaluator)}evaluate(t,e,i,r,n,s){this._evaluator.globals=t,this._evaluator.feature=e||null,this._evaluator.featureState=i||null,this._evaluator.canonical=r,this._evaluator.availableImages=n||null,this._evaluator.formattedSection=s||null;try{const t=this.expression.evaluate(this._evaluator);if(null==t||"number"==typeof t&&t!=t)return this._defaultValue;if(this._enumValues&&!(t in this._enumValues))throw new jt(`Expected value to be one of ${Object.keys(this._enumValues).map((t=>JSON.stringify(t))).join(", ")}, but found ${JSON.stringify(t)} instead.`);return t}catch(t){return this._warningHistory[t.message]||(this._warningHistory[t.message]=!0,"undefined"!=typeof console&&console.warn(t.message)),this._defaultValue}}}function Di(t){return Array.isArray(t)&&t.length>0&&"string"==typeof t[0]&&t[0]in di}function Li(t,e){const i=new be(di,[],e?function(t){const e={color:ft,string:dt,number:pt,enum:dt,boolean:mt,formatted:xt,padding:vt,resolvedImage:bt};return"array"===t.type?wt(e[t.value]||_t,t.length):e[t.type]}(e):void 0),r=i.parse(t,void 0,void 0,void 0,e&&"string"===e.type?{typeAnnotation:"coerce"}:void 0);return r?yi(new Pi(r,e)):xi(i.errors)}class Bi{constructor(t,e){this.kind=t,this._styleExpression=e,this.isStateDependent="constant"!==t&&!ye(e.expression)}evaluateWithoutErrorHandling(t,e,i,r,n,s){return this._styleExpression.evaluateWithoutErrorHandling(t,e,i,r,n,s)}evaluate(t,e,i,r,n,s){return this._styleExpression.evaluate(t,e,i,r,n,s)}}class Ri{constructor(t,e,i,r){this.kind=t,this.zoomStops=i,this._styleExpression=e,this.isStateDependent="camera"!==t&&!ye(e.expression),this.interpolationType=r}evaluateWithoutErrorHandling(t,e,i,r,n,s){return this._styleExpression.evaluateWithoutErrorHandling(t,e,i,r,n,s)}evaluate(t,e,i,r,n,s){return this._styleExpression.evaluate(t,e,i,r,n,s)}interpolationFactor(t,e,i){return this.interpolationType?qe.interpolationFactor(this.interpolationType,t,e,i):0}}function Fi(t,e){const i=Li(t,e);if("error"===i.result)return i;const r=i.value.expression,n=_e(r);if(!n&&!vi(e))return xi([new ct("","data expressions not supported")]);const s=xe(r,["zoom"]);if(!s&&!bi(e))return xi([new ct("","zoom expressions not supported")]);const o=Ui(r);return o||s?o instanceof ct?xi([o]):o instanceof qe&&!wi(e)?xi([new ct("",'"interpolate" expressions cannot be used with this property')]):yi(o?new Ri(n?"camera":"composite",i.value,o.labels,o instanceof qe?o.interpolation:void 0):new Bi(n?"constant":"source",i.value)):xi([new ct("",'"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.')])}class Oi{constructor(t,e){this._parameters=t,this._specification=e,ot(this,Ii(this._parameters,this._specification))}static deserialize(t){return new Oi(t._parameters,t._specification)}static serialize(t){return{_parameters:t._parameters,_specification:t._specification}}}function Ui(t){let e=null;if(t instanceof Xe)e=Ui(t.result);else if(t instanceof Ze){for(const i of t.args)if(e=Ui(i),e)break}else(t instanceof Ee||t instanceof qe)&&t.input instanceof Yt&&"zoom"===t.input.name&&(e=t);return e instanceof ct||t.eachChild((t=>{const i=Ui(t);i instanceof ct?e=i:!e&&i?e=new ct("",'"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.'):e&&i&&e!==i&&(e=new ct("",'Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.'))})),e}function Vi(t){const e=t.key,i=t.value,r=t.valueSpec||{},n=t.objectElementValidators||{},s=t.style,o=t.styleSpec;let a=[];const l=Ti(i);if("object"!==l)return[new nt(e,i,`object expected, ${l} found`)];for(const t in i){const l=t.split(".")[0],c=r[l]||r["*"];let h;if(n[l])h=n[l];else if(r[l])h=mr;else if(n["*"])h=n["*"];else{if(!r["*"]){a.push(new nt(e,i[t],`unknown property "${t}"`));continue}h=mr}a=a.concat(h({key:(e?`${e}.`:e)+t,value:i[t],valueSpec:c,style:s,styleSpec:o,object:i,objectKey:t},i))}for(const t in r)n[t]||r[t].required&&void 0===r[t].default&&void 0===i[t]&&a.push(new nt(e,i,`missing required property "${t}"`));return a}function Ni(t){const e=t.value,i=t.valueSpec,r=t.style,n=t.styleSpec,s=t.key,o=t.arrayElementValidator||mr;if("array"!==Ti(e))return[new nt(s,e,`array expected, ${Ti(e)} found`)];if(i.length&&e.length!==i.length)return[new nt(s,e,`array length ${i.length} expected, length ${e.length} found`)];if(i["min-length"]&&e.lengthr.maximum?[new nt(e,i,`${i} is greater than the maximum value ${r.maximum}`)]:[]}function $i(t){const e=t.valueSpec,i=at(t.value.type);let r,n,s,o={};const a="categorical"!==i&&void 0===t.value.property,l=!a,c="array"===Ti(t.value.stops)&&"array"===Ti(t.value.stops[0])&&"object"===Ti(t.value.stops[0][0]),h=Vi({key:t.key,value:t.value,valueSpec:t.styleSpec.function,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{stops:function(t){if("identity"===i)return[new nt(t.key,t.value,'identity function may not have a "stops" property')];let e=[];const r=t.value;return e=e.concat(Ni({key:t.key,value:r,valueSpec:t.valueSpec,style:t.style,styleSpec:t.styleSpec,arrayElementValidator:u})),"array"===Ti(r)&&0===r.length&&e.push(new nt(t.key,r,"array must have at least one stop")),e},default:function(t){return mr({key:t.key,value:t.value,valueSpec:e,style:t.style,styleSpec:t.styleSpec})}}});return"identity"===i&&a&&h.push(new nt(t.key,t.value,'missing required property "property"')),"identity"===i||t.value.stops||h.push(new nt(t.key,t.value,'missing required property "stops"')),"exponential"===i&&t.valueSpec.expression&&!wi(t.valueSpec)&&h.push(new nt(t.key,t.value,"exponential functions not supported")),t.styleSpec.$version>=8&&(l&&!vi(t.valueSpec)?h.push(new nt(t.key,t.value,"property functions not supported")):a&&!bi(t.valueSpec)&&h.push(new nt(t.key,t.value,"zoom functions not supported"))),"categorical"!==i&&!c||void 0!==t.value.property||h.push(new nt(t.key,t.value,'"property" property is required')),h;function u(t){let i=[];const r=t.value,a=t.key;if("array"!==Ti(r))return[new nt(a,r,`array expected, ${Ti(r)} found`)];if(2!==r.length)return[new nt(a,r,`array length 2 expected, length ${r.length} found`)];if(c){if("object"!==Ti(r[0]))return[new nt(a,r,`object expected, ${Ti(r[0])} found`)];if(void 0===r[0].zoom)return[new nt(a,r,"object stop key must have zoom")];if(void 0===r[0].value)return[new nt(a,r,"object stop key must have value")];if(s&&s>at(r[0].zoom))return[new nt(a,r[0].zoom,"stop zoom values must appear in ascending order")];at(r[0].zoom)!==s&&(s=at(r[0].zoom),n=void 0,o={}),i=i.concat(Vi({key:`${a}[0]`,value:r[0],valueSpec:{zoom:{}},style:t.style,styleSpec:t.styleSpec,objectElementValidators:{zoom:Gi,value:p}}))}else i=i.concat(p({key:`${a}[0]`,value:r[0],valueSpec:{},style:t.style,styleSpec:t.styleSpec},r));return Di(lt(r[1]))?i.concat([new nt(`${a}[1]`,r[1],"expressions are not allowed in function stops.")]):i.concat(mr({key:`${a}[1]`,value:r[1],valueSpec:e,style:t.style,styleSpec:t.styleSpec}))}function p(t,s){const a=Ti(t.value),l=at(t.value),c=null!==t.value?t.value:s;if(r){if(a!==r)return[new nt(t.key,c,`${a} stop domain type must match previous stop domain type ${r}`)]}else r=a;if("number"!==a&&"string"!==a&&"boolean"!==a)return[new nt(t.key,c,"stop domain value must be a number, string, or boolean")];if("number"!==a&&"categorical"!==i){let r=`number expected, ${a} found`;return vi(e)&&void 0===i&&(r+='\nIf you intended to use a categorical function, specify `"type": "categorical"`.'),[new nt(t.key,c,r)]}return"categorical"!==i||"number"!==a||isFinite(l)&&Math.floor(l)===l?"categorical"!==i&&"number"===a&&void 0!==n&&lnew nt(`${t.key}${e.key}`,t.value,e.message)));const i=e.value.expression||e.value._styleExpression.expression;if("property"===t.expressionContext&&"text-font"===t.propertyKey&&!i.outputDefined())return[new nt(t.key,t.value,`Invalid data expression for "${t.propertyKey}". Output values must be contained as literals within the expression.`)];if("property"===t.expressionContext&&"layout"===t.propertyType&&!ye(i))return[new nt(t.key,t.value,'"feature-state" data expressions are not supported with layout properties.')];if("filter"===t.expressionContext&&!ye(i))return[new nt(t.key,t.value,'"feature-state" data expressions are not supported with filters.')];if(t.expressionContext&&0===t.expressionContext.indexOf("cluster")){if(!xe(i,["zoom","feature-state"]))return[new nt(t.key,t.value,'"zoom" and "feature-state" expressions are not supported with cluster properties.')];if("cluster-initial"===t.expressionContext&&!_e(i))return[new nt(t.key,t.value,"Feature data expressions are not supported with initial expression part of cluster properties.")]}return[]}function ji(t){const e=t.key,i=t.value,r=t.valueSpec,n=[];return Array.isArray(r.values)?-1===r.values.indexOf(at(i))&&n.push(new nt(e,i,`expected one of [${r.values.join(", ")}], ${JSON.stringify(i)} found`)):-1===Object.keys(r.values).indexOf(at(i))&&n.push(new nt(e,i,`expected one of [${Object.keys(r.values).join(", ")}], ${JSON.stringify(i)} found`)),n}function Zi(t){if(!0===t||!1===t)return!0;if(!Array.isArray(t)||0===t.length)return!1;switch(t[0]){case"has":return t.length>=2&&"$id"!==t[1]&&"$type"!==t[1];case"in":return t.length>=3&&("string"!=typeof t[1]||Array.isArray(t[2]));case"!in":case"!has":case"none":return!1;case"==":case"!=":case">":case">=":case"<":case"<=":return 3!==t.length||Array.isArray(t[1])||Array.isArray(t[2]);case"any":case"all":for(const e of t.slice(1))if(!Zi(e)&&"boolean"!=typeof e)return!1;return!0;default:return!0}}const Xi={type:"boolean",default:!1,transition:!1,"property-type":"data-driven",expression:{interpolated:!1,parameters:["zoom","feature"]}};function Wi(t){if(null==t)return{filter:()=>!0,needGeometry:!1};Zi(t)||(t=Ji(t));const e=Li(t,Xi);if("error"===e.result)throw new Error(e.value.map((t=>`${t.key}: ${t.message}`)).join(", "));return{filter:(t,i,r)=>e.value.evaluate(t,i,{},r),needGeometry:Ki(t)}}function Hi(t,e){return te?1:0}function Ki(t){if(!Array.isArray(t))return!1;if("within"===t[0])return!0;for(let e=1;e"===e||"<="===e||">="===e?Yi(t[1],t[2],e):"any"===e?(i=t.slice(1),["any"].concat(i.map(Ji))):"all"===e?["all"].concat(t.slice(1).map(Ji)):"none"===e?["all"].concat(t.slice(1).map(Ji).map(er)):"in"===e?Qi(t[1],t.slice(2)):"!in"===e?er(Qi(t[1],t.slice(2))):"has"===e?tr(t[1]):"!has"===e?er(tr(t[1])):"within"!==e||t;var i}function Yi(t,e,i){switch(t){case"$type":return[`filter-type-${i}`,e];case"$id":return[`filter-id-${i}`,e];default:return[`filter-${i}`,t,e]}}function Qi(t,e){if(0===e.length)return!1;switch(t){case"$type":return["filter-type-in",["literal",e]];case"$id":return["filter-id-in",["literal",e]];default:return e.length>200&&!e.some((t=>typeof t!=typeof e[0]))?["filter-in-large",t,["literal",e.sort(Hi)]]:["filter-in-small",t,["literal",e]]}}function tr(t){switch(t){case"$type":return!0;case"$id":return["filter-has-id"];default:return["filter-has",t]}}function er(t){return["!",t]}function ir(t){return Zi(lt(t.value))?qi(ot({},t,{expressionContext:"filter",valueSpec:{value:"boolean"}})):rr(t)}function rr(t){const e=t.value,i=t.key;if("array"!==Ti(e))return[new nt(i,e,`array expected, ${Ti(e)} found`)];const r=t.styleSpec;let n,s=[];if(e.length<1)return[new nt(i,e,"filter array must have at least 1 element")];switch(s=s.concat(ji({key:`${i}[0]`,value:e[0],valueSpec:r.filter_operator,style:t.style,styleSpec:t.styleSpec})),at(e[0])){case"<":case"<=":case">":case">=":e.length>=2&&"$type"===at(e[1])&&s.push(new nt(i,e,`"$type" cannot be use with operator "${e[0]}"`));case"==":case"!=":3!==e.length&&s.push(new nt(i,e,`filter array for operator "${e[0]}" must have 3 elements`));case"in":case"!in":e.length>=2&&(n=Ti(e[1]),"string"!==n&&s.push(new nt(`${i}[1]`,e[1],`string expected, ${n} found`)));for(let o=2;o{t in i&&e.push(new nt(r,i[t],`"${t}" is prohibited for ref layers`))})),n.layers.forEach((e=>{at(e.id)===a&&(t=e)})),t?t.ref?e.push(new nt(r,i.ref,"ref cannot reference another ref layer")):o=at(t.type):e.push(new nt(r,i.ref,`ref layer "${a}" not found`))}else if("background"!==o)if(i.source){const t=n.sources&&n.sources[i.source],s=t&&at(t.type);t?"vector"===s&&"raster"===o?e.push(new nt(r,i.source,`layer "${i.id}" requires a raster source`)):"raster"===s&&"raster"!==o?e.push(new nt(r,i.source,`layer "${i.id}" requires a vector source`)):"vector"!==s||i["source-layer"]?"raster-dem"===s&&"hillshade"!==o?e.push(new nt(r,i.source,"raster-dem source can only be used with layer type 'hillshade'.")):"line"!==o||!i.paint||!i.paint["line-gradient"]||"geojson"===s&&t.lineMetrics||e.push(new nt(r,i,`layer "${i.id}" specifies a line-gradient, which requires a GeoJSON source with \`lineMetrics\` enabled.`)):e.push(new nt(r,i,`layer "${i.id}" must specify a "source-layer"`)):e.push(new nt(r,i.source,`source "${i.source}" not found`))}else e.push(new nt(r,i,'missing required property "source"'));return e=e.concat(Vi({key:r,value:i,valueSpec:s.layer,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{"*":()=>[],type:()=>mr({key:`${r}.type`,value:i.type,valueSpec:s.layer.type,style:t.style,styleSpec:t.styleSpec,object:i,objectKey:"type"}),filter:ir,layout:t=>Vi({layer:i,key:t.key,value:t.value,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{"*":t=>or(ot({layerType:o},t))}}),paint:t=>Vi({layer:i,key:t.key,value:t.value,style:t.style,styleSpec:t.styleSpec,objectElementValidators:{"*":t=>sr(ot({layerType:o},t))}})}})),e}function lr(t){const e=t.value,i=t.key,r=Ti(e);return"string"!==r?[new nt(i,e,`string expected, ${r} found`)]:[]}const cr={promoteId:function({key:t,value:e}){if("string"===Ti(e))return lr({key:t,value:e});{const i=[];for(const r in e)i.push(...lr({key:`${t}.${r}`,value:e[r]}));return i}}};function hr(t){const e=t.value,i=t.key,r=t.styleSpec,n=t.style;if(!e.type)return[new nt(i,e,'"type" is required')];const s=at(e.type);let o;switch(s){case"vector":case"raster":case"raster-dem":return o=Vi({key:i,value:e,valueSpec:r[`source_${s.replace("-","_")}`],style:t.style,styleSpec:r,objectElementValidators:cr}),o;case"geojson":if(o=Vi({key:i,value:e,valueSpec:r.source_geojson,style:n,styleSpec:r,objectElementValidators:cr}),e.cluster)for(const t in e.clusterProperties){const[r,n]=e.clusterProperties[t],s="string"==typeof r?[r,["accumulated"],["get",t]]:r;o.push(...qi({key:`${i}.${t}.map`,value:n,expressionContext:"cluster-map"})),o.push(...qi({key:`${i}.${t}.reduce`,value:s,expressionContext:"cluster-reduce"}))}return o;case"video":return Vi({key:i,value:e,valueSpec:r.source_video,style:n,styleSpec:r});case"image":return Vi({key:i,value:e,valueSpec:r.source_image,style:n,styleSpec:r});case"canvas":return[new nt(i,null,"Please use runtime APIs to add canvas sources, rather than including them in stylesheets.","source.canvas")];default:return ji({key:`${i}.type`,value:e.type,valueSpec:{values:["vector","raster","raster-dem","geojson","video","image"]},style:n,styleSpec:r})}}function ur(t){const e=t.value,i=t.styleSpec,r=i.light,n=t.style;let s=[];const o=Ti(e);if(void 0===e)return s;if("object"!==o)return s=s.concat([new nt("light",e,`object expected, ${o} found`)]),s;for(const t in e){const o=t.match(/^(.*)-transition$/);s=s.concat(o&&r[o[1]]&&r[o[1]].transition?mr({key:t,value:e[t],valueSpec:i.transition,style:n,styleSpec:i}):r[t]?mr({key:t,value:e[t],valueSpec:r[t],style:n,styleSpec:i}):[new nt(t,e[t],`unknown property "${t}"`)])}return s}function pr(t){const e=t.value,i=t.styleSpec,r=i.terrain,n=t.style;let s=[];const o=Ti(e);if(void 0===e)return s;if("object"!==o)return s=s.concat([new nt("terrain",e,`object expected, ${o} found`)]),s;for(const t in e)s=s.concat(r[t]?mr({key:t,value:e[t],valueSpec:r[t],style:n,styleSpec:i}):[new nt(t,e[t],`unknown property "${t}"`)]);return s}const dr={"*":()=>[],array:Ni,boolean:function(t){const e=t.value,i=t.key,r=Ti(e);return"boolean"!==r?[new nt(i,e,`boolean expected, ${r} found`)]:[]},number:Gi,color:function(t){const e=t.key,i=t.value,r=Ti(i);return"string"!==r?[new nt(e,i,`color expected, ${r} found`)]:null===zt(i)?[new nt(e,i,`color expected, "${i}" found`)]:[]},constants:st,enum:ji,filter:ir,function:$i,layer:ar,object:Vi,source:hr,light:ur,terrain:pr,string:lr,formatted:function(t){return 0===lr(t).length?[]:qi(t)},resolvedImage:function(t){return 0===lr(t).length?[]:qi(t)},padding:function(t){const e=t.key,i=t.value;if("array"===Ti(i)){if(i.length<1||i.length>4)return[new nt(e,i,`padding requires 1 to 4 values; ${i.length} values found`)];const t={type:"number"};let r=[];for(let n=0;n[]}})),t.constants&&(i=i.concat(st({key:"constants",value:t.constants,style:t,styleSpec:e}))),_r(i)}function _r(t){return[].concat(t).sort(((t,e)=>t.line-e.line))}function yr(t){return function(...e){return _r(t.apply(this,e))}}gr.source=yr(hr),gr.light=yr(ur),gr.terrain=yr(pr),gr.layer=yr(ar),gr.filter=yr(ir),gr.paintProperty=yr(sr),gr.layoutProperty=yr(or);const xr=gr,vr=xr.light,br=xr.paintProperty,wr=xr.layoutProperty;function Tr(t,e){let i=!1;if(e&&e.length)for(const r of e)t.fire(new et(new Error(r.message))),i=!0;return i}class Er{constructor(t,e,i){const r=this.cells=[];if(t instanceof ArrayBuffer){this.arrayBuffer=t;const n=new Int32Array(this.arrayBuffer);t=n[0],this.d=(e=n[1])+2*(i=n[2]);for(let t=0;t=c[l+0]&&r>=c[l+1])?(o[u]=!0,s.push(n[u])):o[u]=!1}}}}_forEachCell(t,e,i,r,n,s,o,a){const l=this._convertToCellCoord(t),c=this._convertToCellCoord(e),h=this._convertToCellCoord(i),u=this._convertToCellCoord(r);for(let p=l;p<=h;p++)for(let l=c;l<=u;l++){const c=this.d*l+p;if((!a||a(this._convertFromCellCoord(p),this._convertFromCellCoord(l),this._convertFromCellCoord(p+1),this._convertFromCellCoord(l+1)))&&n.call(this,t,e,i,r,c,s,o,a))return}}_convertFromCellCoord(t){return(t-this.padding)/this.scale}_convertToCellCoord(t){return Math.max(0,Math.min(this.d-1,Math.floor(t*this.scale)+this.padding))}toArrayBuffer(){if(this.arrayBuffer)return this.arrayBuffer;const t=this.cells,e=3+this.cells.length+1+1;let i=0;for(let t=0;t=0)continue;const s=t[i];n[i]=Sr[r].shallow.indexOf(i)>=0?s:zr(s,e)}t instanceof Error&&(n.message=t.message)}if(n.$name)throw new Error("$name property is reserved for worker serialization logic.");return"Object"!==r&&(n.$name=r),n}throw new Error("can't serialize object of type "+typeof t)}function Cr(t){if(null==t||"boolean"==typeof t||"number"==typeof t||"string"==typeof t||t instanceof Boolean||t instanceof Number||t instanceof String||t instanceof Date||t instanceof RegExp||t instanceof Blob||Ar(t)||T(t)||ArrayBuffer.isView(t)||t instanceof ImageData)return t;if(Array.isArray(t))return t.map(Cr);if("object"==typeof t){const e=t.$name||"Object";if(!Sr[e])throw new Error(`can't deserialize unregistered class ${e}`);const{klass:i}=Sr[e];if(!i)throw new Error(`can't deserialize unregistered class ${e}`);if(i.deserialize)return i.deserialize(t);const r=Object.create(i.prototype);for(const i of Object.keys(t)){if("$name"===i)continue;const n=t[i];r[i]=Sr[e].shallow.indexOf(i)>=0?n:Cr(n)}return r}throw new Error("can't deserialize object of type "+typeof t)}class Mr{constructor(){this.first=!0}update(t,e){const i=Math.floor(t);return this.first?(this.first=!1,this.lastIntegerZoom=i,this.lastIntegerZoomTime=0,this.lastZoom=t,this.lastFloorZoom=i,!0):(this.lastFloorZoom>i?(this.lastIntegerZoom=i+1,this.lastIntegerZoomTime=e):this.lastFloorZoomt>=128&&t<=255,Arabic:t=>t>=1536&&t<=1791,"Arabic Supplement":t=>t>=1872&&t<=1919,"Arabic Extended-A":t=>t>=2208&&t<=2303,"Hangul Jamo":t=>t>=4352&&t<=4607,"Unified Canadian Aboriginal Syllabics":t=>t>=5120&&t<=5759,Khmer:t=>t>=6016&&t<=6143,"Unified Canadian Aboriginal Syllabics Extended":t=>t>=6320&&t<=6399,"General Punctuation":t=>t>=8192&&t<=8303,"Letterlike Symbols":t=>t>=8448&&t<=8527,"Number Forms":t=>t>=8528&&t<=8591,"Miscellaneous Technical":t=>t>=8960&&t<=9215,"Control Pictures":t=>t>=9216&&t<=9279,"Optical Character Recognition":t=>t>=9280&&t<=9311,"Enclosed Alphanumerics":t=>t>=9312&&t<=9471,"Geometric Shapes":t=>t>=9632&&t<=9727,"Miscellaneous Symbols":t=>t>=9728&&t<=9983,"Miscellaneous Symbols and Arrows":t=>t>=11008&&t<=11263,"CJK Radicals Supplement":t=>t>=11904&&t<=12031,"Kangxi Radicals":t=>t>=12032&&t<=12255,"Ideographic Description Characters":t=>t>=12272&&t<=12287,"CJK Symbols and Punctuation":t=>t>=12288&&t<=12351,Hiragana:t=>t>=12352&&t<=12447,Katakana:t=>t>=12448&&t<=12543,Bopomofo:t=>t>=12544&&t<=12591,"Hangul Compatibility Jamo":t=>t>=12592&&t<=12687,Kanbun:t=>t>=12688&&t<=12703,"Bopomofo Extended":t=>t>=12704&&t<=12735,"CJK Strokes":t=>t>=12736&&t<=12783,"Katakana Phonetic Extensions":t=>t>=12784&&t<=12799,"Enclosed CJK Letters and Months":t=>t>=12800&&t<=13055,"CJK Compatibility":t=>t>=13056&&t<=13311,"CJK Unified Ideographs Extension A":t=>t>=13312&&t<=19903,"Yijing Hexagram Symbols":t=>t>=19904&&t<=19967,"CJK Unified Ideographs":t=>t>=19968&&t<=40959,"Yi Syllables":t=>t>=40960&&t<=42127,"Yi Radicals":t=>t>=42128&&t<=42191,"Hangul Jamo Extended-A":t=>t>=43360&&t<=43391,"Hangul Syllables":t=>t>=44032&&t<=55215,"Hangul Jamo Extended-B":t=>t>=55216&&t<=55295,"Private Use Area":t=>t>=57344&&t<=63743,"CJK Compatibility Ideographs":t=>t>=63744&&t<=64255,"Arabic Presentation Forms-A":t=>t>=64336&&t<=65023,"Vertical Forms":t=>t>=65040&&t<=65055,"CJK Compatibility Forms":t=>t>=65072&&t<=65103,"Small Form Variants":t=>t>=65104&&t<=65135,"Arabic Presentation Forms-B":t=>t>=65136&&t<=65279,"Halfwidth and Fullwidth Forms":t=>t>=65280&&t<=65519};function Pr(t){for(const e of t)if(Br(e.charCodeAt(0)))return!0;return!1}function Dr(t){for(const e of t)if(!Lr(e.charCodeAt(0)))return!1;return!0}function Lr(t){return!(kr.Arabic(t)||kr["Arabic Supplement"](t)||kr["Arabic Extended-A"](t)||kr["Arabic Presentation Forms-A"](t)||kr["Arabic Presentation Forms-B"](t))}function Br(t){return!(746!==t&&747!==t&&(t<4352||!(kr["Bopomofo Extended"](t)||kr.Bopomofo(t)||kr["CJK Compatibility Forms"](t)&&!(t>=65097&&t<=65103)||kr["CJK Compatibility Ideographs"](t)||kr["CJK Compatibility"](t)||kr["CJK Radicals Supplement"](t)||kr["CJK Strokes"](t)||!(!kr["CJK Symbols and Punctuation"](t)||t>=12296&&t<=12305||t>=12308&&t<=12319||12336===t)||kr["CJK Unified Ideographs Extension A"](t)||kr["CJK Unified Ideographs"](t)||kr["Enclosed CJK Letters and Months"](t)||kr["Hangul Compatibility Jamo"](t)||kr["Hangul Jamo Extended-A"](t)||kr["Hangul Jamo Extended-B"](t)||kr["Hangul Jamo"](t)||kr["Hangul Syllables"](t)||kr.Hiragana(t)||kr["Ideographic Description Characters"](t)||kr.Kanbun(t)||kr["Kangxi Radicals"](t)||kr["Katakana Phonetic Extensions"](t)||kr.Katakana(t)&&12540!==t||!(!kr["Halfwidth and Fullwidth Forms"](t)||65288===t||65289===t||65293===t||t>=65306&&t<=65310||65339===t||65341===t||65343===t||t>=65371&&t<=65503||65507===t||t>=65512&&t<=65519)||!(!kr["Small Form Variants"](t)||t>=65112&&t<=65118||t>=65123&&t<=65126)||kr["Unified Canadian Aboriginal Syllabics"](t)||kr["Unified Canadian Aboriginal Syllabics Extended"](t)||kr["Vertical Forms"](t)||kr["Yijing Hexagram Symbols"](t)||kr["Yi Syllables"](t)||kr["Yi Radicals"](t))))}function Rr(t){return!(Br(t)||function(t){return!!(kr["Latin-1 Supplement"](t)&&(167===t||169===t||174===t||177===t||188===t||189===t||190===t||215===t||247===t)||kr["General Punctuation"](t)&&(8214===t||8224===t||8225===t||8240===t||8241===t||8251===t||8252===t||8258===t||8263===t||8264===t||8265===t||8273===t)||kr["Letterlike Symbols"](t)||kr["Number Forms"](t)||kr["Miscellaneous Technical"](t)&&(t>=8960&&t<=8967||t>=8972&&t<=8991||t>=8996&&t<=9e3||9003===t||t>=9085&&t<=9114||t>=9150&&t<=9165||9167===t||t>=9169&&t<=9179||t>=9186&&t<=9215)||kr["Control Pictures"](t)&&9251!==t||kr["Optical Character Recognition"](t)||kr["Enclosed Alphanumerics"](t)||kr["Geometric Shapes"](t)||kr["Miscellaneous Symbols"](t)&&!(t>=9754&&t<=9759)||kr["Miscellaneous Symbols and Arrows"](t)&&(t>=11026&&t<=11055||t>=11088&&t<=11097||t>=11192&&t<=11243)||kr["CJK Symbols and Punctuation"](t)||kr.Katakana(t)||kr["Private Use Area"](t)||kr["CJK Compatibility Forms"](t)||kr["Small Form Variants"](t)||kr["Halfwidth and Fullwidth Forms"](t)||8734===t||8756===t||8757===t||t>=9984&&t<=10087||t>=10102&&t<=10131||65532===t||65533===t)}(t))}function Fr(t){return t>=1424&&t<=2303||kr["Arabic Presentation Forms-A"](t)||kr["Arabic Presentation Forms-B"](t)}function Or(t,e){return!(!e&&Fr(t)||t>=2304&&t<=3583||t>=3840&&t<=4255||kr.Khmer(t))}function Ur(t){for(const e of t)if(Fr(e.charCodeAt(0)))return!0;return!1}const Vr="deferred",Nr="loading",Gr="loaded";let $r=null,qr="unavailable",jr=null;const Zr=function(t){t&&"string"==typeof t&&t.indexOf("NetworkError")>-1&&(qr="error"),$r&&$r(t)};function Xr(){Wr.fire(new tt("pluginStateChange",{pluginStatus:qr,pluginURL:jr}))}const Wr=new it,Hr=function(){return qr},Kr=function(){if(qr!==Vr||!jr)throw new Error("rtl-text-plugin cannot be downloaded unless a pluginURL is specified");qr=Nr,Xr(),jr&&Z({url:jr},(t=>{t?Zr(t):(qr=Gr,Xr())}))},Jr={applyArabicShaping:null,processBidirectionalText:null,processStyledBidirectionalText:null,isLoaded:()=>qr===Gr||null!=Jr.applyArabicShaping,isLoading:()=>qr===Nr,setState(t){qr=t.pluginStatus,jr=t.pluginURL},isParsed:()=>null!=Jr.applyArabicShaping&&null!=Jr.processBidirectionalText&&null!=Jr.processStyledBidirectionalText,getPluginURL:()=>jr};class Yr{constructor(t,e){this.zoom=t,e?(this.now=e.now,this.fadeDuration=e.fadeDuration,this.zoomHistory=e.zoomHistory,this.transition=e.transition):(this.now=0,this.fadeDuration=0,this.zoomHistory=new Mr,this.transition={})}isSupportedScript(t){return function(t,e){for(const i of t)if(!Or(i.charCodeAt(0),e))return!1;return!0}(t,Jr.isLoaded())}crossFadingFactor(){return 0===this.fadeDuration?1:Math.min((this.now-this.zoomHistory.lastIntegerZoomTime)/this.fadeDuration,1)}getCrossfadeParameters(){const t=this.zoom,e=t-Math.floor(t),i=this.crossFadingFactor();return t>this.zoomHistory.lastIntegerZoom?{fromScale:2,toScale:1,t:e+(1-e)*i}:{fromScale:.5,toScale:1,t:1-(1-i)*e}}}class Qr{constructor(t,e){this.property=t,this.value=e,this.expression=function(t,e){if(Ei(t))return new Oi(t,e);if(Di(t)){const i=Fi(t,e);if("error"===i.result)throw new Error(i.value.map((t=>`${t.key}: ${t.message}`)).join(", "));return i.value}{let i=t;return"color"===e.type&&"string"==typeof t?i=Lt.parse(t):"padding"!==e.type||"number"!=typeof t&&!Array.isArray(t)||(i=Ot.parse(t)),{kind:"constant",evaluate:()=>i}}}(void 0===e?t.specification.default:e,t.specification)}isDataDriven(){return"source"===this.expression.kind||"composite"===this.expression.kind}possiblyEvaluate(t,e,i){return this.property.possiblyEvaluate(this,t,e,i)}}class tn{constructor(t){this.property=t,this.value=new Qr(t,void 0)}transitioned(t,e){return new rn(this.property,this.value,e,a({},t.transition,this.transition),t.now)}untransitioned(){return new rn(this.property,this.value,null,{},0)}}class en{constructor(t){this._properties=t,this._values=Object.create(t.defaultTransitionablePropertyValues)}getValue(t){return p(this._values[t].value.value)}setValue(t,e){Object.prototype.hasOwnProperty.call(this._values,t)||(this._values[t]=new tn(this._values[t].property)),this._values[t].value=new Qr(this._values[t].property,null===e?void 0:p(e))}getTransition(t){return p(this._values[t].transition)}setTransition(t,e){Object.prototype.hasOwnProperty.call(this._values,t)||(this._values[t]=new tn(this._values[t].property)),this._values[t].transition=p(e)||void 0}serialize(){const t={};for(const e of Object.keys(this._values)){const i=this.getValue(e);void 0!==i&&(t[e]=i);const r=this.getTransition(e);void 0!==r&&(t[`${e}-transition`]=r)}return t}transitioned(t,e){const i=new nn(this._properties);for(const r of Object.keys(this._values))i._values[r]=this._values[r].transitioned(t,e._values[r]);return i}untransitioned(){const t=new nn(this._properties);for(const e of Object.keys(this._values))t._values[e]=this._values[e].untransitioned();return t}}class rn{constructor(t,e,i,r,n){this.property=t,this.value=e,this.begin=n+r.delay||0,this.end=this.begin+r.duration||0,t.specification.transition&&(r.delay||r.duration)&&(this.prior=i)}possiblyEvaluate(t,e,i){const r=t.now||0,n=this.value.possiblyEvaluate(t,e,i),s=this.prior;if(s){if(r>this.end)return this.prior=null,n;if(this.value.isDataDriven())return this.prior=null,n;if(r=1)return 1;const e=t*t,i=e*t;return 4*(t<.5?i:3*(t-e)+i-.75)}(o))}}return n}}class nn{constructor(t){this._properties=t,this._values=Object.create(t.defaultTransitioningPropertyValues)}possiblyEvaluate(t,e,i){const r=new an(this._properties);for(const n of Object.keys(this._values))r._values[n]=this._values[n].possiblyEvaluate(t,e,i);return r}hasTransition(){for(const t of Object.keys(this._values))if(this._values[t].prior)return!0;return!1}}class sn{constructor(t){this._properties=t,this._values=Object.create(t.defaultPropertyValues)}getValue(t){return p(this._values[t].value)}setValue(t,e){this._values[t]=new Qr(this._values[t].property,null===e?void 0:p(e))}serialize(){const t={};for(const e of Object.keys(this._values)){const i=this.getValue(e);void 0!==i&&(t[e]=i)}return t}possiblyEvaluate(t,e,i){const r=new an(this._properties);for(const n of Object.keys(this._values))r._values[n]=this._values[n].possiblyEvaluate(t,e,i);return r}}class on{constructor(t,e,i){this.property=t,this.value=e,this.parameters=i}isConstant(){return"constant"===this.value.kind}constantOr(t){return"constant"===this.value.kind?this.value.value:t}evaluate(t,e,i,r){return this.property.evaluate(this.value,this.parameters,t,e,i,r)}}class an{constructor(t){this._properties=t,this._values=Object.create(t.defaultPossiblyEvaluatedValues)}get(t){return this._values[t]}}class ln{constructor(t){this.specification=t}possiblyEvaluate(t,e){return t.expression.evaluate(e)}interpolate(t,e,i){const r=Ie[this.specification.type];return r?r(t,e,i):t}}class cn{constructor(t,e){this.specification=t,this.overrides=e}possiblyEvaluate(t,e,i,r){return new on(this,"constant"===t.expression.kind||"camera"===t.expression.kind?{kind:"constant",value:t.expression.evaluate(e,null,{},i,r)}:t.expression,e)}interpolate(t,e,i){if("constant"!==t.value.kind||"constant"!==e.value.kind)return t;if(void 0===t.value.value||void 0===e.value.value)return new on(this,{kind:"constant",value:void 0},t.parameters);const r=Ie[this.specification.type];return r?new on(this,{kind:"constant",value:r(t.value.value,e.value.value,i)},t.parameters):t}evaluate(t,e,i,r,n,s){return"constant"===t.kind?t.value:t.evaluate(e,i,r,n,s)}}class hn extends cn{possiblyEvaluate(t,e,i,r){if(void 0===t.value)return new on(this,{kind:"constant",value:void 0},e);if("constant"===t.expression.kind){const n=t.expression.evaluate(e,null,{},i,r),s="resolvedImage"===t.property.specification.type&&"string"!=typeof n?n.name:n,o=this._calculate(s,s,s,e);return new on(this,{kind:"constant",value:o},e)}if("camera"===t.expression.kind){const i=this._calculate(t.expression.evaluate({zoom:e.zoom-1}),t.expression.evaluate({zoom:e.zoom}),t.expression.evaluate({zoom:e.zoom+1}),e);return new on(this,{kind:"constant",value:i},e)}return new on(this,t.expression,e)}evaluate(t,e,i,r,n,s){if("source"===t.kind){const o=t.evaluate(e,i,r,n,s);return this._calculate(o,o,o,e)}return"composite"===t.kind?this._calculate(t.evaluate({zoom:Math.floor(e.zoom)-1},i,r),t.evaluate({zoom:Math.floor(e.zoom)},i,r),t.evaluate({zoom:Math.floor(e.zoom)+1},i,r),e):t.value}_calculate(t,e,i,r){return r.zoom>r.zoomHistory.lastIntegerZoom?{from:t,to:e}:{from:i,to:e}}interpolate(t){return t}}class un{constructor(t){this.specification=t}possiblyEvaluate(t,e,i,r){if(void 0!==t.value){if("constant"===t.expression.kind){const n=t.expression.evaluate(e,null,{},i,r);return this._calculate(n,n,n,e)}return this._calculate(t.expression.evaluate(new Yr(Math.floor(e.zoom-1),e)),t.expression.evaluate(new Yr(Math.floor(e.zoom),e)),t.expression.evaluate(new Yr(Math.floor(e.zoom+1),e)),e)}}_calculate(t,e,i,r){return r.zoom>r.zoomHistory.lastIntegerZoom?{from:t,to:e}:{from:i,to:e}}interpolate(t){return t}}class pn{constructor(t){this.specification=t}possiblyEvaluate(t,e,i,r){return!!t.expression.evaluate(e,null,{},i,r)}interpolate(){return!1}}class dn{constructor(t){this.properties=t,this.defaultPropertyValues={},this.defaultTransitionablePropertyValues={},this.defaultTransitioningPropertyValues={},this.defaultPossiblyEvaluatedValues={},this.overridableProperties=[];for(const e in t){const i=t[e];i.specification.overridable&&this.overridableProperties.push(e);const r=this.defaultPropertyValues[e]=new Qr(i,void 0),n=this.defaultTransitionablePropertyValues[e]=new tn(i);this.defaultTransitioningPropertyValues[e]=n.untransitioned(),this.defaultPossiblyEvaluatedValues[e]=r.possiblyEvaluate({})}}}Ir("DataDrivenProperty",cn),Ir("DataConstantProperty",ln),Ir("CrossFadedDataDrivenProperty",hn),Ir("CrossFadedProperty",un),Ir("ColorRampProperty",pn);const mn="-transition";class fn extends it{constructor(t,e){if(super(),this.id=t.id,this.type=t.type,this._featureFilter={filter:()=>!0,needGeometry:!1},"custom"!==t.type&&(this.metadata=t.metadata,this.minzoom=t.minzoom,this.maxzoom=t.maxzoom,"background"!==t.type&&(this.source=t.source,this.sourceLayer=t["source-layer"],this.filter=t.filter),e.layout&&(this._unevaluatedLayout=new sn(e.layout)),e.paint)){this._transitionablePaint=new en(e.paint);for(const e in t.paint)this.setPaintProperty(e,t.paint[e],{validate:!1});for(const e in t.layout)this.setLayoutProperty(e,t.layout[e],{validate:!1});this._transitioningPaint=this._transitionablePaint.untransitioned(),this.paint=new an(e.paint)}}getCrossfadeParameters(){return this._crossfadeParameters}getLayoutProperty(t){return"visibility"===t?this.visibility:this._unevaluatedLayout.getValue(t)}setLayoutProperty(t,e,i={}){null!=e&&this._validate(wr,`layers.${this.id}.layout.${t}`,t,e,i)||("visibility"!==t?this._unevaluatedLayout.setValue(t,e):this.visibility=e)}getPaintProperty(t){return t.endsWith(mn)?this._transitionablePaint.getTransition(t.slice(0,-mn.length)):this._transitionablePaint.getValue(t)}setPaintProperty(t,e,i={}){if(null!=e&&this._validate(br,`layers.${this.id}.paint.${t}`,t,e,i))return!1;if(t.endsWith(mn))return this._transitionablePaint.setTransition(t.slice(0,-mn.length),e||void 0),!1;{const i=this._transitionablePaint._values[t],r="cross-faded-data-driven"===i.property.specification["property-type"],n=i.value.isDataDriven(),s=i.value;this._transitionablePaint.setValue(t,e),this._handleSpecialPaintPropertyUpdate(t);const o=this._transitionablePaint._values[t].value;return o.isDataDriven()||n||r||this._handleOverridablePaintPropertyUpdate(t,s,o)}}_handleSpecialPaintPropertyUpdate(t){}_handleOverridablePaintPropertyUpdate(t,e,i){return!1}isHidden(t){return!!(this.minzoom&&t=this.maxzoom)||"none"===this.visibility}updateTransitions(t){this._transitioningPaint=this._transitionablePaint.transitioned(t,this._transitioningPaint)}hasTransition(){return this._transitioningPaint.hasTransition()}recalculate(t,e){t.getCrossfadeParameters&&(this._crossfadeParameters=t.getCrossfadeParameters()),this._unevaluatedLayout&&(this.layout=this._unevaluatedLayout.possiblyEvaluate(t,void 0,e)),this.paint=this._transitioningPaint.possiblyEvaluate(t,void 0,e)}serialize(){const t={id:this.id,type:this.type,source:this.source,"source-layer":this.sourceLayer,metadata:this.metadata,minzoom:this.minzoom,maxzoom:this.maxzoom,filter:this.filter,layout:this._unevaluatedLayout&&this._unevaluatedLayout.serialize(),paint:this._transitionablePaint&&this._transitionablePaint.serialize()};return this.visibility&&(t.layout=t.layout||{},t.layout.visibility=this.visibility),u(t,((t,e)=>!(void 0===t||"layout"===e&&!Object.keys(t).length||"paint"===e&&!Object.keys(t).length)))}_validate(t,e,i,r,n={}){return(!n||!1!==n.validate)&&Tr(this,t.call(xr,{key:e,layerType:this.type,objectKey:i,value:r,styleSpec:rt,style:{glyphs:!0,sprite:!0}}))}is3D(){return!1}isTileClipped(){return!1}hasOffscreenPass(){return!1}resize(){}isStateDependent(){for(const t in this.paint._values){const e=this.paint.get(t);if(e instanceof on&&vi(e.property.specification)&&("source"===e.value.kind||"composite"===e.value.kind)&&e.value.isStateDependent)return!0}return!1}}const gn={Int8:Int8Array,Uint8:Uint8Array,Int16:Int16Array,Uint16:Uint16Array,Int32:Int32Array,Uint32:Uint32Array,Float32:Float32Array};class _n{constructor(t,e){this._structArray=t,this._pos1=e*this.size,this._pos2=this._pos1/2,this._pos4=this._pos1/4,this._pos8=this._pos1/8}}class yn{constructor(){this.isTransferred=!1,this.capacity=-1,this.resize(0)}static serialize(t,e){return t._trim(),e&&(t.isTransferred=!0,e.push(t.arrayBuffer)),{length:t.length,arrayBuffer:t.arrayBuffer}}static deserialize(t){const e=Object.create(this.prototype);return e.arrayBuffer=t.arrayBuffer,e.length=t.length,e.capacity=t.arrayBuffer.byteLength/e.bytesPerElement,e._refreshViews(),e}_trim(){this.length!==this.capacity&&(this.capacity=this.length,this.arrayBuffer=this.arrayBuffer.slice(0,this.length*this.bytesPerElement),this._refreshViews())}clear(){this.length=0}resize(t){this.reserve(t),this.length=t}reserve(t){if(t>this.capacity){this.capacity=Math.max(t,Math.floor(5*this.capacity),128),this.arrayBuffer=new ArrayBuffer(this.capacity*this.bytesPerElement);const e=this.uint8;this._refreshViews(),e&&this.uint8.set(e)}}_refreshViews(){throw new Error("_refreshViews() must be implemented by each concrete StructArray layout")}}function xn(t,e=1){let i=0,r=0;return{members:t.map((t=>{const n=gn[t.type].BYTES_PER_ELEMENT,s=i=vn(i,Math.max(e,n)),o=t.components||1;return r=Math.max(r,n),i+=n*o,{name:t.name,type:t.type,components:o,offset:s}})),size:vn(i,Math.max(r,e)),alignment:e}}function vn(t,e){return Math.ceil(t/e)*e}class bn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e){const i=this.length;return this.resize(i+1),this.emplace(i,t,e)}emplace(t,e,i){const r=2*t;return this.int16[r+0]=e,this.int16[r+1]=i,t}}bn.prototype.bytesPerElement=4,Ir("StructArrayLayout2i4",bn);class wn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e,i,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,i,r)}emplace(t,e,i,r,n){const s=4*t;return this.int16[s+0]=e,this.int16[s+1]=i,this.int16[s+2]=r,this.int16[s+3]=n,t}}wn.prototype.bytesPerElement=8,Ir("StructArrayLayout4i8",wn);class Tn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,i,r,n,s)}emplace(t,e,i,r,n,s,o){const a=6*t;return this.int16[a+0]=e,this.int16[a+1]=i,this.int16[a+2]=r,this.int16[a+3]=n,this.int16[a+4]=s,this.int16[a+5]=o,t}}Tn.prototype.bytesPerElement=12,Ir("StructArrayLayout2i4i12",Tn);class En extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,i,r,n,s)}emplace(t,e,i,r,n,s,o){const a=4*t,l=8*t;return this.int16[a+0]=e,this.int16[a+1]=i,this.uint8[l+4]=r,this.uint8[l+5]=n,this.uint8[l+6]=s,this.uint8[l+7]=o,t}}En.prototype.bytesPerElement=8,Ir("StructArrayLayout2i4ub8",En);class Sn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t,e){const i=this.length;return this.resize(i+1),this.emplace(i,t,e)}emplace(t,e,i){const r=2*t;return this.float32[r+0]=e,this.float32[r+1]=i,t}}Sn.prototype.bytesPerElement=8,Ir("StructArrayLayout2f8",Sn);class In extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s,o,a,l,c){const h=this.length;return this.resize(h+1),this.emplace(h,t,e,i,r,n,s,o,a,l,c)}emplace(t,e,i,r,n,s,o,a,l,c,h){const u=10*t;return this.uint16[u+0]=e,this.uint16[u+1]=i,this.uint16[u+2]=r,this.uint16[u+3]=n,this.uint16[u+4]=s,this.uint16[u+5]=o,this.uint16[u+6]=a,this.uint16[u+7]=l,this.uint16[u+8]=c,this.uint16[u+9]=h,t}}In.prototype.bytesPerElement=20,Ir("StructArrayLayout10ui20",In);class An extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s,o,a,l,c,h,u){const p=this.length;return this.resize(p+1),this.emplace(p,t,e,i,r,n,s,o,a,l,c,h,u)}emplace(t,e,i,r,n,s,o,a,l,c,h,u,p){const d=12*t;return this.int16[d+0]=e,this.int16[d+1]=i,this.int16[d+2]=r,this.int16[d+3]=n,this.uint16[d+4]=s,this.uint16[d+5]=o,this.uint16[d+6]=a,this.uint16[d+7]=l,this.int16[d+8]=c,this.int16[d+9]=h,this.int16[d+10]=u,this.int16[d+11]=p,t}}An.prototype.bytesPerElement=24,Ir("StructArrayLayout4i4ui4i24",An);class zn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t,e,i){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,i)}emplace(t,e,i,r){const n=3*t;return this.float32[n+0]=e,this.float32[n+1]=i,this.float32[n+2]=r,t}}zn.prototype.bytesPerElement=12,Ir("StructArrayLayout3f12",zn);class Cn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer)}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.uint32[1*t+0]=e,t}}Cn.prototype.bytesPerElement=4,Ir("StructArrayLayout1ul4",Cn);class Mn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s,o,a,l){const c=this.length;return this.resize(c+1),this.emplace(c,t,e,i,r,n,s,o,a,l)}emplace(t,e,i,r,n,s,o,a,l,c){const h=10*t,u=5*t;return this.int16[h+0]=e,this.int16[h+1]=i,this.int16[h+2]=r,this.int16[h+3]=n,this.int16[h+4]=s,this.int16[h+5]=o,this.uint32[u+3]=a,this.uint16[h+8]=l,this.uint16[h+9]=c,t}}Mn.prototype.bytesPerElement=20,Ir("StructArrayLayout6i1ul2ui20",Mn);class kn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s){const o=this.length;return this.resize(o+1),this.emplace(o,t,e,i,r,n,s)}emplace(t,e,i,r,n,s,o){const a=6*t;return this.int16[a+0]=e,this.int16[a+1]=i,this.int16[a+2]=r,this.int16[a+3]=n,this.int16[a+4]=s,this.int16[a+5]=o,t}}kn.prototype.bytesPerElement=12,Ir("StructArrayLayout2i2i2i12",kn);class Pn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n){const s=this.length;return this.resize(s+1),this.emplace(s,t,e,i,r,n)}emplace(t,e,i,r,n,s){const o=4*t,a=8*t;return this.float32[o+0]=e,this.float32[o+1]=i,this.float32[o+2]=r,this.int16[a+6]=n,this.int16[a+7]=s,t}}Pn.prototype.bytesPerElement=16,Ir("StructArrayLayout2f1f2i16",Pn);class Dn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t,e,i,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,i,r)}emplace(t,e,i,r,n){const s=12*t,o=3*t;return this.uint8[s+0]=e,this.uint8[s+1]=i,this.float32[o+1]=r,this.float32[o+2]=n,t}}Dn.prototype.bytesPerElement=12,Ir("StructArrayLayout2ub2f12",Dn);class Ln extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t,e,i){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,i)}emplace(t,e,i,r){const n=3*t;return this.uint16[n+0]=e,this.uint16[n+1]=i,this.uint16[n+2]=r,t}}Ln.prototype.bytesPerElement=6,Ir("StructArrayLayout3ui6",Ln);class Bn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g){const _=this.length;return this.resize(_+1),this.emplace(_,t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g)}emplace(t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g,_){const y=24*t,x=12*t,v=48*t;return this.int16[y+0]=e,this.int16[y+1]=i,this.uint16[y+2]=r,this.uint16[y+3]=n,this.uint32[x+2]=s,this.uint32[x+3]=o,this.uint32[x+4]=a,this.uint16[y+10]=l,this.uint16[y+11]=c,this.uint16[y+12]=h,this.float32[x+7]=u,this.float32[x+8]=p,this.uint8[v+36]=d,this.uint8[v+37]=m,this.uint8[v+38]=f,this.uint32[x+10]=g,this.int16[y+22]=_,t}}Bn.prototype.bytesPerElement=48,Ir("StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48",Bn);class Rn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g,_,y,x,v,b,w,T,E,S,I,A){const z=this.length;return this.resize(z+1),this.emplace(z,t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g,_,y,x,v,b,w,T,E,S,I,A)}emplace(t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g,_,y,x,v,b,w,T,E,S,I,A,z){const C=34*t,M=17*t;return this.int16[C+0]=e,this.int16[C+1]=i,this.int16[C+2]=r,this.int16[C+3]=n,this.int16[C+4]=s,this.int16[C+5]=o,this.int16[C+6]=a,this.int16[C+7]=l,this.uint16[C+8]=c,this.uint16[C+9]=h,this.uint16[C+10]=u,this.uint16[C+11]=p,this.uint16[C+12]=d,this.uint16[C+13]=m,this.uint16[C+14]=f,this.uint16[C+15]=g,this.uint16[C+16]=_,this.uint16[C+17]=y,this.uint16[C+18]=x,this.uint16[C+19]=v,this.uint16[C+20]=b,this.uint16[C+21]=w,this.uint16[C+22]=T,this.uint32[M+12]=E,this.float32[M+13]=S,this.float32[M+14]=I,this.float32[M+15]=A,this.float32[M+16]=z,t}}Rn.prototype.bytesPerElement=68,Ir("StructArrayLayout8i15ui1ul4f68",Rn);class Fn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.float32[1*t+0]=e,t}}Fn.prototype.bytesPerElement=4,Ir("StructArrayLayout1f4",Fn);class On extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.int16=new Int16Array(this.arrayBuffer)}emplaceBack(t,e,i){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,i)}emplace(t,e,i,r){const n=3*t;return this.int16[n+0]=e,this.int16[n+1]=i,this.int16[n+2]=r,t}}On.prototype.bytesPerElement=6,Ir("StructArrayLayout3i6",On);class Un extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint32=new Uint32Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t,e,i){const r=this.length;return this.resize(r+1),this.emplace(r,t,e,i)}emplace(t,e,i,r){const n=4*t;return this.uint32[2*t+0]=e,this.uint16[n+2]=i,this.uint16[n+3]=r,t}}Un.prototype.bytesPerElement=8,Ir("StructArrayLayout1ul2ui8",Un);class Vn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t,e){const i=this.length;return this.resize(i+1),this.emplace(i,t,e)}emplace(t,e,i){const r=2*t;return this.uint16[r+0]=e,this.uint16[r+1]=i,t}}Vn.prototype.bytesPerElement=4,Ir("StructArrayLayout2ui4",Vn);class Nn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.uint16=new Uint16Array(this.arrayBuffer)}emplaceBack(t){const e=this.length;return this.resize(e+1),this.emplace(e,t)}emplace(t,e){return this.uint16[1*t+0]=e,t}}Nn.prototype.bytesPerElement=2,Ir("StructArrayLayout1ui2",Nn);class Gn extends yn{_refreshViews(){this.uint8=new Uint8Array(this.arrayBuffer),this.float32=new Float32Array(this.arrayBuffer)}emplaceBack(t,e,i,r){const n=this.length;return this.resize(n+1),this.emplace(n,t,e,i,r)}emplace(t,e,i,r,n){const s=4*t;return this.float32[s+0]=e,this.float32[s+1]=i,this.float32[s+2]=r,this.float32[s+3]=n,t}}Gn.prototype.bytesPerElement=16,Ir("StructArrayLayout4f16",Gn);class $n extends _n{get anchorPointX(){return this._structArray.int16[this._pos2+0]}get anchorPointY(){return this._structArray.int16[this._pos2+1]}get x1(){return this._structArray.int16[this._pos2+2]}get y1(){return this._structArray.int16[this._pos2+3]}get x2(){return this._structArray.int16[this._pos2+4]}get y2(){return this._structArray.int16[this._pos2+5]}get featureIndex(){return this._structArray.uint32[this._pos4+3]}get sourceLayerIndex(){return this._structArray.uint16[this._pos2+8]}get bucketIndex(){return this._structArray.uint16[this._pos2+9]}get anchorPoint(){return new S(this.anchorPointX,this.anchorPointY)}}$n.prototype.size=20;class qn extends Mn{get(t){return new $n(this,t)}}Ir("CollisionBoxArray",qn);class jn extends _n{get anchorX(){return this._structArray.int16[this._pos2+0]}get anchorY(){return this._structArray.int16[this._pos2+1]}get glyphStartIndex(){return this._structArray.uint16[this._pos2+2]}get numGlyphs(){return this._structArray.uint16[this._pos2+3]}get vertexStartIndex(){return this._structArray.uint32[this._pos4+2]}get lineStartIndex(){return this._structArray.uint32[this._pos4+3]}get lineLength(){return this._structArray.uint32[this._pos4+4]}get segment(){return this._structArray.uint16[this._pos2+10]}get lowerSize(){return this._structArray.uint16[this._pos2+11]}get upperSize(){return this._structArray.uint16[this._pos2+12]}get lineOffsetX(){return this._structArray.float32[this._pos4+7]}get lineOffsetY(){return this._structArray.float32[this._pos4+8]}get writingMode(){return this._structArray.uint8[this._pos1+36]}get placedOrientation(){return this._structArray.uint8[this._pos1+37]}set placedOrientation(t){this._structArray.uint8[this._pos1+37]=t}get hidden(){return this._structArray.uint8[this._pos1+38]}set hidden(t){this._structArray.uint8[this._pos1+38]=t}get crossTileID(){return this._structArray.uint32[this._pos4+10]}set crossTileID(t){this._structArray.uint32[this._pos4+10]=t}get associatedIconIndex(){return this._structArray.int16[this._pos2+22]}}jn.prototype.size=48;class Zn extends Bn{get(t){return new jn(this,t)}}Ir("PlacedSymbolArray",Zn);class Xn extends _n{get anchorX(){return this._structArray.int16[this._pos2+0]}get anchorY(){return this._structArray.int16[this._pos2+1]}get rightJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+2]}get centerJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+3]}get leftJustifiedTextSymbolIndex(){return this._structArray.int16[this._pos2+4]}get verticalPlacedTextSymbolIndex(){return this._structArray.int16[this._pos2+5]}get placedIconSymbolIndex(){return this._structArray.int16[this._pos2+6]}get verticalPlacedIconSymbolIndex(){return this._structArray.int16[this._pos2+7]}get key(){return this._structArray.uint16[this._pos2+8]}get textBoxStartIndex(){return this._structArray.uint16[this._pos2+9]}get textBoxEndIndex(){return this._structArray.uint16[this._pos2+10]}get verticalTextBoxStartIndex(){return this._structArray.uint16[this._pos2+11]}get verticalTextBoxEndIndex(){return this._structArray.uint16[this._pos2+12]}get iconBoxStartIndex(){return this._structArray.uint16[this._pos2+13]}get iconBoxEndIndex(){return this._structArray.uint16[this._pos2+14]}get verticalIconBoxStartIndex(){return this._structArray.uint16[this._pos2+15]}get verticalIconBoxEndIndex(){return this._structArray.uint16[this._pos2+16]}get featureIndex(){return this._structArray.uint16[this._pos2+17]}get numHorizontalGlyphVertices(){return this._structArray.uint16[this._pos2+18]}get numVerticalGlyphVertices(){return this._structArray.uint16[this._pos2+19]}get numIconVertices(){return this._structArray.uint16[this._pos2+20]}get numVerticalIconVertices(){return this._structArray.uint16[this._pos2+21]}get useRuntimeCollisionCircles(){return this._structArray.uint16[this._pos2+22]}get crossTileID(){return this._structArray.uint32[this._pos4+12]}set crossTileID(t){this._structArray.uint32[this._pos4+12]=t}get textBoxScale(){return this._structArray.float32[this._pos4+13]}get textOffset0(){return this._structArray.float32[this._pos4+14]}get textOffset1(){return this._structArray.float32[this._pos4+15]}get collisionCircleDiameter(){return this._structArray.float32[this._pos4+16]}}Xn.prototype.size=68;class Wn extends Rn{get(t){return new Xn(this,t)}}Ir("SymbolInstanceArray",Wn);class Hn extends Fn{getoffsetX(t){return this.float32[1*t+0]}}Ir("GlyphOffsetArray",Hn);class Kn extends On{getx(t){return this.int16[3*t+0]}gety(t){return this.int16[3*t+1]}gettileUnitDistanceFromAnchor(t){return this.int16[3*t+2]}}Ir("SymbolLineVertexArray",Kn);class Jn extends _n{get featureIndex(){return this._structArray.uint32[this._pos4+0]}get sourceLayerIndex(){return this._structArray.uint16[this._pos2+2]}get bucketIndex(){return this._structArray.uint16[this._pos2+3]}}Jn.prototype.size=8;class Yn extends Un{get(t){return new Jn(this,t)}}Ir("FeatureIndexArray",Yn);class Qn extends bn{}class ts extends bn{}class es extends bn{}class is extends Tn{}class rs extends En{}class ns extends Sn{}class ss extends In{}class os extends An{}class as extends zn{}class ls extends Cn{}class cs extends kn{}class hs extends Dn{}class us extends Ln{}class ps extends Vn{}const ds=xn([{name:"a_pos",components:2,type:"Int16"}],4),{members:ms}=ds;class fs{constructor(t=[]){this.segments=t}prepareSegment(t,e,i,r){let n=this.segments[this.segments.length-1];return t>fs.MAX_VERTEX_ARRAY_LENGTH&&m(`Max vertices per segment is ${fs.MAX_VERTEX_ARRAY_LENGTH}: bucket requested ${t}`),(!n||n.vertexLength+t>fs.MAX_VERTEX_ARRAY_LENGTH||n.sortKey!==r)&&(n={vertexOffset:e.length,primitiveOffset:i.length,vertexLength:0,primitiveLength:0},void 0!==r&&(n.sortKey=r),this.segments.push(n)),n}get(){return this.segments}destroy(){for(const t of this.segments)for(const e in t.vaos)t.vaos[e].destroy()}static simpleSegment(t,e,i,r){return new fs([{vertexOffset:t,primitiveOffset:e,vertexLength:i,primitiveLength:r,vaos:{},sortKey:0}])}}function gs(t,e){return 256*(t=s(Math.floor(t),0,255))+s(Math.floor(e),0,255)}fs.MAX_VERTEX_ARRAY_LENGTH=Math.pow(2,16)-1,Ir("SegmentVector",fs);const _s=xn([{name:"a_pattern_from",components:4,type:"Uint16"},{name:"a_pattern_to",components:4,type:"Uint16"},{name:"a_pixel_ratio_from",components:1,type:"Uint16"},{name:"a_pixel_ratio_to",components:1,type:"Uint16"}]);var ys={exports:{}},xs={exports:{}};xs.exports=function(t,e){var i,r,n,s,o,a,l,c;for(r=t.length-(i=3&t.length),n=e,o=3432918353,a=461845907,c=0;c>>16)*o&65535)<<16)&4294967295)<<15|l>>>17))*a+(((l>>>16)*a&65535)<<16)&4294967295)<<13|n>>>19))+((5*(n>>>16)&65535)<<16)&4294967295))+((58964+(s>>>16)&65535)<<16);switch(l=0,i){case 3:l^=(255&t.charCodeAt(c+2))<<16;case 2:l^=(255&t.charCodeAt(c+1))<<8;case 1:n^=l=(65535&(l=(l=(65535&(l^=255&t.charCodeAt(c)))*o+(((l>>>16)*o&65535)<<16)&4294967295)<<15|l>>>17))*a+(((l>>>16)*a&65535)<<16)&4294967295}return n^=t.length,n=2246822507*(65535&(n^=n>>>16))+((2246822507*(n>>>16)&65535)<<16)&4294967295,n=3266489909*(65535&(n^=n>>>13))+((3266489909*(n>>>16)&65535)<<16)&4294967295,(n^=n>>>16)>>>0};var vs={exports:{}};vs.exports=function(t,e){for(var i,r=t.length,n=e^r,s=0;r>=4;)i=1540483477*(65535&(i=255&t.charCodeAt(s)|(255&t.charCodeAt(++s))<<8|(255&t.charCodeAt(++s))<<16|(255&t.charCodeAt(++s))<<24))+((1540483477*(i>>>16)&65535)<<16),n=1540483477*(65535&n)+((1540483477*(n>>>16)&65535)<<16)^(i=1540483477*(65535&(i^=i>>>24))+((1540483477*(i>>>16)&65535)<<16)),r-=4,++s;switch(r){case 3:n^=(255&t.charCodeAt(s+2))<<16;case 2:n^=(255&t.charCodeAt(s+1))<<8;case 1:n=1540483477*(65535&(n^=255&t.charCodeAt(s)))+((1540483477*(n>>>16)&65535)<<16)}return n=1540483477*(65535&(n^=n>>>13))+((1540483477*(n>>>16)&65535)<<16),(n^=n>>>15)>>>0};var bs=xs.exports,ws=vs.exports;ys.exports=bs,ys.exports.murmur3=bs,ys.exports.murmur2=ws;class Ts{constructor(){this.ids=[],this.positions=[],this.indexed=!1}add(t,e,i,r){this.ids.push(Es(t)),this.positions.push(e,i,r)}getPositions(t){const e=Es(t);let i=0,r=this.ids.length-1;for(;i>1;this.ids[t]>=e?r=t:i=t+1}const n=[];for(;this.ids[i]===e;)n.push({index:this.positions[3*i],start:this.positions[3*i+1],end:this.positions[3*i+2]}),i++;return n}static serialize(t,e){const i=new Float64Array(t.ids),r=new Uint32Array(t.positions);return Ss(i,r,0,i.length-1),e&&e.push(i.buffer,r.buffer),{ids:i,positions:r}}static deserialize(t){const e=new Ts;return e.ids=t.ids,e.positions=t.positions,e.indexed=!0,e}}function Es(t){const e=+t;return!isNaN(e)&&e<=Number.MAX_SAFE_INTEGER?e:ys.exports(String(t))}function Ss(t,e,i,r){for(;i>1];let s=i-1,o=r+1;for(;;){do{s++}while(t[s]n);if(s>=o)break;Is(t,s,o),Is(e,3*s,3*o),Is(e,3*s+1,3*o+1),Is(e,3*s+2,3*o+2)}o-i`u_${t}`)),this.type=i}setUniform(t,e,i){t.set(i.constantOr(this.value))}getBinding(t,e,i){return"color"===this.type?new Ms(t,e):new zs(t,e)}}class Ls{constructor(t,e){this.uniformNames=e.map((t=>`u_${t}`)),this.patternFrom=null,this.patternTo=null,this.pixelRatioFrom=1,this.pixelRatioTo=1}setConstantPatternPositions(t,e){this.pixelRatioFrom=e.pixelRatio,this.pixelRatioTo=t.pixelRatio,this.patternFrom=e.tlbr,this.patternTo=t.tlbr}setUniform(t,e,i,r){const n="u_pattern_to"===r?this.patternTo:"u_pattern_from"===r?this.patternFrom:"u_pixel_ratio_to"===r?this.pixelRatioTo:"u_pixel_ratio_from"===r?this.pixelRatioFrom:null;n&&t.set(n)}getBinding(t,e,i){return"u_pattern"===i.substr(0,9)?new Cs(t,e):new zs(t,e)}}class Bs{constructor(t,e,i,r){this.expression=t,this.type=i,this.maxValue=0,this.paintVertexAttributes=e.map((t=>({name:`a_${t}`,type:"Float32",components:"color"===i?2:1,offset:0}))),this.paintVertexArray=new r}populatePaintArray(t,e,i,r,n){const s=this.paintVertexArray.length,o=this.expression.evaluate(new Yr(0),e,{},r,[],n);this.paintVertexArray.resize(t),this._setPaintValue(s,t,o)}updatePaintArray(t,e,i,r){const n=this.expression.evaluate({zoom:0},i,r);this._setPaintValue(t,e,n)}_setPaintValue(t,e,i){if("color"===this.type){const r=Ps(i);for(let i=t;i`u_${t}_t`)),this.type=i,this.useIntegerZoom=r,this.zoom=n,this.maxValue=0,this.paintVertexAttributes=e.map((t=>({name:`a_${t}`,type:"Float32",components:"color"===i?4:2,offset:0}))),this.paintVertexArray=new s}populatePaintArray(t,e,i,r,n){const s=this.expression.evaluate(new Yr(this.zoom),e,{},r,[],n),o=this.expression.evaluate(new Yr(this.zoom+1),e,{},r,[],n),a=this.paintVertexArray.length;this.paintVertexArray.resize(t),this._setPaintValue(a,t,s,o)}updatePaintArray(t,e,i,r){const n=this.expression.evaluate({zoom:this.zoom},i,r),s=this.expression.evaluate({zoom:this.zoom+1},i,r);this._setPaintValue(t,e,n,s)}_setPaintValue(t,e,i,r){if("color"===this.type){const n=Ps(i),s=Ps(r);for(let i=t;i`#define HAS_UNIFORM_${t}`)))}return t}getBinderAttributes(){const t=[];for(const e in this.binders){const i=this.binders[e];if(i instanceof Bs||i instanceof Rs)for(let e=0;e!0)){this.programConfigurations={};for(const r of t)this.programConfigurations[r.id]=new Os(r,e,i);this.needsUpload=!1,this._featureMap=new Ts,this._bufferOffset=0}populatePaintArrays(t,e,i,r,n,s){for(const i in this.programConfigurations)this.programConfigurations[i].populatePaintArrays(t,e,r,n,s);void 0!==e.id&&this._featureMap.add(e.id,i,this._bufferOffset,t),this._bufferOffset=t,this.needsUpload=!0}updatePaintArrays(t,e,i,r){for(const n of i)this.needsUpload=this.programConfigurations[n.id].updatePaintArrays(t,this._featureMap,e,n,r)||this.needsUpload}get(t){return this.programConfigurations[t]}upload(t){if(this.needsUpload){for(const e in this.programConfigurations)this.programConfigurations[e].upload(t);this.needsUpload=!1}}destroy(){for(const t in this.programConfigurations)this.programConfigurations[t].destroy()}}function Vs(t,e){return{"text-opacity":["opacity"],"icon-opacity":["opacity"],"text-color":["fill_color"],"icon-color":["fill_color"],"text-halo-color":["halo_color"],"icon-halo-color":["halo_color"],"text-halo-blur":["halo_blur"],"icon-halo-blur":["halo_blur"],"text-halo-width":["halo_width"],"icon-halo-width":["halo_width"],"line-gap-width":["gapwidth"],"line-pattern":["pattern_to","pattern_from","pixel_ratio_to","pixel_ratio_from"],"fill-pattern":["pattern_to","pattern_from","pixel_ratio_to","pixel_ratio_from"],"fill-extrusion-pattern":["pattern_to","pattern_from","pixel_ratio_to","pixel_ratio_from"]}[t]||[t.replace(`${e}-`,"").replace(/-/g,"_")]}function Ns(t,e,i){const r={color:{source:Sn,composite:Gn},number:{source:Fn,composite:Sn}},n=function(t){return{"line-pattern":{source:ss,composite:ss},"fill-pattern":{source:ss,composite:ss},"fill-extrusion-pattern":{source:ss,composite:ss}}[t]}(t);return n&&n[i]||r[e][i]}Ir("ConstantBinder",Ds),Ir("CrossFadedConstantBinder",Ls),Ir("SourceExpressionBinder",Bs),Ir("CrossFadedCompositeBinder",Fs),Ir("CompositeExpressionBinder",Rs),Ir("ProgramConfiguration",Os,{omit:["_buffers"]}),Ir("ProgramConfigurationSet",Us);var Gs=8192;const $s=Math.pow(2,14)-1,qs=-$s-1;function js(t){const e=Gs/t.extent,i=t.loadGeometry();for(let t=0;ti.x+1||oi.y+1)&&m("Geometry exceeds allowed extent, reduce your vector tile buffer size")}}return i}function Zs(t,e){return{type:t.type,id:t.id,properties:t.properties,geometry:e?js(t):[]}}function Xs(t,e,i,r,n){t.emplaceBack(2*e+(r+1)/2,2*i+(n+1)/2)}class Ws{constructor(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.layoutVertexArray=new ts,this.indexArray=new us,this.segments=new fs,this.programConfigurations=new Us(t.layers,t.zoom),this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id))}populate(t,e,i){const r=this.layers[0],n=[];let s=null,o=!1;"circle"===r.type&&(s=r.layout.get("circle-sort-key"),o=!s.isConstant());for(const{feature:e,id:r,index:a,sourceLayerIndex:l}of t){const t=this.layers[0]._featureFilter.needGeometry,c=Zs(e,t);if(!this.layers[0]._featureFilter.filter(new Yr(this.zoom),c,i))continue;const h=o?s.evaluate(c,{},i):void 0,u={id:r,properties:e.properties,type:e.type,sourceLayerIndex:l,index:a,geometry:t?c.geometry:js(e),patterns:{},sortKey:h};n.push(u)}o&&n.sort(((t,e)=>t.sortKey-e.sortKey));for(const r of n){const{geometry:n,index:s,sourceLayerIndex:o}=r,a=t[s].feature;this.addFeature(r,n,s,i),e.featureIndex.insert(a,n,s,o,this.index)}}update(t,e,i){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,i)}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return!this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,ms),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy())}addFeature(t,e,i,r){for(const i of e)for(const e of i){const i=e.x,r=e.y;if(i<0||i>=Gs||r<0||r>=Gs)continue;const n=this.segments.prepareSegment(4,this.layoutVertexArray,this.indexArray,t.sortKey),s=n.vertexLength;Xs(this.layoutVertexArray,i,r,-1,-1),Xs(this.layoutVertexArray,i,r,1,-1),Xs(this.layoutVertexArray,i,r,1,1),Xs(this.layoutVertexArray,i,r,-1,1),this.indexArray.emplaceBack(s,s+1,s+2),this.indexArray.emplaceBack(s,s+3,s+2),n.vertexLength+=4,n.primitiveLength+=2}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,i,{},r)}}function Hs(t,e){for(let i=0;i1){if(Qs(t,e))return!0;for(let r=0;r1?i:i.sub(e)._mult(n)._add(e))}function ro(t,e){let i,r,n,s=!1;for(let o=0;oe.y!=n.y>e.y&&e.x<(n.x-r.x)*(e.y-r.y)/(n.y-r.y)+r.x&&(s=!s)}return s}function no(t,e){let i=!1;for(let r=0,n=t.length-1;re.y!=o.y>e.y&&e.x<(o.x-s.x)*(e.y-s.y)/(o.y-s.y)+s.x&&(i=!i)}return i}function so(t,e,i){const r=i[0],n=i[2];if(t.xn.x&&e.x>n.x||t.yn.y&&e.y>n.y)return!1;const s=f(t,e,i[0]);return s!==f(t,e,i[1])||s!==f(t,e,i[2])||s!==f(t,e,i[3])}function oo(t,e,i){const r=e.paint.get(t).value;return"constant"===r.kind?r.value:i.programConfigurations.get(e.id).getMaxValue(t)}function ao(t){return Math.sqrt(t[0]*t[0]+t[1]*t[1])}function lo(t,e,i,r,n){if(!e[0]&&!e[1])return t;const s=S.convert(e)._mult(n);"viewport"===i&&s._rotate(-r);const o=[];for(let e=0;et.width||n.height>t.height||i.x>t.width-n.width||i.y>t.height-n.height)throw new RangeError("out of range source coordinates for image copy");if(n.width>e.width||n.height>e.height||r.x>e.width-n.width||r.y>e.height-n.height)throw new RangeError("out of range destination coordinates for image copy");const o=t.data,a=e.data;for(let l=0;l{e[t.evaluationKey]=s;const o=t.expression.evaluate(e);n.data[i+r+0]=Math.floor(255*o.r/o.a),n.data[i+r+1]=Math.floor(255*o.g/o.a),n.data[i+r+2]=Math.floor(255*o.b/o.a),n.data[i+r+3]=Math.floor(255*o.a)};if(t.clips)for(let e=0,n=0;e80*i){r=s=t[0],n=o=t[1];for(var m=i;ms&&(s=a),l>o&&(o=l);c=0!==(c=Math.max(s-r,o-n))?32767/c:0}return Uo(p,d,i,r,n,c,0),d}function Fo(t,e,i,r,n){var s,o;if(n===aa(t,e,i,r)>0)for(s=e;s=e;s-=r)o=na(s,t[s],t[s+1],o);return o&&Yo(o,o.next)&&(sa(o),o=o.next),o}function Oo(t,e){if(!t)return t;e||(e=t);var i,r=t;do{if(i=!1,r.steiner||!Yo(r,r.next)&&0!==Jo(r.prev,r,r.next))r=r.next;else{if(sa(r),(r=e=r.prev)===r.next)break;i=!0}}while(i||r!==e);return e}function Uo(t,e,i,r,n,s,o){if(t){!o&&s&&function(t,e,i,r){var n=t;do{0===n.z&&(n.z=Xo(n.x,n.y,e,i,r)),n.prevZ=n.prev,n.nextZ=n.next,n=n.next}while(n!==t);n.prevZ.nextZ=null,n.prevZ=null,function(t){var e,i,r,n,s,o,a,l,c=1;do{for(i=t,t=null,s=null,o=0;i;){for(o++,r=i,a=0,e=0;e0||l>0&&r;)0!==a&&(0===l||!r||i.z<=r.z)?(n=i,i=i.nextZ,a--):(n=r,r=r.nextZ,l--),s?s.nextZ=n:t=n,n.prevZ=s,s=n;i=r}s.nextZ=null,c*=2}while(o>1)}(n)}(t,r,n,s);for(var a,l,c=t;t.prev!==t.next;)if(a=t.prev,l=t.next,s?No(t,r,n,s):Vo(t))e.push(a.i/i|0),e.push(t.i/i|0),e.push(l.i/i|0),sa(t),t=l.next,c=l.next;else if((t=l)===c){o?1===o?Uo(t=Go(Oo(t),e,i),e,i,r,n,s,2):2===o&&$o(t,e,i,r,n,s):Uo(Oo(t),e,i,r,n,s,1);break}}}function Vo(t){var e=t.prev,i=t,r=t.next;if(Jo(e,i,r)>=0)return!1;for(var n=e.x,s=i.x,o=r.x,a=e.y,l=i.y,c=r.y,h=ns?n>o?n:o:s>o?s:o,d=a>l?a>c?a:c:l>c?l:c,m=r.next;m!==e;){if(m.x>=h&&m.x<=p&&m.y>=u&&m.y<=d&&Ho(n,a,s,l,o,c,m.x,m.y)&&Jo(m.prev,m,m.next)>=0)return!1;m=m.next}return!0}function No(t,e,i,r){var n=t.prev,s=t,o=t.next;if(Jo(n,s,o)>=0)return!1;for(var a=n.x,l=s.x,c=o.x,h=n.y,u=s.y,p=o.y,d=al?a>c?a:c:l>c?l:c,g=h>u?h>p?h:p:u>p?u:p,_=Xo(d,m,e,i,r),y=Xo(f,g,e,i,r),x=t.prevZ,v=t.nextZ;x&&x.z>=_&&v&&v.z<=y;){if(x.x>=d&&x.x<=f&&x.y>=m&&x.y<=g&&x!==n&&x!==o&&Ho(a,h,l,u,c,p,x.x,x.y)&&Jo(x.prev,x,x.next)>=0)return!1;if(x=x.prevZ,v.x>=d&&v.x<=f&&v.y>=m&&v.y<=g&&v!==n&&v!==o&&Ho(a,h,l,u,c,p,v.x,v.y)&&Jo(v.prev,v,v.next)>=0)return!1;v=v.nextZ}for(;x&&x.z>=_;){if(x.x>=d&&x.x<=f&&x.y>=m&&x.y<=g&&x!==n&&x!==o&&Ho(a,h,l,u,c,p,x.x,x.y)&&Jo(x.prev,x,x.next)>=0)return!1;x=x.prevZ}for(;v&&v.z<=y;){if(v.x>=d&&v.x<=f&&v.y>=m&&v.y<=g&&v!==n&&v!==o&&Ho(a,h,l,u,c,p,v.x,v.y)&&Jo(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function Go(t,e,i){var r=t;do{var n=r.prev,s=r.next.next;!Yo(n,s)&&Qo(n,r,r.next,s)&&ia(n,s)&&ia(s,n)&&(e.push(n.i/i|0),e.push(r.i/i|0),e.push(s.i/i|0),sa(r),sa(r.next),r=t=s),r=r.next}while(r!==t);return Oo(r)}function $o(t,e,i,r,n,s){var o=t;do{for(var a=o.next.next;a!==o.prev;){if(o.i!==a.i&&Ko(o,a)){var l=ra(o,a);return o=Oo(o,o.next),l=Oo(l,l.next),Uo(o,e,i,r,n,s,0),void Uo(l,e,i,r,n,s,0)}a=a.next}o=o.next}while(o!==t)}function qo(t,e){return t.x-e.x}function jo(t,e){var i=function(t,e){var i,r=e,n=t.x,s=t.y,o=-1/0;do{if(s<=r.y&&s>=r.next.y&&r.next.y!==r.y){var a=r.x+(s-r.y)*(r.next.x-r.x)/(r.next.y-r.y);if(a<=n&&a>o&&(o=a,i=r.x=r.x&&r.x>=h&&n!==r.x&&Ho(si.x||r.x===i.x&&Zo(i,r)))&&(i=r,p=l)),r=r.next}while(r!==c);return i}(t,e);if(!i)return e;var r=ra(i,t);return Oo(r,r.next),Oo(i,i.next)}function Zo(t,e){return Jo(t.prev,t,e.prev)<0&&Jo(e.next,t,t.next)<0}function Xo(t,e,i,r,n){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=(t-i)*n|0)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=(e-r)*n|0)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function Wo(t){var e=t,i=t;do{(e.x=(t-o)*(s-a)&&(t-o)*(r-a)>=(i-o)*(e-a)&&(i-o)*(s-a)>=(n-o)*(r-a)}function Ko(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){var i=t;do{if(i.i!==t.i&&i.next.i!==t.i&&i.i!==e.i&&i.next.i!==e.i&&Qo(i,i.next,t,e))return!0;i=i.next}while(i!==t);return!1}(t,e)&&(ia(t,e)&&ia(e,t)&&function(t,e){var i=t,r=!1,n=(t.x+e.x)/2,s=(t.y+e.y)/2;do{i.y>s!=i.next.y>s&&i.next.y!==i.y&&n<(i.next.x-i.x)*(s-i.y)/(i.next.y-i.y)+i.x&&(r=!r),i=i.next}while(i!==t);return r}(t,e)&&(Jo(t.prev,t,e.prev)||Jo(t,e.prev,e))||Yo(t,e)&&Jo(t.prev,t,t.next)>0&&Jo(e.prev,e,e.next)>0)}function Jo(t,e,i){return(e.y-t.y)*(i.x-e.x)-(e.x-t.x)*(i.y-e.y)}function Yo(t,e){return t.x===e.x&&t.y===e.y}function Qo(t,e,i,r){var n=ea(Jo(t,e,i)),s=ea(Jo(t,e,r)),o=ea(Jo(i,r,t)),a=ea(Jo(i,r,e));return n!==s&&o!==a||!(0!==n||!ta(t,i,e))||!(0!==s||!ta(t,r,e))||!(0!==o||!ta(i,t,r))||!(0!==a||!ta(i,e,r))}function ta(t,e,i){return e.x<=Math.max(t.x,i.x)&&e.x>=Math.min(t.x,i.x)&&e.y<=Math.max(t.y,i.y)&&e.y>=Math.min(t.y,i.y)}function ea(t){return t>0?1:t<0?-1:0}function ia(t,e){return Jo(t.prev,t,t.next)<0?Jo(t,e,t.next)>=0&&Jo(t,t.prev,e)>=0:Jo(t,e,t.prev)<0||Jo(t,t.next,e)<0}function ra(t,e){var i=new oa(t.i,t.x,t.y),r=new oa(e.i,e.x,e.y),n=t.next,s=e.prev;return t.next=e,e.prev=t,i.next=n,n.prev=i,r.next=i,i.prev=r,s.next=r,r.prev=s,r}function na(t,e,i,r){var n=new oa(t,e,i);return r?(n.next=r.next,n.prev=r,r.next.prev=n,r.next=n):(n.prev=n,n.next=n),n}function sa(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function oa(t,e,i){this.i=t,this.x=e,this.y=i,this.prev=null,this.next=null,this.z=0,this.prevZ=null,this.nextZ=null,this.steiner=!1}function aa(t,e,i,r){for(var n=0,s=e,o=i-r;si;){if(r-i>600){var s=r-i+1,o=e-i+1,a=Math.log(s),l=.5*Math.exp(2*a/3),c=.5*Math.sqrt(a*l*(s-l)/s)*(o-s/2<0?-1:1);ca(t,e,Math.max(i,Math.floor(e-o*l/s+c)),Math.min(r,Math.floor(e+(s-o)*l/s+c)),n)}var h=t[e],u=i,p=r;for(ha(t,i,e),n(t[r],h)>0&&ha(t,i,r);u0;)p--}0===n(t[i],h)?ha(t,i,p):ha(t,++p,r),p<=e&&(i=p+1),e<=p&&(r=p-1)}}function ha(t,e,i){var r=t[e];t[e]=t[i],t[i]=r}function ua(t,e){return te?1:0}function pa(t,e){const i=t.length;if(i<=1)return[t];const r=[];let n,s;for(let e=0;e1)for(let t=0;t0&&i.holes.push(r+=t[n-1].length)}return i};class ga{constructor(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.patternFeatures=[],this.layoutVertexArray=new es,this.indexArray=new us,this.indexArray2=new ps,this.programConfigurations=new Us(t.layers,t.zoom),this.segments=new fs,this.segments2=new fs,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id))}populate(t,e,i){this.hasPattern=ma("fill",this.layers,e);const r=this.layers[0].layout.get("fill-sort-key"),n=!r.isConstant(),s=[];for(const{feature:o,id:a,index:l,sourceLayerIndex:c}of t){const t=this.layers[0]._featureFilter.needGeometry,h=Zs(o,t);if(!this.layers[0]._featureFilter.filter(new Yr(this.zoom),h,i))continue;const u=n?r.evaluate(h,{},i,e.availableImages):void 0,p={id:a,properties:o.properties,type:o.type,sourceLayerIndex:c,index:l,geometry:t?h.geometry:js(o),patterns:{},sortKey:u};s.push(p)}n&&s.sort(((t,e)=>t.sortKey-e.sortKey));for(const r of s){const{geometry:n,index:s,sourceLayerIndex:o}=r;if(this.hasPattern){const t=fa("fill",this.layers,r,this.zoom,e);this.patternFeatures.push(t)}else this.addFeature(r,n,s,i,{});e.featureIndex.insert(t[s].feature,n,s,o,this.index)}}update(t,e,i){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,i)}addFeatures(t,e,i){for(const t of this.patternFeatures)this.addFeature(t,t.geometry,t.index,e,i)}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return!this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,Lo),this.indexBuffer=t.createIndexBuffer(this.indexArray),this.indexBuffer2=t.createIndexBuffer(this.indexArray2)),this.programConfigurations.upload(t),this.uploaded=!0}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.indexBuffer2.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.segments2.destroy())}addFeature(t,e,i,r,n){for(const t of pa(e,500)){let e=0;for(const i of t)e+=i.length;const i=this.segments.prepareSegment(e,this.layoutVertexArray,this.indexArray),r=i.vertexLength,n=[],s=[];for(const e of t){if(0===e.length)continue;e!==t[0]&&s.push(n.length/2);const i=this.segments2.prepareSegment(e.length,this.layoutVertexArray,this.indexArray2),r=i.vertexLength;this.layoutVertexArray.emplaceBack(e[0].x,e[0].y),this.indexArray2.emplaceBack(r+e.length-1,r),n.push(e[0].x),n.push(e[0].y);for(let t=1;t>3}if(n--,1===r||2===r)s+=t.readSVarint(),o+=t.readSVarint(),1===r&&(e&&a.push(e),e=[]),e.push(new Ta(s,o));else{if(7!==r)throw new Error("unknown command "+r);e&&e.push(e[0].clone())}}return e&&a.push(e),a},Sa.prototype.bbox=function(){var t=this._pbf;t.pos=this._geometry;for(var e=t.readVarint()+t.pos,i=1,r=0,n=0,s=0,o=1/0,a=-1/0,l=1/0,c=-1/0;t.pos>3}if(r--,1===i||2===i)(n+=t.readSVarint())a&&(a=n),(s+=t.readSVarint())c&&(c=s);else if(7!==i)throw new Error("unknown command "+i)}return[o,l,a,c]},Sa.prototype.toGeoJSON=function(t,e,i){var r,n,s=this.extent*Math.pow(2,i),o=this.extent*t,a=this.extent*e,l=this.loadGeometry(),c=Sa.types[this.type];function h(t){for(var e=0;e>3;e=1===r?t.readString():2===r?t.readFloat():3===r?t.readDouble():4===r?t.readVarint64():5===r?t.readVarint():6===r?t.readSVarint():7===r?t.readBoolean():null}return e}(i))}Ma.prototype.feature=function(t){if(t<0||t>=this._features.length)throw new Error("feature index out of bounds");this._pbf.pos=this._features[t];var e=this._pbf.readVarint()+this._pbf.pos;return new za(this._pbf,e,this.extent,this._keys,this._values)};var Pa=Ca;function Da(t,e,i){if(3===t){var r=new Pa(i,i.readVarint()+i.pos);r.length&&(e[r.name]=r)}}wa.VectorTile=function(t,e){this.layers=t.readFields(Da,{},e)},wa.VectorTileFeature=Ea,wa.VectorTileLayer=Ca;const La=wa.VectorTileFeature.types,Ba=Math.pow(2,13);function Ra(t,e,i,r,n,s,o,a){t.emplaceBack(e,i,2*Math.floor(r*Ba)+o,n*Ba*2,s*Ba*2,Math.round(a))}class Fa{constructor(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map((t=>t.id)),this.index=t.index,this.hasPattern=!1,this.layoutVertexArray=new is,this.centroidVertexArray=new Qn,this.indexArray=new us,this.programConfigurations=new Us(t.layers,t.zoom),this.segments=new fs,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id))}populate(t,e,i){this.features=[],this.hasPattern=ma("fill-extrusion",this.layers,e);for(const{feature:r,id:n,index:s,sourceLayerIndex:o}of t){const t=this.layers[0]._featureFilter.needGeometry,a=Zs(r,t);if(!this.layers[0]._featureFilter.filter(new Yr(this.zoom),a,i))continue;const l={id:n,sourceLayerIndex:o,index:s,geometry:t?a.geometry:js(r),properties:r.properties,type:r.type,patterns:{}};this.hasPattern?this.features.push(fa("fill-extrusion",this.layers,l,this.zoom,e)):this.addFeature(l,l.geometry,s,i,{}),e.featureIndex.insert(r,l.geometry,s,o,this.index,!0)}}addFeatures(t,e,i){for(const t of this.features){const{geometry:r}=t;this.addFeature(t,r,t.index,e,i)}}update(t,e,i){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,i)}isEmpty(){return 0===this.layoutVertexArray.length&&0===this.centroidVertexArray.length}uploadPending(){return!this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,ba),this.centroidVertexBuffer=t.createVertexBuffer(this.centroidVertexArray,va.members,!0),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.centroidVertexBuffer.destroy())}addFeature(t,e,i,r,n){const s={x:0,y:0,vertexCount:0};for(const i of pa(e,500)){let e=0;for(const t of i)e+=t.length;let r=this.segments.prepareSegment(4,this.layoutVertexArray,this.indexArray);for(const t of i){if(0===t.length)continue;if(Ua(t))continue;let e=0;for(let i=0;i=1){const o=t[i-1];if(!Oa(n,o)){r.vertexLength+4>fs.MAX_VERTEX_ARRAY_LENGTH&&(r=this.segments.prepareSegment(4,this.layoutVertexArray,this.indexArray));const t=n.sub(o)._perp()._unit(),i=o.dist(n);e+i>32768&&(e=0),Ra(this.layoutVertexArray,n.x,n.y,t.x,t.y,0,0,e),Ra(this.layoutVertexArray,n.x,n.y,t.x,t.y,0,1,e),s.x+=2*n.x,s.y+=2*n.y,s.vertexCount+=2,e+=i,Ra(this.layoutVertexArray,o.x,o.y,t.x,t.y,0,0,e),Ra(this.layoutVertexArray,o.x,o.y,t.x,t.y,0,1,e),s.x+=2*o.x,s.y+=2*o.y,s.vertexCount+=2;const a=r.vertexLength;this.indexArray.emplaceBack(a,a+2,a+1),this.indexArray.emplaceBack(a+1,a+2,a+3),r.vertexLength+=4,r.primitiveLength+=2}}}}if(r.vertexLength+e>fs.MAX_VERTEX_ARRAY_LENGTH&&(r=this.segments.prepareSegment(e,this.layoutVertexArray,this.indexArray)),"Polygon"!==La[t.type])continue;const n=[],o=[],a=r.vertexLength;for(const t of i)if(0!==t.length){t!==i[0]&&o.push(n.length/2);for(let e=0;eGs)||t.y===e.y&&(t.y<0||t.y>Gs)}function Ua(t){return t.every((t=>t.x<0))||t.every((t=>t.x>Gs))||t.every((t=>t.y<0))||t.every((t=>t.y>Gs))}Ir("FillExtrusionBucket",Fa,{omit:["layers","features"]});var Va={paint:new dn({"fill-extrusion-opacity":new ln(rt["paint_fill-extrusion"]["fill-extrusion-opacity"]),"fill-extrusion-color":new cn(rt["paint_fill-extrusion"]["fill-extrusion-color"]),"fill-extrusion-translate":new ln(rt["paint_fill-extrusion"]["fill-extrusion-translate"]),"fill-extrusion-translate-anchor":new ln(rt["paint_fill-extrusion"]["fill-extrusion-translate-anchor"]),"fill-extrusion-pattern":new hn(rt["paint_fill-extrusion"]["fill-extrusion-pattern"]),"fill-extrusion-height":new cn(rt["paint_fill-extrusion"]["fill-extrusion-height"]),"fill-extrusion-base":new cn(rt["paint_fill-extrusion"]["fill-extrusion-base"]),"fill-extrusion-vertical-gradient":new ln(rt["paint_fill-extrusion"]["fill-extrusion-vertical-gradient"])})};function Na(t,e){return t.x*e.x+t.y*e.y}function Ga(t,e){if(1===t.length){let i=0;const r=e[i++];let n;for(;!n||r.equals(n);)if(n=e[i++],!n)return 1/0;for(;it.id)),this.index=t.index,this.hasPattern=!1,this.patternFeatures=[],this.lineClipsArray=[],this.gradients={},this.layers.forEach((t=>{this.gradients[t.id]={}})),this.layoutVertexArray=new rs,this.layoutVertexArray2=new ns,this.indexArray=new us,this.programConfigurations=new Us(t.layers,t.zoom),this.segments=new fs,this.maxLineLength=0,this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id))}populate(t,e,i){this.hasPattern=ma("line",this.layers,e);const r=this.layers[0].layout.get("line-sort-key"),n=!r.isConstant(),s=[];for(const{feature:e,id:o,index:a,sourceLayerIndex:l}of t){const t=this.layers[0]._featureFilter.needGeometry,c=Zs(e,t);if(!this.layers[0]._featureFilter.filter(new Yr(this.zoom),c,i))continue;const h=n?r.evaluate(c,{},i):void 0,u={id:o,properties:e.properties,type:e.type,sourceLayerIndex:l,index:a,geometry:t?c.geometry:js(e),patterns:{},sortKey:h};s.push(u)}n&&s.sort(((t,e)=>t.sortKey-e.sortKey));for(const r of s){const{geometry:n,index:s,sourceLayerIndex:o}=r;if(this.hasPattern){const t=fa("line",this.layers,r,this.zoom,e);this.patternFeatures.push(t)}else this.addFeature(r,n,s,i,{});e.featureIndex.insert(t[s].feature,n,s,o,this.index)}}update(t,e,i){this.stateDependentLayers.length&&this.programConfigurations.updatePaintArrays(t,e,this.stateDependentLayers,i)}addFeatures(t,e,i){for(const t of this.patternFeatures)this.addFeature(t,t.geometry,t.index,e,i)}isEmpty(){return 0===this.layoutVertexArray.length}uploadPending(){return!this.uploaded||this.programConfigurations.needsUpload}upload(t){this.uploaded||(0!==this.layoutVertexArray2.length&&(this.layoutVertexBuffer2=t.createVertexBuffer(this.layoutVertexArray2,Za)),this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,qa),this.indexBuffer=t.createIndexBuffer(this.indexArray)),this.programConfigurations.upload(t),this.uploaded=!0}destroy(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy())}lineFeatureClips(t){if(t.properties&&Object.prototype.hasOwnProperty.call(t.properties,"mapbox_clip_start")&&Object.prototype.hasOwnProperty.call(t.properties,"mapbox_clip_end"))return{start:+t.properties.mapbox_clip_start,end:+t.properties.mapbox_clip_end}}addFeature(t,e,i,r,n){const s=this.layers[0].layout,o=s.get("line-join").evaluate(t,{}),a=s.get("line-cap"),l=s.get("line-miter-limit"),c=s.get("line-round-limit");this.lineClips=this.lineFeatureClips(t);for(const i of e)this.addLine(i,t,o,a,l,c);this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t,i,n,r)}addLine(t,e,i,r,n,s){if(this.distance=0,this.scaledDistance=0,this.totalDistance=0,this.lineClips){this.lineClipsArray.push(this.lineClips);for(let e=0;e=2&&t[a-1].equals(t[a-2]);)a--;let l=0;for(;l0;if(b&&e>l){const t=u.dist(p);if(t>2*c){const e=u.sub(u.sub(p)._mult(c/t)._round());this.updateDistance(p,e),this.addCurrentVertex(e,m,0,0,h),p=e}}const T=p&&d;let E=T?i:o?"butt":r;if(T&&"round"===E&&(xn&&(E="bevel"),"bevel"===E&&(x>2&&(E="flipbevel"),x100)g=f.mult(-1);else{const t=x*m.add(f).mag()/m.sub(f).mag();g._perp()._mult(t*(w?-1:1))}this.addCurrentVertex(u,g,0,0,h),this.addCurrentVertex(u,g.mult(-1),0,0,h)}else if("bevel"===E||"fakeround"===E){const t=-Math.sqrt(x*x-1),e=w?t:0,i=w?0:t;if(p&&this.addCurrentVertex(u,m,e,i,h),"fakeround"===E){const t=Math.round(180*v/Math.PI/20);for(let e=1;e2*c){const e=u.add(d.sub(u)._mult(c/t)._round());this.updateDistance(u,e),this.addCurrentVertex(e,f,0,0,h),u=e}}}}addCurrentVertex(t,e,i,r,n,s=!1){const o=e.y*r-e.x,a=-e.y-e.x*r;this.addHalfVertex(t,e.x+e.y*i,e.y-e.x*i,s,!1,i,n),this.addHalfVertex(t,o,a,s,!0,-r,n),this.distance>Ha/2&&0===this.totalDistance&&(this.distance=0,this.addCurrentVertex(t,e,i,r,n,s))}addHalfVertex({x:t,y:e},i,r,n,s,o,a){const l=.5*(this.lineClips?this.scaledDistance*(Ha-1):this.scaledDistance);this.layoutVertexArray.emplaceBack((t<<1)+(n?1:0),(e<<1)+(s?1:0),Math.round(63*i)+128,Math.round(63*r)+128,1+(0===o?0:o<0?-1:1)|(63&l)<<2,l>>6),this.lineClips&&this.layoutVertexArray2.emplaceBack((this.scaledDistance-this.lineClips.start)/(this.lineClips.end-this.lineClips.start),this.lineClipsArray.length);const c=a.vertexLength++;this.e1>=0&&this.e2>=0&&(this.indexArray.emplaceBack(this.e1,this.e2,c),a.primitiveLength++),s?this.e2=c:this.e1=c}updateScaledDistance(){this.scaledDistance=this.lineClips?this.lineClips.start+(this.lineClips.end-this.lineClips.start)*this.distance/this.totalDistance:this.distance}updateDistance(t,e){this.distance+=t.dist(e),this.updateScaledDistance()}}Ir("LineBucket",Ka,{omit:["layers","patternFeatures"]});const Ja=new dn({"line-cap":new ln(rt.layout_line["line-cap"]),"line-join":new cn(rt.layout_line["line-join"]),"line-miter-limit":new ln(rt.layout_line["line-miter-limit"]),"line-round-limit":new ln(rt.layout_line["line-round-limit"]),"line-sort-key":new cn(rt.layout_line["line-sort-key"])});var Ya={paint:new dn({"line-opacity":new cn(rt.paint_line["line-opacity"]),"line-color":new cn(rt.paint_line["line-color"]),"line-translate":new ln(rt.paint_line["line-translate"]),"line-translate-anchor":new ln(rt.paint_line["line-translate-anchor"]),"line-width":new cn(rt.paint_line["line-width"]),"line-gap-width":new cn(rt.paint_line["line-gap-width"]),"line-offset":new cn(rt.paint_line["line-offset"]),"line-blur":new cn(rt.paint_line["line-blur"]),"line-dasharray":new un(rt.paint_line["line-dasharray"]),"line-pattern":new hn(rt.paint_line["line-pattern"]),"line-gradient":new pn(rt.paint_line["line-gradient"])}),layout:Ja};const Qa=new class extends cn{possiblyEvaluate(t,e){return e=new Yr(Math.floor(e.zoom),{now:e.now,fadeDuration:e.fadeDuration,zoomHistory:e.zoomHistory,transition:e.transition}),super.possiblyEvaluate(t,e)}evaluate(t,e,i,r){return e=a({},e,{zoom:Math.floor(e.zoom)}),super.evaluate(t,e,i,r)}}(Ya.paint.properties["line-width"].specification);function tl(t,e){return e>0?e+2*t:t}Qa.useIntegerZoom=!0;const el=xn([{name:"a_pos_offset",components:4,type:"Int16"},{name:"a_data",components:4,type:"Uint16"},{name:"a_pixeloffset",components:4,type:"Int16"}],4),il=xn([{name:"a_projected_pos",components:3,type:"Float32"}],4);xn([{name:"a_fade_opacity",components:1,type:"Uint32"}],4);const rl=xn([{name:"a_placed",components:2,type:"Uint8"},{name:"a_shift",components:2,type:"Float32"}]);xn([{type:"Int16",name:"anchorPointX"},{type:"Int16",name:"anchorPointY"},{type:"Int16",name:"x1"},{type:"Int16",name:"y1"},{type:"Int16",name:"x2"},{type:"Int16",name:"y2"},{type:"Uint32",name:"featureIndex"},{type:"Uint16",name:"sourceLayerIndex"},{type:"Uint16",name:"bucketIndex"}]);const nl=xn([{name:"a_pos",components:2,type:"Int16"},{name:"a_anchor_pos",components:2,type:"Int16"},{name:"a_extrude",components:2,type:"Int16"}],4),sl=xn([{name:"a_pos",components:2,type:"Float32"},{name:"a_radius",components:1,type:"Float32"},{name:"a_flags",components:2,type:"Int16"}],4);function ol(t,e,i){return t.sections.forEach((t=>{t.text=function(t,e,i){const r=e.layout.get("text-transform").evaluate(i,{});return"uppercase"===r?t=t.toLocaleUpperCase():"lowercase"===r&&(t=t.toLocaleLowerCase()),Jr.applyArabicShaping&&(t=Jr.applyArabicShaping(t)),t}(t.text,e,i)})),t}xn([{name:"triangle",components:3,type:"Uint16"}]),xn([{type:"Int16",name:"anchorX"},{type:"Int16",name:"anchorY"},{type:"Uint16",name:"glyphStartIndex"},{type:"Uint16",name:"numGlyphs"},{type:"Uint32",name:"vertexStartIndex"},{type:"Uint32",name:"lineStartIndex"},{type:"Uint32",name:"lineLength"},{type:"Uint16",name:"segment"},{type:"Uint16",name:"lowerSize"},{type:"Uint16",name:"upperSize"},{type:"Float32",name:"lineOffsetX"},{type:"Float32",name:"lineOffsetY"},{type:"Uint8",name:"writingMode"},{type:"Uint8",name:"placedOrientation"},{type:"Uint8",name:"hidden"},{type:"Uint32",name:"crossTileID"},{type:"Int16",name:"associatedIconIndex"}]),xn([{type:"Int16",name:"anchorX"},{type:"Int16",name:"anchorY"},{type:"Int16",name:"rightJustifiedTextSymbolIndex"},{type:"Int16",name:"centerJustifiedTextSymbolIndex"},{type:"Int16",name:"leftJustifiedTextSymbolIndex"},{type:"Int16",name:"verticalPlacedTextSymbolIndex"},{type:"Int16",name:"placedIconSymbolIndex"},{type:"Int16",name:"verticalPlacedIconSymbolIndex"},{type:"Uint16",name:"key"},{type:"Uint16",name:"textBoxStartIndex"},{type:"Uint16",name:"textBoxEndIndex"},{type:"Uint16",name:"verticalTextBoxStartIndex"},{type:"Uint16",name:"verticalTextBoxEndIndex"},{type:"Uint16",name:"iconBoxStartIndex"},{type:"Uint16",name:"iconBoxEndIndex"},{type:"Uint16",name:"verticalIconBoxStartIndex"},{type:"Uint16",name:"verticalIconBoxEndIndex"},{type:"Uint16",name:"featureIndex"},{type:"Uint16",name:"numHorizontalGlyphVertices"},{type:"Uint16",name:"numVerticalGlyphVertices"},{type:"Uint16",name:"numIconVertices"},{type:"Uint16",name:"numVerticalIconVertices"},{type:"Uint16",name:"useRuntimeCollisionCircles"},{type:"Uint32",name:"crossTileID"},{type:"Float32",name:"textBoxScale"},{type:"Float32",components:2,name:"textOffset"},{type:"Float32",name:"collisionCircleDiameter"}]),xn([{type:"Float32",name:"offsetX"}]),xn([{type:"Int16",name:"x"},{type:"Int16",name:"y"},{type:"Int16",name:"tileUnitDistanceFromAnchor"}]);const al={"!":"︕","#":"#",$:"$","%":"%","&":"&","(":"︵",")":"︶","*":"*","+":"+",",":"︐","-":"︲",".":"・","/":"/",":":"︓",";":"︔","<":"︿","=":"=",">":"﹀","?":"︖","@":"@","[":"﹇","\\":"\","]":"﹈","^":"^",_:"︳","`":"`","{":"︷","|":"―","}":"︸","~":"~","¢":"¢","£":"£","¥":"¥","¦":"¦","¬":"¬","¯":" ̄","–":"︲","—":"︱","‘":"﹃","’":"﹄","“":"﹁","”":"﹂","…":"︙","‧":"・","₩":"₩","、":"︑","。":"︒","〈":"︿","〉":"﹀","《":"︽","》":"︾","「":"﹁","」":"﹂","『":"﹃","』":"﹄","【":"︻","】":"︼","〔":"︹","〕":"︺","〖":"︗","〗":"︘","!":"︕","(":"︵",")":"︶",",":"︐","-":"︲",".":"・",":":"︓",";":"︔","<":"︿",">":"﹀","?":"︖","[":"﹇","]":"﹈","_":"︳","{":"︷","|":"―","}":"︸","⦅":"︵","⦆":"︶","。":"︒","「":"﹁","」":"﹂"};var ll=24,cl=pl,hl=function(t,e,i,r,n){var s,o,a=8*n-r-1,l=(1<>1,h=-7,u=i?n-1:0,p=i?-1:1,d=t[e+u];for(u+=p,s=d&(1<<-h)-1,d>>=-h,h+=a;h>0;s=256*s+t[e+u],u+=p,h-=8);for(o=s&(1<<-h)-1,s>>=-h,h+=r;h>0;o=256*o+t[e+u],u+=p,h-=8);if(0===s)s=1-c;else{if(s===l)return o?NaN:1/0*(d?-1:1);o+=Math.pow(2,r),s-=c}return(d?-1:1)*o*Math.pow(2,s-r)},ul=function(t,e,i,r,n,s){var o,a,l,c=8*s-n-1,h=(1<>1,p=23===n?Math.pow(2,-24)-Math.pow(2,-77):0,d=r?0:s-1,m=r?1:-1,f=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(a=isNaN(e)?1:0,o=h):(o=Math.floor(Math.log(e)/Math.LN2),e*(l=Math.pow(2,-o))<1&&(o--,l*=2),(e+=o+u>=1?p/l:p*Math.pow(2,1-u))*l>=2&&(o++,l/=2),o+u>=h?(a=0,o=h):o+u>=1?(a=(e*l-1)*Math.pow(2,n),o+=u):(a=e*Math.pow(2,u-1)*Math.pow(2,n),o=0));n>=8;t[i+d]=255&a,d+=m,a/=256,n-=8);for(o=o<0;t[i+d]=255&o,d+=m,o/=256,c-=8);t[i+d-m]|=128*f};function pl(t){this.buf=ArrayBuffer.isView&&ArrayBuffer.isView(t)?t:new Uint8Array(t||0),this.pos=0,this.type=0,this.length=this.buf.length}pl.Varint=0,pl.Fixed64=1,pl.Bytes=2,pl.Fixed32=5;var dl,ml=4294967296,fl=1/ml,gl="undefined"==typeof TextDecoder?null:new TextDecoder("utf8");function _l(t){return t.type===pl.Bytes?t.readVarint()+t.pos:t.pos+1}function yl(t,e,i){return i?4294967296*e+(t>>>0):4294967296*(e>>>0)+(t>>>0)}function xl(t,e,i){var r=e<=16383?1:e<=2097151?2:e<=268435455?3:Math.floor(Math.log(e)/(7*Math.LN2));i.realloc(r);for(var n=i.pos-1;n>=t;n--)i.buf[n+r]=i.buf[n]}function vl(t,e){for(var i=0;i>>8,t[i+2]=e>>>16,t[i+3]=e>>>24}function kl(t,e){return(t[e]|t[e+1]<<8|t[e+2]<<16)+(t[e+3]<<24)}function Pl(t,e,i){1===t&&i.readMessage(Dl,e)}function Dl(t,e,i){if(3===t){const{id:t,bitmap:r,width:n,height:s,left:o,top:a,advance:l}=i.readMessage(Ll,{});e.push({id:t,bitmap:new Co({width:n+6,height:s+6},r),metrics:{width:n,height:s,left:o,top:a,advance:l}})}}function Ll(t,e,i){1===t?e.id=i.readVarint():2===t?e.bitmap=i.readBytes():3===t?e.width=i.readVarint():4===t?e.height=i.readVarint():5===t?e.left=i.readSVarint():6===t?e.top=i.readSVarint():7===t&&(e.advance=i.readVarint())}function Bl(t){let e=0,i=0;for(const r of t)e+=r.w*r.h,i=Math.max(i,r.w);t.sort(((t,e)=>e.h-t.h));const r=[{x:0,y:0,w:Math.max(Math.ceil(Math.sqrt(e/.95)),i),h:1/0}];let n=0,s=0;for(const e of t)for(let t=r.length-1;t>=0;t--){const i=r[t];if(!(e.w>i.w||e.h>i.h)){if(e.x=i.x,e.y=i.y,s=Math.max(s,e.y+e.h),n=Math.max(n,e.x+e.w),e.w===i.w&&e.h===i.h){const e=r.pop();t>3,s=this.pos;this.type=7&r,t(n,e,this),this.pos===s&&this.skip(r)}return e},readMessage:function(t,e){return this.readFields(t,e,this.readVarint()+this.pos)},readFixed32:function(){var t=Cl(this.buf,this.pos);return this.pos+=4,t},readSFixed32:function(){var t=kl(this.buf,this.pos);return this.pos+=4,t},readFixed64:function(){var t=Cl(this.buf,this.pos)+Cl(this.buf,this.pos+4)*ml;return this.pos+=8,t},readSFixed64:function(){var t=Cl(this.buf,this.pos)+kl(this.buf,this.pos+4)*ml;return this.pos+=8,t},readFloat:function(){var t=hl(this.buf,this.pos,!0,23,4);return this.pos+=4,t},readDouble:function(){var t=hl(this.buf,this.pos,!0,52,8);return this.pos+=8,t},readVarint:function(t){var e,i,r=this.buf;return e=127&(i=r[this.pos++]),i<128?e:(e|=(127&(i=r[this.pos++]))<<7,i<128?e:(e|=(127&(i=r[this.pos++]))<<14,i<128?e:(e|=(127&(i=r[this.pos++]))<<21,i<128?e:function(t,e,i){var r,n,s=i.buf;if(r=(112&(n=s[i.pos++]))>>4,n<128)return yl(t,r,e);if(r|=(127&(n=s[i.pos++]))<<3,n<128)return yl(t,r,e);if(r|=(127&(n=s[i.pos++]))<<10,n<128)return yl(t,r,e);if(r|=(127&(n=s[i.pos++]))<<17,n<128)return yl(t,r,e);if(r|=(127&(n=s[i.pos++]))<<24,n<128)return yl(t,r,e);if(r|=(1&(n=s[i.pos++]))<<31,n<128)return yl(t,r,e);throw new Error("Expected varint not more than 10 bytes")}(e|=(15&(i=r[this.pos]))<<28,t,this))))},readVarint64:function(){return this.readVarint(!0)},readSVarint:function(){var t=this.readVarint();return t%2==1?(t+1)/-2:t/2},readBoolean:function(){return Boolean(this.readVarint())},readString:function(){var t=this.readVarint()+this.pos,e=this.pos;return this.pos=t,t-e>=12&&gl?function(t,e,i){return gl.decode(t.subarray(e,i))}(this.buf,e,t):function(t,e,i){for(var r="",n=e;n239?4:l>223?3:l>191?2:1;if(n+h>i)break;1===h?l<128&&(c=l):2===h?128==(192&(s=t[n+1]))&&(c=(31&l)<<6|63&s)<=127&&(c=null):3===h?(o=t[n+2],128==(192&(s=t[n+1]))&&128==(192&o)&&((c=(15&l)<<12|(63&s)<<6|63&o)<=2047||c>=55296&&c<=57343)&&(c=null)):4===h&&(o=t[n+2],a=t[n+3],128==(192&(s=t[n+1]))&&128==(192&o)&&128==(192&a)&&((c=(15&l)<<18|(63&s)<<12|(63&o)<<6|63&a)<=65535||c>=1114112)&&(c=null)),null===c?(c=65533,h=1):c>65535&&(c-=65536,r+=String.fromCharCode(c>>>10&1023|55296),c=56320|1023&c),r+=String.fromCharCode(c),n+=h}return r}(this.buf,e,t)},readBytes:function(){var t=this.readVarint()+this.pos,e=this.buf.subarray(this.pos,t);return this.pos=t,e},readPackedVarint:function(t,e){if(this.type!==pl.Bytes)return t.push(this.readVarint(e));var i=_l(this);for(t=t||[];this.pos127;);else if(e===pl.Bytes)this.pos=this.readVarint()+this.pos;else if(e===pl.Fixed32)this.pos+=4;else{if(e!==pl.Fixed64)throw new Error("Unimplemented type: "+e);this.pos+=8}},writeTag:function(t,e){this.writeVarint(t<<3|e)},realloc:function(t){for(var e=this.length||16;e268435455||t<0?function(t,e){var i,r;if(t>=0?(i=t%4294967296|0,r=t/4294967296|0):(r=~(-t/4294967296),4294967295^(i=~(-t%4294967296))?i=i+1|0:(i=0,r=r+1|0)),t>=0x10000000000000000||t<-0x10000000000000000)throw new Error("Given varint doesn't fit into 10 bytes");e.realloc(10),function(t,e,i){i.buf[i.pos++]=127&t|128,t>>>=7,i.buf[i.pos++]=127&t|128,t>>>=7,i.buf[i.pos++]=127&t|128,t>>>=7,i.buf[i.pos++]=127&t|128,i.buf[i.pos]=127&(t>>>=7)}(i,0,e),function(t,e){var i=(7&t)<<4;e.buf[e.pos++]|=i|((t>>>=3)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t)))))}(r,e)}(t,this):(this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127))))},writeSVarint:function(t){this.writeVarint(t<0?2*-t-1:2*t)},writeBoolean:function(t){this.writeVarint(Boolean(t))},writeString:function(t){t=String(t),this.realloc(4*t.length),this.pos++;var e=this.pos;this.pos=function(t,e,i){for(var r,n,s=0;s55295&&r<57344){if(!n){r>56319||s+1===e.length?(t[i++]=239,t[i++]=191,t[i++]=189):n=r;continue}if(r<56320){t[i++]=239,t[i++]=191,t[i++]=189,n=r;continue}r=n-55296<<10|r-56320|65536,n=null}else n&&(t[i++]=239,t[i++]=191,t[i++]=189,n=null);r<128?t[i++]=r:(r<2048?t[i++]=r>>6|192:(r<65536?t[i++]=r>>12|224:(t[i++]=r>>18|240,t[i++]=r>>12&63|128),t[i++]=r>>6&63|128),t[i++]=63&r|128)}return i}(this.buf,t,this.pos);var i=this.pos-e;i>=128&&xl(e,i,this),this.pos=e-1,this.writeVarint(i),this.pos+=i},writeFloat:function(t){this.realloc(4),ul(this.buf,t,this.pos,!0,23,4),this.pos+=4},writeDouble:function(t){this.realloc(8),ul(this.buf,t,this.pos,!0,52,8),this.pos+=8},writeBytes:function(t){var e=t.length;this.writeVarint(e),this.realloc(e);for(var i=0;i=128&&xl(i,r,this),this.pos=i-1,this.writeVarint(r),this.pos+=r},writeMessage:function(t,e,i){this.writeTag(t,pl.Bytes),this.writeRawMessage(e,i)},writePackedVarint:function(t,e){e.length&&this.writeMessage(t,vl,e)},writePackedSVarint:function(t,e){e.length&&this.writeMessage(t,bl,e)},writePackedBoolean:function(t,e){e.length&&this.writeMessage(t,El,e)},writePackedFloat:function(t,e){e.length&&this.writeMessage(t,wl,e)},writePackedDouble:function(t,e){e.length&&this.writeMessage(t,Tl,e)},writePackedFixed32:function(t,e){e.length&&this.writeMessage(t,Sl,e)},writePackedSFixed32:function(t,e){e.length&&this.writeMessage(t,Il,e)},writePackedFixed64:function(t,e){e.length&&this.writeMessage(t,Al,e)},writePackedSFixed64:function(t,e){e.length&&this.writeMessage(t,zl,e)},writeBytesField:function(t,e){this.writeTag(t,pl.Bytes),this.writeBytes(e)},writeFixed32Field:function(t,e){this.writeTag(t,pl.Fixed32),this.writeFixed32(e)},writeSFixed32Field:function(t,e){this.writeTag(t,pl.Fixed32),this.writeSFixed32(e)},writeFixed64Field:function(t,e){this.writeTag(t,pl.Fixed64),this.writeFixed64(e)},writeSFixed64Field:function(t,e){this.writeTag(t,pl.Fixed64),this.writeSFixed64(e)},writeVarintField:function(t,e){this.writeTag(t,pl.Varint),this.writeVarint(e)},writeSVarintField:function(t,e){this.writeTag(t,pl.Varint),this.writeSVarint(e)},writeStringField:function(t,e){this.writeTag(t,pl.Bytes),this.writeString(e)},writeFloatField:function(t,e){this.writeTag(t,pl.Fixed32),this.writeFloat(e)},writeDoubleField:function(t,e){this.writeTag(t,pl.Fixed64),this.writeDouble(e)},writeBooleanField:function(t,e){this.writeVarintField(t,Boolean(e))}};class Rl{constructor(t,{pixelRatio:e,version:i,stretchX:r,stretchY:n,content:s}){this.paddedRect=t,this.pixelRatio=e,this.stretchX=r,this.stretchY=n,this.content=s,this.version=i}get tl(){return[this.paddedRect.x+1,this.paddedRect.y+1]}get br(){return[this.paddedRect.x+this.paddedRect.w-1,this.paddedRect.y+this.paddedRect.h-1]}get tlbr(){return this.tl.concat(this.br)}get displaySize(){return[(this.paddedRect.w-2)/this.pixelRatio,(this.paddedRect.h-2)/this.pixelRatio]}}class Fl{constructor(t,e){const i={},r={};this.haveRenderCallbacks=[];const n=[];this.addImages(t,i,n),this.addImages(e,r,n);const{w:s,h:o}=Bl(n),a=new Mo({width:s||1,height:o||1});for(const e in t){const r=t[e],n=i[e].paddedRect;Mo.copy(r.data,a,{x:0,y:0},{x:n.x+1,y:n.y+1},r.data)}for(const t in e){const i=e[t],n=r[t].paddedRect,s=n.x+1,o=n.y+1,l=i.data.width,c=i.data.height;Mo.copy(i.data,a,{x:0,y:0},{x:s,y:o},i.data),Mo.copy(i.data,a,{x:0,y:c-1},{x:s,y:o-1},{width:l,height:1}),Mo.copy(i.data,a,{x:0,y:0},{x:s,y:o+c},{width:l,height:1}),Mo.copy(i.data,a,{x:l-1,y:0},{x:s-1,y:o},{width:1,height:c}),Mo.copy(i.data,a,{x:0,y:0},{x:s+l,y:o},{width:1,height:c})}this.image=a,this.iconPositions=i,this.patternPositions=r}addImages(t,e,i){for(const r in t){const n=t[r],s={x:0,y:0,w:n.data.width+2,h:n.data.height+2};i.push(s),e[r]=new Rl(s,n),n.hasRenderCallback&&this.haveRenderCallbacks.push(r)}}patchUpdatedImages(t,e){t.dispatchRenderCallbacks(this.haveRenderCallbacks);for(const i in t.updatedImages)this.patchUpdatedImage(this.iconPositions[i],t.getImage(i),e),this.patchUpdatedImage(this.patternPositions[i],t.getImage(i),e)}patchUpdatedImage(t,e,i){if(!t||!e)return;if(t.version===e.version)return;t.version=e.version;const[r,n]=t.tl;i.update(e.data,void 0,{x:r,y:n})}}Ir("ImagePosition",Rl),Ir("ImageAtlas",Fl),t.WritingMode=void 0,(dl=t.WritingMode||(t.WritingMode={}))[dl.none=0]="none",dl[dl.horizontal=1]="horizontal",dl[dl.vertical=2]="vertical",dl[dl.horizontalOnly=3]="horizontalOnly";const Ol=-17;class Ul{constructor(){this.scale=1,this.fontStack="",this.imageName=null}static forText(t,e){const i=new Ul;return i.scale=t||1,i.fontStack=e,i}static forImage(t){const e=new Ul;return e.imageName=t,e}}class Vl{constructor(){this.text="",this.sectionIndex=[],this.sections=[],this.imageSectionID=null}static fromFeature(t,e){const i=new Vl;for(let r=0;r=0&&i>=t&&Gl[this.text.charCodeAt(i)];i--)e--;this.text=this.text.substring(t,e),this.sectionIndex=this.sectionIndex.slice(t,e)}substring(t,e){const i=new Vl;return i.text=this.text.substring(t,e),i.sectionIndex=this.sectionIndex.slice(t,e),i.sections=this.sections,i}toString(){return this.text}getMaxScale(){return this.sectionIndex.reduce(((t,e)=>Math.max(t,this.sections[e].scale)),0)}addTextSection(t,e){this.text+=t.text,this.sections.push(Ul.forText(t.scale,t.fontStack||e));const i=this.sections.length-1;for(let e=0;e=63743?null:++this.imageSectionID:(this.imageSectionID=57344,this.imageSectionID)}}function Nl(e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g){const _=Vl.fromFeature(e,s);let y;p===t.WritingMode.vertical&&_.verticalizePunctuation();const{processBidirectionalText:x,processStyledBidirectionalText:v}=Jr;if(x&&1===_.sections.length){y=[];const t=x(_.toString(),Hl(_,h,o,i,n,m,f));for(const e of t){const t=new Vl;t.text=e,t.sections=_.sections;for(let i=0;i0&&r>w&&(w=r)}else{const t=r[f.fontStack],e=t&&t[_];if(e&&e.rect)T=e.rect,v=e.metrics;else{const t=i[f.fontStack],e=t&&t[_];if(!e)continue;v=e.metrics}y=(s-f.scale)*ll}I?(e.verticalizable=!0,b.push({glyph:_,imageName:E,x:d,y:m+y,vertical:I,scale:f.scale,fontStack:f.fontStack,sectionIndex:g,metrics:v,rect:T}),d+=S*f.scale+h):(b.push({glyph:_,imageName:E,x:d,y:m+y,vertical:I,scale:f.scale,fontStack:f.fontStack,sectionIndex:g,metrics:v,rect:T}),d+=v.advance*f.scale+h)}0!==b.length&&(f=Math.max(d-h,f),Jl(b,0,b.length-1,_,w)),d=0;const T=o*s+w;v.lineOffset=Math.max(w,l),m+=T,g=Math.max(T,g),++y}var x;const v=m-Ol,{horizontalAlign:b,verticalAlign:w}=Kl(a);(function(t,e,i,r,n,s,o,a,l){const c=(e-i)*n;let h=0;h=s!==o?-a*r-Ol:(-r*l+.5)*o;for(const e of t)for(const t of e.positionedGlyphs)t.x+=c,t.y+=h})(e.positionedLines,_,b,w,f,g,o,v,s.length),e.top+=-w*v,e.bottom=e.top+v,e.left+=-b*f,e.right=e.left+f}(w,i,r,n,y,a,l,c,p,h,d,g),!function(t){for(const e of t)if(0!==e.positionedGlyphs.length)return!1;return!0}(b)&&w}const Gl={9:!0,10:!0,11:!0,12:!0,13:!0,32:!0},$l={10:!0,32:!0,38:!0,40:!0,41:!0,43:!0,45:!0,47:!0,173:!0,183:!0,8203:!0,8208:!0,8211:!0,8231:!0};function ql(t,e,i,r,n,s){if(e.imageName){const t=r[e.imageName];return t?t.displaySize[0]*e.scale*ll/s+n:0}{const r=i[e.fontStack],s=r&&r[t];return s?s.metrics.advance*e.scale+n:0}}function jl(t,e,i,r){const n=Math.pow(t-e,2);return r?t=0;let h=0;for(let i=0;i-i/2;){if(o--,o<0)return!1;a-=t[o].dist(s),s=t[o]}a+=t[o].dist(t[o+1]),o++;const l=[];let c=0;for(;ar;)c-=l.shift().angleDelta;if(c>n)return!1;o++,a+=e.dist(i)}return!0}function nc(t){let e=0;for(let i=0;ic){const h=(c-l)/s,u=Se(r.x,n.x,h),p=Se(r.y,n.y,h),d=new ic(u,p,n.angleTo(r),i);return d._round(),!o||rc(t,d,a,o,e)?d:void 0}l+=s}}function lc(t,e,i,r,n,s,o,a,l){const c=sc(r,s,o),h=oc(r,n),u=h*o,p=0===t[0].x||t[0].x===l||0===t[0].y||t[0].y===l;return e-u=0&&_=0&&y=0&&p+c<=h){const i=new ic(_,y,f,e);i._round(),r&&!rc(t,i,s,r,n)||d.push(i)}}u+=m}return a||d.length||o||(d=cc(t,u/2,i,r,n,s,o,!0,l)),d}function hc(t,e,i,r,n){const s=[];for(let o=0;o=r&&c.x>=r||(o.x>=r?o=new S(r,o.y+(r-o.x)/(c.x-o.x)*(c.y-o.y))._round():c.x>=r&&(c=new S(r,o.y+(r-o.x)/(c.x-o.x)*(c.y-o.y))._round()),o.y>=n&&c.y>=n||(o.y>=n?o=new S(o.x+(n-o.y)/(c.y-o.y)*(c.x-o.x),n)._round():c.y>=n&&(c=new S(o.x+(n-o.y)/(c.y-o.y)*(c.x-o.x),n)._round()),l&&o.equals(l[l.length-1])||(l=[o],s.push(l)),l.push(c)))))}}return s}function uc(t,e,i,r){const n=[],s=t.image,o=s.pixelRatio,a=s.paddedRect.w-2,l=s.paddedRect.h-2,c=t.right-t.left,h=t.bottom-t.top,u=s.stretchX||[[0,a]],p=s.stretchY||[[0,l]],d=(t,e)=>t+e[1]-e[0],m=u.reduce(d,0),f=p.reduce(d,0),g=a-m,_=l-f;let y=0,x=m,v=0,b=f,w=0,T=g,E=0,I=_;if(s.content&&r){const t=s.content;y=pc(u,0,t[0]),v=pc(p,0,t[1]),x=pc(u,t[0],t[2]),b=pc(p,t[1],t[3]),w=t[0]-y,E=t[1]-v,T=t[2]-t[0]-x,I=t[3]-t[1]-b}const A=(r,n,a,l)=>{const u=mc(r.stretch-y,x,c,t.left),p=fc(r.fixed-w,T,r.stretch,m),d=mc(n.stretch-v,b,h,t.top),g=fc(n.fixed-E,I,n.stretch,f),_=mc(a.stretch-y,x,c,t.left),A=fc(a.fixed-w,T,a.stretch,m),z=mc(l.stretch-v,b,h,t.top),C=fc(l.fixed-E,I,l.stretch,f),M=new S(u,d),k=new S(_,d),P=new S(_,z),D=new S(u,z),L=new S(p/o,g/o),B=new S(A/o,C/o),R=e*Math.PI/180;if(R){const t=Math.sin(R),e=Math.cos(R),i=[e,-t,t,e];M._matMult(i),k._matMult(i),D._matMult(i),P._matMult(i)}const F=r.stretch+r.fixed,O=n.stretch+n.fixed;return{tl:M,tr:k,bl:D,br:P,tex:{x:s.paddedRect.x+1+F,y:s.paddedRect.y+1+O,w:a.stretch+a.fixed-F,h:l.stretch+l.fixed-O},writingMode:void 0,glyphOffset:[0,0],sectionIndex:0,pixelOffsetTL:L,pixelOffsetBR:B,minFontScaleX:T/o/c,minFontScaleY:I/o/h,isSDF:i}};if(r&&(s.stretchX||s.stretchY)){const t=dc(u,g,m),e=dc(p,_,f);for(let i=0;i0&&(r=Math.max(10,r),this.circleDiameter=r)}else{let l=s.top*o-a[0],h=s.bottom*o+a[2],u=s.left*o-a[3],p=s.right*o+a[1];const d=s.collisionPadding;if(d&&(u-=d[0]*o,l-=d[1]*o,p+=d[2]*o,h+=d[3]*o),c){const t=new S(u,l),e=new S(p,l),i=new S(u,h),r=new S(p,h),n=c*Math.PI/180;t._rotate(n),e._rotate(n),i._rotate(n),r._rotate(n),u=Math.min(t.x,e.x,i.x,r.x),p=Math.max(t.x,e.x,i.x,r.x),l=Math.min(t.y,e.y,i.y,r.y),h=Math.max(t.y,e.y,i.y,r.y)}t.emplaceBack(e.x,e.y,u,l,p,h,i,r,n)}this.boxEndIndex=t.length}}class _c{constructor(t=[],e=yc){if(this.data=t,this.length=this.data.length,this.compare=e,this.length>0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t)}push(t){this.data.push(t),this.length++,this._up(this.length-1)}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return this.length--,this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:i}=this,r=e[t];for(;t>0;){const n=t-1>>1,s=e[n];if(i(r,s)>=0)break;e[t]=s,t=n}e[t]=r}_down(t){const{data:e,compare:i}=this,r=this.length>>1,n=e[t];for(;t=0)break;e[t]=s,t=r}e[t]=n}}function yc(t,e){return te?1:0}function xc(t,e=1,i=!1){let r=1/0,n=1/0,s=-1/0,o=-1/0;const a=t[0];for(let t=0;ts)&&(s=e.x),(!t||e.y>o)&&(o=e.y)}const l=Math.min(s-r,o-n);let c=l/2;const h=new _c([],vc);if(0===l)return new S(r,n);for(let e=r;eu.d||!u.d)&&(u=r,i&&console.log("found best %d after %d probes",Math.round(1e4*r.d)/1e4,p)),r.max-u.d<=e||(c=r.h/2,h.push(new bc(r.p.x-c,r.p.y-c,c,t)),h.push(new bc(r.p.x+c,r.p.y-c,c,t)),h.push(new bc(r.p.x-c,r.p.y+c,c,t)),h.push(new bc(r.p.x+c,r.p.y+c,c,t)),p+=4)}return i&&(console.log(`num probes: ${p}`),console.log(`best distance: ${u.d}`)),u.p}function vc(t,e){return e.max-t.max}function bc(t,e,i,r){this.p=new S(t,e),this.h=i,this.d=function(t,e){let i=!1,r=1/0;for(let n=0;nt.y!=a.y>t.y&&t.x<(a.x-n.x)*(t.y-n.y)/(a.y-n.y)+n.x&&(i=!i),r=Math.min(r,io(t,n,a))}}return(i?1:-1)*Math.sqrt(r)}(this.p,r),this.max=this.d+this.h*Math.SQRT2}const wc=Number.POSITIVE_INFINITY;function Tc(t,e){return e[1]!==wc?function(t,e,i){let r=0,n=0;switch(e=Math.abs(e),i=Math.abs(i),t){case"top-right":case"top-left":case"top":n=i-7;break;case"bottom-right":case"bottom-left":case"bottom":n=7-i}switch(t){case"top-right":case"bottom-right":case"right":r=-e;break;case"top-left":case"bottom-left":case"left":r=e}return[r,n]}(t,e[0],e[1]):function(t,e){let i=0,r=0;e<0&&(e=0);const n=e/Math.sqrt(2);switch(t){case"top-right":case"top-left":r=n-7;break;case"bottom-right":case"bottom-left":r=7-n;break;case"bottom":r=7-e;break;case"top":r=e-7}switch(t){case"top-right":case"bottom-right":i=-n;break;case"top-left":case"bottom-left":i=n;break;case"left":i=e;break;case"right":i=-e}return[i,r]}(t,e[0])}function Ec(t){switch(t){case"right":case"top-right":case"bottom-right":return"right";case"left":case"top-left":case"bottom-left":return"left"}return"center"}function Sc(e,i,r,n,s,o,a,l,c,h,u){let p=o.textMaxSize.evaluate(i,{});void 0===p&&(p=a);const d=e.layers[0].layout,f=d.get("icon-offset").evaluate(i,{},u),g=zc(r.horizontal),_=a/24,y=e.tilePixelRatio*_,x=e.tilePixelRatio*p/24,v=e.tilePixelRatio*l,b=e.tilePixelRatio*d.get("symbol-spacing"),w=d.get("text-padding")*e.tilePixelRatio,T=function(t,e,i,r=1){const n=t.get("icon-padding").evaluate(e,{},i),s=n&&n.values;return[s[0]*r,s[1]*r,s[2]*r,s[3]*r]}(d,i,u,e.tilePixelRatio),E=d.get("text-max-angle")/180*Math.PI,S="viewport"!==d.get("text-rotation-alignment")&&"point"!==d.get("symbol-placement"),I="map"===d.get("icon-rotation-alignment")&&"point"!==d.get("symbol-placement"),A=d.get("symbol-placement"),z=b/2,C=d.get("icon-text-fit");let M;n&&"none"!==C&&(e.allowVerticalPlacement&&r.vertical&&(M=Ql(n,r.vertical,C,d.get("icon-text-fit-padding"),f,_)),g&&(n=Ql(n,g,C,d.get("icon-text-fit-padding"),f,_)));const k=(l,p)=>{p.x<0||p.x>=Gs||p.y<0||p.y>=Gs||function(e,i,r,n,s,o,a,l,c,h,u,p,d,f,g,_,y,x,v,b,w,T,E,S,I){const A=e.addToLineVertexArray(i,r);let z,C,M,k,P=0,D=0,L=0,B=0,R=-1,F=-1;const O={};let U=ys.exports(""),V=0,N=0;if(void 0===l._unevaluatedLayout.getValue("text-radial-offset")?[V,N]=l.layout.get("text-offset").evaluate(w,{},S).map((t=>t*ll)):(V=l.layout.get("text-radial-offset").evaluate(w,{},S)*ll,N=wc),e.allowVerticalPlacement&&n.vertical){const t=l.layout.get("text-rotate").evaluate(w,{},S)+90;M=new gc(c,i,h,u,p,n.vertical,d,f,g,t),a&&(k=new gc(c,i,h,u,p,a,y,x,g,t))}if(s){const r=l.layout.get("icon-rotate").evaluate(w,{}),n="none"!==l.layout.get("icon-text-fit"),o=uc(s,r,E,n),d=a?uc(a,r,E,n):void 0;C=new gc(c,i,h,u,p,s,y,x,!1,r),P=4*o.length;const f=e.iconSizeData;let g=null;"source"===f.kind?(g=[tc*l.layout.get("icon-size").evaluate(w,{})],g[0]>Ic&&m(`${e.layerIds[0]}: Value for "icon-size" is >= 255. Reduce your "icon-size".`)):"composite"===f.kind&&(g=[tc*T.compositeIconSizes[0].evaluate(w,{},S),tc*T.compositeIconSizes[1].evaluate(w,{},S)],(g[0]>Ic||g[1]>Ic)&&m(`${e.layerIds[0]}: Value for "icon-size" is >= 255. Reduce your "icon-size".`)),e.addSymbols(e.icon,o,g,b,v,w,t.WritingMode.none,i,A.lineStartIndex,A.lineLength,-1,S),R=e.icon.placedSymbolArray.length-1,d&&(D=4*d.length,e.addSymbols(e.icon,d,g,b,v,w,t.WritingMode.vertical,i,A.lineStartIndex,A.lineLength,-1,S),F=e.icon.placedSymbolArray.length-1)}const G=Object.keys(n.horizontal);for(const r of G){const s=n.horizontal[r];if(!z){U=ys.exports(s.text);const t=l.layout.get("text-rotate").evaluate(w,{},S);z=new gc(c,i,h,u,p,s,d,f,g,t)}const a=1===s.positionedLines.length;if(L+=Ac(e,i,s,o,l,g,w,_,A,n.vertical?t.WritingMode.horizontal:t.WritingMode.horizontalOnly,a?G:[r],O,R,T,S),a)break}n.vertical&&(B+=Ac(e,i,n.vertical,o,l,g,w,_,A,t.WritingMode.vertical,["vertical"],O,F,T,S));const $=z?z.boxStartIndex:e.collisionBoxArray.length,q=z?z.boxEndIndex:e.collisionBoxArray.length,j=M?M.boxStartIndex:e.collisionBoxArray.length,Z=M?M.boxEndIndex:e.collisionBoxArray.length,X=C?C.boxStartIndex:e.collisionBoxArray.length,W=C?C.boxEndIndex:e.collisionBoxArray.length,H=k?k.boxStartIndex:e.collisionBoxArray.length,K=k?k.boxEndIndex:e.collisionBoxArray.length;let J=-1;const Y=(t,e)=>t&&t.circleDiameter?Math.max(t.circleDiameter,e):e;J=Y(z,J),J=Y(M,J),J=Y(C,J),J=Y(k,J);const Q=J>-1?1:0;Q&&(J*=I/ll),e.glyphOffsetArray.length>=Fc.MAX_GLYPHS&&m("Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907"),void 0!==w.sortKey&&e.addToSortKeyRanges(e.symbolInstances.length,w.sortKey),e.symbolInstances.emplaceBack(i.x,i.y,O.right>=0?O.right:-1,O.center>=0?O.center:-1,O.left>=0?O.left:-1,O.vertical||-1,R,F,U,$,q,j,Z,X,W,H,K,h,L,B,P,D,Q,0,d,V,N,J)}(e,p,l,r,n,s,M,e.layers[0],e.collisionBoxArray,i.index,i.sourceLayerIndex,e.index,y,[w,w,w,w],S,c,v,T,I,f,i,o,h,u,a)};if("line"===A)for(const t of hc(i.geometry,0,0,Gs,Gs)){const i=lc(t,b,E,r.vertical||g,n,24,x,e.overscaling,Gs);for(const r of i)g&&Cc(e,g.text,z,r)||k(t,r)}else if("line-center"===A){for(const t of i.geometry)if(t.length>1){const e=ac(t,E,r.vertical||g,n,24,x);e&&k(t,e)}}else if("Polygon"===i.type)for(const t of pa(i.geometry,0)){const e=xc(t,16);k(t[0],new ic(e.x,e.y,0))}else if("LineString"===i.type)for(const t of i.geometry)k(t,new ic(t[0].x,t[0].y,0));else if("Point"===i.type)for(const t of i.geometry)for(const e of t)k([e],new ic(e.x,e.y,0))}const Ic=32640;function Ac(t,e,i,r,n,s,o,a,l,c,h,u,p,d,f){const g=function(t,e,i,r,n,s,o,a){const l=r.layout.get("text-rotate").evaluate(s,{})*Math.PI/180,c=[];for(const t of e.positionedLines)for(const r of t.positionedGlyphs){if(!r.rect)continue;const s=r.rect||{};let h=4,u=!0,p=1,d=0;const m=(n||a)&&r.vertical,f=r.metrics.advance*r.scale/2;if(a&&e.verticalizable){const e=(r.scale-1)*ll,i=(ll-r.metrics.width*r.scale)/2;d=t.lineOffset/2-(r.imageName?-i:e)}if(r.imageName){const t=o[r.imageName];u=t.sdf,p=t.pixelRatio,h=1/p}const g=n?[r.x+f,r.y]:[0,0];let _=n?[0,0]:[r.x+f+i[0],r.y+i[1]-d],y=[0,0];m&&(y=_,_=[0,0]);const x=(r.metrics.left-h)*r.scale-f+_[0],v=(-r.metrics.top-h)*r.scale+_[1],b=x+s.w*r.scale/p,w=v+s.h*r.scale/p,T=new S(x,v),E=new S(b,v),I=new S(x,w),A=new S(b,w);if(m){const t=new S(-f,f-Ol),e=-Math.PI/2,i=12-f,n=new S(22-i,-(r.imageName?i:0)),s=new S(...y);T._rotateAround(e,t)._add(n)._add(s),E._rotateAround(e,t)._add(n)._add(s),I._rotateAround(e,t)._add(n)._add(s),A._rotateAround(e,t)._add(n)._add(s)}if(l){const t=Math.sin(l),e=Math.cos(l),i=[e,-t,t,e];T._matMult(i),E._matMult(i),I._matMult(i),A._matMult(i)}const z=new S(0,0),C=new S(0,0);c.push({tl:T,tr:E,bl:I,br:A,tex:s,writingMode:e.writingMode,glyphOffset:g,sectionIndex:r.sectionIndex,isSDF:u,pixelOffsetTL:z,pixelOffsetBR:C,minFontScaleX:0,minFontScaleY:0})}return c}(0,i,a,n,s,o,r,t.allowVerticalPlacement),_=t.textSizeData;let y=null;"source"===_.kind?(y=[tc*n.layout.get("text-size").evaluate(o,{})],y[0]>Ic&&m(`${t.layerIds[0]}: Value for "text-size" is >= 255. Reduce your "text-size".`)):"composite"===_.kind&&(y=[tc*d.compositeTextSizes[0].evaluate(o,{},f),tc*d.compositeTextSizes[1].evaluate(o,{},f)],(y[0]>Ic||y[1]>Ic)&&m(`${t.layerIds[0]}: Value for "text-size" is >= 255. Reduce your "text-size".`)),t.addSymbols(t.text,g,y,a,s,o,c,e,l.lineStartIndex,l.lineLength,p,f);for(const e of h)u[e]=t.text.placedSymbolArray.length-1;return 4*g.length}function zc(t){for(const e in t)return t[e];return null}function Cc(t,e,i,r){const n=t.compareText;if(e in n){const t=n[e];for(let e=t.length-1;e>=0;e--)if(r.dist(t[e])t.id)),this.index=e.index,this.pixelRatio=e.pixelRatio,this.sourceLayerIndex=e.sourceLayerIndex,this.hasPattern=!1,this.hasRTLText=!1,this.sortKeyRanges=[],this.collisionCircleArray=[],this.placementInvProjMatrix=fo([]),this.placementViewportMatrix=fo([]);const i=this.layers[0]._unevaluatedLayout._values;this.textSizeData=ec(this.zoom,i["text-size"]),this.iconSizeData=ec(this.zoom,i["icon-size"]);const r=this.layers[0].layout,n=r.get("symbol-sort-key"),s=r.get("symbol-z-order");this.canOverlap="never"!==Gc(r,"text-overlap","text-allow-overlap")||"never"!==Gc(r,"icon-overlap","icon-allow-overlap")||r.get("text-ignore-placement")||r.get("icon-ignore-placement"),this.sortFeaturesByKey="viewport-y"!==s&&!n.isConstant(),this.sortFeaturesByY=("viewport-y"===s||"auto"===s&&!this.sortFeaturesByKey)&&this.canOverlap,"point"===r.get("symbol-placement")&&(this.writingModes=r.get("text-writing-mode").map((e=>t.WritingMode[e]))),this.stateDependentLayerIds=this.layers.filter((t=>t.isStateDependent())).map((t=>t.id)),this.sourceID=e.sourceID}createArrays(){this.text=new Bc(new Us(this.layers,this.zoom,(t=>/^text/.test(t)))),this.icon=new Bc(new Us(this.layers,this.zoom,(t=>/^icon/.test(t)))),this.glyphOffsetArray=new Hn,this.lineVertexArray=new Kn,this.symbolInstances=new Wn}calculateGlyphDependencies(t,e,i,r,n){for(let s=0;s0)&&("constant"!==o.value.kind||o.value.value.length>0),h="constant"!==l.value.kind||!!l.value.value||Object.keys(l.parameters).length>0,u=s.get("symbol-sort-key");if(this.features=[],!c&&!h)return;const p=i.iconDependencies,d=i.glyphDependencies,m=i.availableImages,f=new Yr(this.zoom);for(const{feature:i,id:a,index:l,sourceLayerIndex:g}of e){const e=n._featureFilter.needGeometry,_=Zs(i,e);if(!n._featureFilter.filter(f,_,r))continue;let y,x;if(e||(_.geometry=js(i)),c){const t=n.getValueAndResolveTokens("text-field",_,r,m),e=Ft.factory(t);Lc(e)&&(this.hasRTLText=!0),(!this.hasRTLText||"unavailable"===Hr()||this.hasRTLText&&Jr.isParsed())&&(y=ol(e,n,_))}if(h){const t=n.getValueAndResolveTokens("icon-image",_,r,m);x=t instanceof Ut?t:Ut.fromString(t)}if(!y&&!x)continue;const v=this.sortFeaturesByKey?u.evaluate(_,{},r):void 0;if(this.features.push({id:a,text:y,icon:x,index:l,sourceLayerIndex:g,geometry:_.geometry,properties:i.properties,type:Mc[i.type],sortKey:v}),x&&(p[x.name]=!0),y){const e=o.evaluate(_,{},r).join(","),i="viewport"!==s.get("text-rotation-alignment")&&"point"!==s.get("symbol-placement");this.allowVerticalPlacement=this.writingModes&&this.writingModes.indexOf(t.WritingMode.vertical)>=0;for(const t of y.sections)if(t.image)p[t.image.name]=!0;else{const r=Pr(y.toString()),n=t.fontStack||e,s=d[n]=d[n]||{};this.calculateGlyphDependencies(t.text,s,i,this.allowVerticalPlacement,r)}}}"line"===s.get("symbol-placement")&&(this.features=function(t){const e={},i={},r=[];let n=0;function s(e){r.push(t[e]),n++}function o(t,e,n){const s=i[t];return delete i[t],i[e]=s,r[s].geometry[0].pop(),r[s].geometry[0]=r[s].geometry[0].concat(n[0]),s}function a(t,i,n){const s=e[i];return delete e[i],e[t]=s,r[s].geometry[0].shift(),r[s].geometry[0]=n[0].concat(r[s].geometry[0]),s}function l(t,e,i){const r=i?e[0][e[0].length-1]:e[0][0];return`${t}:${r.x}:${r.y}`}for(let c=0;ct.geometry))}(this.features)),this.sortFeaturesByKey&&this.features.sort(((t,e)=>t.sortKey-e.sortKey))}update(t,e,i){this.stateDependentLayers.length&&(this.text.programConfigurations.updatePaintArrays(t,e,this.layers,i),this.icon.programConfigurations.updatePaintArrays(t,e,this.layers,i))}isEmpty(){return 0===this.symbolInstances.length&&!this.hasRTLText}uploadPending(){return!this.uploaded||this.text.programConfigurations.needsUpload||this.icon.programConfigurations.needsUpload}upload(t){!this.uploaded&&this.hasDebugData()&&(this.textCollisionBox.upload(t),this.iconCollisionBox.upload(t)),this.text.upload(t,this.sortFeaturesByY,!this.uploaded,this.text.programConfigurations.needsUpload),this.icon.upload(t,this.sortFeaturesByY,!this.uploaded,this.icon.programConfigurations.needsUpload),this.uploaded=!0}destroyDebugData(){this.textCollisionBox.destroy(),this.iconCollisionBox.destroy()}destroy(){this.text.destroy(),this.icon.destroy(),this.hasDebugData()&&this.destroyDebugData()}addToLineVertexArray(t,e){const i=this.lineVertexArray.length;if(void 0!==t.segment){let i=t.dist(e[t.segment+1]),r=t.dist(e[t.segment]);const n={};for(let r=t.segment+1;r=0;i--)n[i]={x:e[i].x,y:e[i].y,tileUnitDistanceFromAnchor:r},i>0&&(r+=e[i-1].dist(e[i]));for(let t=0;t0}hasIconData(){return this.icon.segments.get().length>0}hasDebugData(){return this.textCollisionBox&&this.iconCollisionBox}hasTextCollisionBoxData(){return this.hasDebugData()&&this.textCollisionBox.segments.get().length>0}hasIconCollisionBoxData(){return this.hasDebugData()&&this.iconCollisionBox.segments.get().length>0}addIndicesForPlacedSymbol(t,e){const i=t.placedSymbolArray.get(e),r=i.vertexStartIndex+4*i.numGlyphs;for(let e=i.vertexStartIndex;er[t]-r[e]||n[e]-n[t])),s}addToSortKeyRanges(t,e){const i=this.sortKeyRanges[this.sortKeyRanges.length-1];i&&i.sortKey===e?i.symbolInstanceEnd=t+1:this.sortKeyRanges.push({sortKey:e,symbolInstanceStart:t,symbolInstanceEnd:t+1})}sortFeatures(t){if(this.sortFeaturesByY&&this.sortedAngle!==t&&!(this.text.segments.get().length>1||this.icon.segments.get().length>1)){this.symbolInstanceIndexes=this.getSortedSymbolIndexes(t),this.sortedAngle=t,this.text.indexArray.clear(),this.icon.indexArray.clear(),this.featureSortOrder=[];for(const t of this.symbolInstanceIndexes){const e=this.symbolInstances.get(t);this.featureSortOrder.push(e.featureIndex),[e.rightJustifiedTextSymbolIndex,e.centerJustifiedTextSymbolIndex,e.leftJustifiedTextSymbolIndex].forEach(((t,e,i)=>{t>=0&&i.indexOf(t)===e&&this.addIndicesForPlacedSymbol(this.text,t)})),e.verticalPlacedTextSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.text,e.verticalPlacedTextSymbolIndex),e.placedIconSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.icon,e.placedIconSymbolIndex),e.verticalPlacedIconSymbolIndex>=0&&this.addIndicesForPlacedSymbol(this.icon,e.verticalPlacedIconSymbolIndex)}this.text.indexBuffer&&this.text.indexBuffer.updateData(this.text.indexArray),this.icon.indexBuffer&&this.icon.indexBuffer.updateData(this.icon.indexArray)}}}Ir("SymbolBucket",Fc,{omit:["layers","collisionBoxArray","features","compareText"]}),Fc.MAX_GLYPHS=65535,Fc.addDynamicAttributes=Dc;const Oc=new dn({"symbol-placement":new ln(rt.layout_symbol["symbol-placement"]),"symbol-spacing":new ln(rt.layout_symbol["symbol-spacing"]),"symbol-avoid-edges":new ln(rt.layout_symbol["symbol-avoid-edges"]),"symbol-sort-key":new cn(rt.layout_symbol["symbol-sort-key"]),"symbol-z-order":new ln(rt.layout_symbol["symbol-z-order"]),"icon-allow-overlap":new ln(rt.layout_symbol["icon-allow-overlap"]),"icon-overlap":new ln(rt.layout_symbol["icon-overlap"]),"icon-ignore-placement":new ln(rt.layout_symbol["icon-ignore-placement"]),"icon-optional":new ln(rt.layout_symbol["icon-optional"]),"icon-rotation-alignment":new ln(rt.layout_symbol["icon-rotation-alignment"]),"icon-size":new cn(rt.layout_symbol["icon-size"]),"icon-text-fit":new ln(rt.layout_symbol["icon-text-fit"]),"icon-text-fit-padding":new ln(rt.layout_symbol["icon-text-fit-padding"]),"icon-image":new cn(rt.layout_symbol["icon-image"]),"icon-rotate":new cn(rt.layout_symbol["icon-rotate"]),"icon-padding":new cn(rt.layout_symbol["icon-padding"]),"icon-keep-upright":new ln(rt.layout_symbol["icon-keep-upright"]),"icon-offset":new cn(rt.layout_symbol["icon-offset"]),"icon-anchor":new cn(rt.layout_symbol["icon-anchor"]),"icon-pitch-alignment":new ln(rt.layout_symbol["icon-pitch-alignment"]),"text-pitch-alignment":new ln(rt.layout_symbol["text-pitch-alignment"]),"text-rotation-alignment":new ln(rt.layout_symbol["text-rotation-alignment"]),"text-field":new cn(rt.layout_symbol["text-field"]),"text-font":new cn(rt.layout_symbol["text-font"]),"text-size":new cn(rt.layout_symbol["text-size"]),"text-max-width":new cn(rt.layout_symbol["text-max-width"]),"text-line-height":new ln(rt.layout_symbol["text-line-height"]),"text-letter-spacing":new cn(rt.layout_symbol["text-letter-spacing"]),"text-justify":new cn(rt.layout_symbol["text-justify"]),"text-radial-offset":new cn(rt.layout_symbol["text-radial-offset"]),"text-variable-anchor":new ln(rt.layout_symbol["text-variable-anchor"]),"text-anchor":new cn(rt.layout_symbol["text-anchor"]),"text-max-angle":new ln(rt.layout_symbol["text-max-angle"]),"text-writing-mode":new ln(rt.layout_symbol["text-writing-mode"]),"text-rotate":new cn(rt.layout_symbol["text-rotate"]),"text-padding":new ln(rt.layout_symbol["text-padding"]),"text-keep-upright":new ln(rt.layout_symbol["text-keep-upright"]),"text-transform":new cn(rt.layout_symbol["text-transform"]),"text-offset":new cn(rt.layout_symbol["text-offset"]),"text-allow-overlap":new ln(rt.layout_symbol["text-allow-overlap"]),"text-overlap":new ln(rt.layout_symbol["text-overlap"]),"text-ignore-placement":new ln(rt.layout_symbol["text-ignore-placement"]),"text-optional":new ln(rt.layout_symbol["text-optional"])});var Uc={paint:new dn({"icon-opacity":new cn(rt.paint_symbol["icon-opacity"]),"icon-color":new cn(rt.paint_symbol["icon-color"]),"icon-halo-color":new cn(rt.paint_symbol["icon-halo-color"]),"icon-halo-width":new cn(rt.paint_symbol["icon-halo-width"]),"icon-halo-blur":new cn(rt.paint_symbol["icon-halo-blur"]),"icon-translate":new ln(rt.paint_symbol["icon-translate"]),"icon-translate-anchor":new ln(rt.paint_symbol["icon-translate-anchor"]),"text-opacity":new cn(rt.paint_symbol["text-opacity"]),"text-color":new cn(rt.paint_symbol["text-color"],{runtimeType:ft,getOverride:t=>t.textColor,hasOverride:t=>!!t.textColor}),"text-halo-color":new cn(rt.paint_symbol["text-halo-color"]),"text-halo-width":new cn(rt.paint_symbol["text-halo-width"]),"text-halo-blur":new cn(rt.paint_symbol["text-halo-blur"]),"text-translate":new ln(rt.paint_symbol["text-translate"]),"text-translate-anchor":new ln(rt.paint_symbol["text-translate-anchor"])}),layout:Oc};class Vc{constructor(t){this.type=t.property.overrides?t.property.overrides.runtimeType:ut,this.defaultValue=t}evaluate(t){if(t.formattedSection){const e=this.defaultValue.property.overrides;if(e&&e.hasOverride(t.formattedSection))return e.getOverride(t.formattedSection)}return t.feature&&t.featureState?this.defaultValue.evaluate(t.feature,t.featureState):this.defaultValue.property.specification.default}eachChild(t){this.defaultValue.isConstant()||t(this.defaultValue.value._styleExpression.expression)}outputDefined(){return!1}serialize(){return null}}Ir("FormatSectionOverride",Vc,{omit:["defaultValue"]});class Nc extends fn{constructor(t){super(t,Uc)}recalculate(t,e){if(super.recalculate(t,e),"auto"===this.layout.get("icon-rotation-alignment")&&(this.layout._values["icon-rotation-alignment"]="point"!==this.layout.get("symbol-placement")?"map":"viewport"),"auto"===this.layout.get("text-rotation-alignment")&&(this.layout._values["text-rotation-alignment"]="point"!==this.layout.get("symbol-placement")?"map":"viewport"),"auto"===this.layout.get("text-pitch-alignment")&&(this.layout._values["text-pitch-alignment"]="map"===this.layout.get("text-rotation-alignment")?"map":"viewport"),"auto"===this.layout.get("icon-pitch-alignment")&&(this.layout._values["icon-pitch-alignment"]=this.layout.get("icon-rotation-alignment")),"point"===this.layout.get("symbol-placement")){const t=this.layout.get("text-writing-mode");if(t){const e=[];for(const i of t)e.indexOf(i)<0&&e.push(i);this.layout._values["text-writing-mode"]=e}else this.layout._values["text-writing-mode"]=["horizontal"]}this._setPaintOverrides()}getValueAndResolveTokens(t,e,i,r){const n=this.layout.get(t).evaluate(e,{},i,r),s=this._unevaluatedLayout._values[t];return s.isDataDriven()||Di(s.value)||!n?n:function(t,e){return e.replace(/{([^{}]+)}/g,((e,i)=>i in t?String(t[i]):""))}(e.properties,n)}createBucket(t){return new Fc(t)}queryRadius(){return 0}queryIntersectsFeature(){return!1}_setPaintOverrides(){for(const t of Uc.paint.overridableProperties){if(!Nc.hasPaintOverride(this.layout,t))continue;const e=this.paint.get(t),i=new Vc(e),r=new Pi(i,e.property.specification);let n=null;n="constant"===e.value.kind||"source"===e.value.kind?new Bi("source",r):new Ri("composite",r,e.value.zoomStops,e.value._interpolationType),this.paint._values[t]=new on(e.property,n,e.parameters)}}_handleOverridablePaintPropertyUpdate(t,e,i){return!(!this.layout||e.isDataDriven()||i.isDataDriven())&&Nc.hasPaintOverride(this.layout,t)}static hasPaintOverride(t,e){const i=t.get("text-field"),r=Uc.paint.properties[e];let n=!1;const s=t=>{for(const e of t)if(r.overrides&&r.overrides.hasOverride(e))return void(n=!0)};if("constant"===i.value.kind&&i.value.value instanceof Ft)s(i.value.value.sections);else if("source"===i.value.kind){const t=e=>{n||(e instanceof qt&&Gt(e.value)===xt?s(e.value.sections):e instanceof hi?s(e.sections):e.eachChild(t))},e=i.value;e._styleExpression&&t(e._styleExpression.expression)}return n}}function Gc(t,e,i){let r="never";const n=t.get(e);return n?r=n:t.get(i)&&(r="always"),r}var $c={paint:new dn({"background-color":new ln(rt.paint_background["background-color"]),"background-pattern":new un(rt.paint_background["background-pattern"]),"background-opacity":new ln(rt.paint_background["background-opacity"])})},qc={paint:new dn({"raster-opacity":new ln(rt.paint_raster["raster-opacity"]),"raster-hue-rotate":new ln(rt.paint_raster["raster-hue-rotate"]),"raster-brightness-min":new ln(rt.paint_raster["raster-brightness-min"]),"raster-brightness-max":new ln(rt.paint_raster["raster-brightness-max"]),"raster-saturation":new ln(rt.paint_raster["raster-saturation"]),"raster-contrast":new ln(rt.paint_raster["raster-contrast"]),"raster-resampling":new ln(rt.paint_raster["raster-resampling"]),"raster-fade-duration":new ln(rt.paint_raster["raster-fade-duration"])})};class jc extends fn{constructor(t){super(t,{}),this.onAdd=t=>{this.implementation.onAdd&&this.implementation.onAdd(t,t.painter.context.gl)},this.onRemove=t=>{this.implementation.onRemove&&this.implementation.onRemove(t,t.painter.context.gl)},this.implementation=t}is3D(){return"3d"===this.implementation.renderingMode}hasOffscreenPass(){return void 0!==this.implementation.prerender}recalculate(){}updateTransitions(){}hasTransition(){return!1}serialize(){}}const Zc={circle:class extends fn{constructor(t){super(t,ho)}createBucket(t){return new Ws(t)}queryRadius(t){const e=t;return oo("circle-radius",this,e)+oo("circle-stroke-width",this,e)+ao(this.paint.get("circle-translate"))}queryIntersectsFeature(t,e,i,r,n,s,o,a){const l=lo(t,this.paint.get("circle-translate"),this.paint.get("circle-translate-anchor"),s.angle,o),c=this.paint.get("circle-radius").evaluate(e,i)+this.paint.get("circle-stroke-width").evaluate(e,i),h="map"===this.paint.get("circle-pitch-alignment"),u=h?l:function(t,e){return t.map((t=>To(t,e)))}(l,a),p=h?c*o:c;for(const t of r)for(const e of t){const t=h?e:To(e,a);let i=p;const r=bo([],[e.x,e.y,0,1],a);if("viewport"===this.paint.get("circle-pitch-scale")&&"map"===this.paint.get("circle-pitch-alignment")?i*=r[3]/s.cameraToCenterDistance:"map"===this.paint.get("circle-pitch-scale")&&"viewport"===this.paint.get("circle-pitch-alignment")&&(i*=s.cameraToCenterDistance/r[3]),Ks(u,t,i))return!0}return!1}},heatmap:class extends fn{constructor(t){super(t,So),this._updateColorRamp()}createBucket(t){return new Eo(t)}_handleSpecialPaintPropertyUpdate(t){"heatmap-color"===t&&this._updateColorRamp()}_updateColorRamp(){this.colorRamp=ko({expression:this._transitionablePaint._values["heatmap-color"].value.expression,evaluationKey:"heatmapDensity",image:this.colorRamp}),this.colorRampTexture=null}resize(){this.heatmapFbo&&(this.heatmapFbo.destroy(),this.heatmapFbo=null)}queryRadius(){return 0}queryIntersectsFeature(){return!1}hasOffscreenPass(){return 0!==this.paint.get("heatmap-opacity")&&"none"!==this.visibility}},hillshade:class extends fn{constructor(t){super(t,Po)}hasOffscreenPass(){return 0!==this.paint.get("hillshade-exaggeration")&&"none"!==this.visibility}},fill:class extends fn{constructor(t){super(t,ya)}recalculate(t,e){super.recalculate(t,e);const i=this.paint._values["fill-outline-color"];"constant"===i.value.kind&&void 0===i.value.value&&(this.paint._values["fill-outline-color"]=this.paint._values["fill-color"])}createBucket(t){return new ga(t)}queryRadius(){return ao(this.paint.get("fill-translate"))}queryIntersectsFeature(t,e,i,r,n,s,o){return Js(lo(t,this.paint.get("fill-translate"),this.paint.get("fill-translate-anchor"),s.angle,o),r)}isTileClipped(){return!0}},"fill-extrusion":class extends fn{constructor(t){super(t,Va)}createBucket(t){return new Fa(t)}queryRadius(){return ao(this.paint.get("fill-extrusion-translate"))}is3D(){return!0}queryIntersectsFeature(t,e,i,r,n,s,o,a){const l=lo(t,this.paint.get("fill-extrusion-translate"),this.paint.get("fill-extrusion-translate-anchor"),s.angle,o),c=this.paint.get("fill-extrusion-height").evaluate(e,i),h=this.paint.get("fill-extrusion-base").evaluate(e,i),u=function(t,e,i,r){const n=[];for(const i of t){const t=[i.x,i.y,0,1];bo(t,t,e),n.push(new S(t[0]/t[3],t[1]/t[3]))}return n}(l,a),p=function(t,e,i,r){const n=[],s=[],o=r[8]*e,a=r[9]*e,l=r[10]*e,c=r[11]*e,h=r[8]*i,u=r[9]*i,p=r[10]*i,d=r[11]*i;for(const e of t){const t=[],i=[];for(const n of e){const e=n.x,s=n.y,m=r[0]*e+r[4]*s+r[12],f=r[1]*e+r[5]*s+r[13],g=r[2]*e+r[6]*s+r[14],_=r[3]*e+r[7]*s+r[15],y=g+l,x=_+c,v=m+h,b=f+u,w=g+p,T=_+d,E=new S((m+o)/x,(f+a)/x);E.z=y/x,t.push(E);const I=new S(v/T,b/T);I.z=w/T,i.push(I)}n.push(t),s.push(i)}return[n,s]}(r,h,c,a);return function(t,e,i){let r=1/0;Js(i,e)&&(r=Ga(i,e[0]));for(let n=0;n=3)for(let e=0;e{this._triggered=!1,this._callback()})}trigger(){this._triggered||(this._triggered=!0,this._channel?this._channel.port1.postMessage(!0):setTimeout((()=>{this._triggered=!1,this._callback()}),0))}remove(){delete this._channel,this._callback=()=>{}}}const Wc=6371008.8;class Hc{constructor(t,e){if(isNaN(t)||isNaN(e))throw new Error(`Invalid LngLat object: (${t}, ${e})`);if(this.lng=+t,this.lat=+e,this.lat>90||this.lat<-90)throw new Error("Invalid LngLat latitude value: must be between -90 and 90")}wrap(){return new Hc(o(this.lng,-180,180),this.lat)}toArray(){return[this.lng,this.lat]}toString(){return`LngLat(${this.lng}, ${this.lat})`}distanceTo(t){const e=Math.PI/180,i=this.lat*e,r=t.lat*e,n=Math.sin(i)*Math.sin(r)+Math.cos(i)*Math.cos(r)*Math.cos((t.lng-this.lng)*e);return Wc*Math.acos(Math.min(n,1))}toBounds(t=0){const e=360*t/40075017,i=e/Math.cos(Math.PI/180*this.lat);return new Kc(new Hc(this.lng-i,this.lat-e),new Hc(this.lng+i,this.lat+e))}static convert(t){if(t instanceof Hc)return t;if(Array.isArray(t)&&(2===t.length||3===t.length))return new Hc(Number(t[0]),Number(t[1]));if(!Array.isArray(t)&&"object"==typeof t&&null!==t)return new Hc(Number("lng"in t?t.lng:t.lon),Number(t.lat));throw new Error("`LngLatLike` argument must be specified as a LngLat instance, an object {lng: , lat: }, an object {lon: , lat: }, or an array of [, ]")}}class Kc{constructor(t,e){t&&(e?this.setSouthWest(t).setNorthEast(e):4===t.length?this.setSouthWest([t[0],t[1]]).setNorthEast([t[2],t[3]]):this.setSouthWest(t[0]).setNorthEast(t[1]))}setNorthEast(t){return this._ne=t instanceof Hc?new Hc(t.lng,t.lat):Hc.convert(t),this}setSouthWest(t){return this._sw=t instanceof Hc?new Hc(t.lng,t.lat):Hc.convert(t),this}extend(t){const e=this._sw,i=this._ne;let r,n;if(t instanceof Hc)r=t,n=t;else{if(!(t instanceof Kc))return Array.isArray(t)?4===t.length||t.every(Array.isArray)?this.extend(Kc.convert(t)):this.extend(Hc.convert(t)):this;if(r=t._sw,n=t._ne,!r||!n)return this}return e||i?(e.lng=Math.min(r.lng,e.lng),e.lat=Math.min(r.lat,e.lat),i.lng=Math.max(n.lng,i.lng),i.lat=Math.max(n.lat,i.lat)):(this._sw=new Hc(r.lng,r.lat),this._ne=new Hc(n.lng,n.lat)),this}getCenter(){return new Hc((this._sw.lng+this._ne.lng)/2,(this._sw.lat+this._ne.lat)/2)}getSouthWest(){return this._sw}getNorthEast(){return this._ne}getNorthWest(){return new Hc(this.getWest(),this.getNorth())}getSouthEast(){return new Hc(this.getEast(),this.getSouth())}getWest(){return this._sw.lng}getSouth(){return this._sw.lat}getEast(){return this._ne.lng}getNorth(){return this._ne.lat}toArray(){return[this._sw.toArray(),this._ne.toArray()]}toString(){return`LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`}isEmpty(){return!(this._sw&&this._ne)}contains(t){const{lng:e,lat:i}=Hc.convert(t);let r=this._sw.lng<=e&&e<=this._ne.lng;return this._sw.lng>this._ne.lng&&(r=this._sw.lng>=e&&e>=this._ne.lng),this._sw.lat<=i&&i<=this._ne.lat&&r}static convert(t){return t instanceof Kc?t:t?new Kc(t):t}}const Jc=2*Math.PI*Wc;function Yc(t){return Jc*Math.cos(t*Math.PI/180)}function Qc(t){return(180+t)/360}function th(t){return(180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t*Math.PI/360)))/360}function eh(t,e){return t/Yc(e)}function ih(t){return 360/Math.PI*Math.atan(Math.exp((180-360*t)*Math.PI/180))-90}class rh{constructor(t,e,i=0){this.x=+t,this.y=+e,this.z=+i}static fromLngLat(t,e=0){const i=Hc.convert(t);return new rh(Qc(i.lng),th(i.lat),eh(e,i.lat))}toLngLat(){return new Hc(360*this.x-180,ih(this.y))}toAltitude(){return this.z*Yc(ih(this.y))}meterInMercatorCoordinateUnits(){return 1/Jc*(t=ih(this.y),1/Math.cos(t*Math.PI/180));var t}}function nh(t,e,i){var r=2*Math.PI*6378137/256/Math.pow(2,i);return[t*r-2*Math.PI*6378137/2,e*r-2*Math.PI*6378137/2]}class sh{constructor(t,e,i){this.z=t,this.x=e,this.y=i,this.key=lh(0,t,t,e,i)}equals(t){return this.z===t.z&&this.x===t.x&&this.y===t.y}url(t,e,i){const r=(s=this.y,o=this.z,a=nh(256*(n=this.x),256*(s=Math.pow(2,o)-s-1),o),l=nh(256*(n+1),256*(s+1),o),a[0]+","+a[1]+","+l[0]+","+l[1]);var n,s,o,a,l;const c=function(t,e,i){let r,n="";for(let s=t;s>0;s--)r=1<1?"@2x":"").replace(/{quadkey}/g,c).replace(/{bbox-epsg-3857}/g,r)}isChildOf(t){const e=this.z-t.z;return e>0&&t.x===this.x>>e&&t.y===this.y>>e}getTilePoint(t){const e=Math.pow(2,this.z);return new S((t.x*e-this.x)*Gs,(t.y*e-this.y)*Gs)}toString(){return`${this.z}/${this.x}/${this.y}`}}class oh{constructor(t,e){this.wrap=t,this.canonical=e,this.key=lh(t,e.z,e.z,e.x,e.y)}}class ah{constructor(t,e,i,r,n){this.overscaledZ=t,this.wrap=e,this.canonical=new sh(i,+r,+n),this.key=lh(e,t,i,r,n)}clone(){return new ah(this.overscaledZ,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)}equals(t){return this.overscaledZ===t.overscaledZ&&this.wrap===t.wrap&&this.canonical.equals(t.canonical)}scaledTo(t){const e=this.canonical.z-t;return t>this.canonical.z?new ah(t,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y):new ah(t,this.wrap,t,this.canonical.x>>e,this.canonical.y>>e)}calculateScaledKey(t,e){const i=this.canonical.z-t;return t>this.canonical.z?lh(this.wrap*+e,t,this.canonical.z,this.canonical.x,this.canonical.y):lh(this.wrap*+e,t,t,this.canonical.x>>i,this.canonical.y>>i)}isChildOf(t){if(t.wrap!==this.wrap)return!1;const e=this.canonical.z-t.canonical.z;return 0===t.overscaledZ||t.overscaledZ>e&&t.canonical.y===this.canonical.y>>e}children(t){if(this.overscaledZ>=t)return[new ah(this.overscaledZ+1,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)];const e=this.canonical.z+1,i=2*this.canonical.x,r=2*this.canonical.y;return[new ah(e,this.wrap,e,i,r),new ah(e,this.wrap,e,i+1,r),new ah(e,this.wrap,e,i,r+1),new ah(e,this.wrap,e,i+1,r+1)]}isLessThan(t){return this.wrapt.wrap)&&(this.overscaledZt.overscaledZ)&&(this.canonical.xt.canonical.x)&&this.canonical.ythis.max&&(this.max=i),i=this.dim+1||e<-1||e>=this.dim+1)throw new RangeError("out of range source coordinates for DEM data");return(e+1)*this.stride+(t+1)}_unpackMapbox(t,e,i){return(256*t*256+256*e+i)/10-1e4}_unpackTerrarium(t,e,i){return 256*t+e+i/256-32768}getPixels(){return new Mo({width:this.stride,height:this.stride},new Uint8Array(this.data.buffer))}backfillBorder(t,e,i){if(this.dim!==t.dim)throw new Error("dem dimension mismatch");let r=e*this.dim,n=e*this.dim+this.dim,s=i*this.dim,o=i*this.dim+this.dim;switch(e){case-1:r=n-1;break;case 1:n=r+1}switch(i){case-1:s=o-1;break;case 1:o=s+1}const a=-e*this.dim,l=-i*this.dim;for(let e=s;e=0&&r[3]>=0&&a.insert(o,r[0],r[1],r[2],r[3])}}loadVTLayers(){return this.vtLayers||(this.vtLayers=new wa.VectorTile(new cl(this.rawTileData)).layers,this.sourceLayerCoder=new hh(this.vtLayers?Object.keys(this.vtLayers).sort():["_geojsonTileLayer"])),this.vtLayers}query(t,e,i,r){this.loadVTLayers();const n=t.params||{},s=Gs/t.tileSize/t.scale,o=Wi(n.filter),a=t.queryGeometry,l=t.queryPadding*s,c=mh(a),h=this.grid.query(c.minX-l,c.minY-l,c.maxX+l,c.maxY+l),u=mh(t.cameraQueryGeometry),p=this.grid3D.query(u.minX-l,u.minY-l,u.maxX+l,u.maxY+l,((e,i,r,n)=>function(t,e,i,r,n){for(const s of t)if(e<=s.x&&i<=s.y&&r>=s.x&&n>=s.y)return!0;const s=[new S(e,i),new S(e,n),new S(r,n),new S(r,i)];if(t.length>2)for(const e of s)if(no(t,e))return!0;for(let e=0;e(p||(p=js(e)),i.queryIntersectsFeature(a,e,r,p,this.z,t.transform,s,t.pixelPosMatrix))))}return d}loadMatchingFeature(t,e,i,r,n,s,o,l,c,h,u){const p=this.bucketLayerIDs[e];if(s&&!function(t,e){for(let i=0;i=0)return!0;return!1}(s,p))return;const d=this.sourceLayerCoder.decode(i),m=this.vtLayers[d].feature(r);if(n.needGeometry){const t=Zs(m,!0);if(!n.filter(new Yr(this.tileID.overscaledZ),t,this.tileID.canonical))return}else if(!n.filter(new Yr(this.tileID.overscaledZ),m))return;const f=this.getId(m,d);for(let e=0;e{const o=e instanceof an?e.get(s):null;return o&&o.evaluate?o.evaluate(i,r,n):o}))}function mh(t){let e=1/0,i=1/0,r=-1/0,n=-1/0;for(const s of t)e=Math.min(e,s.x),i=Math.min(i,s.y),r=Math.max(r,s.x),n=Math.max(n,s.y);return{minX:e,minY:i,maxX:r,maxY:n}}function fh(t,e){return e-t}var gh;Ir("FeatureIndex",ph,{omit:["rawTileData","sourceLayerCoder"]}),t.PerformanceMarkers=void 0,(gh=t.PerformanceMarkers||(t.PerformanceMarkers={})).create="create",gh.load="load",gh.fullLoad="fullLoad";let _h=null,yh=[];const xh=1e3/30,vh={mark(t){performance.mark(t)},frame(t){const e=t;null!=_h&&yh.push(e-_h),_h=e},clearMetrics(){_h=null,yh=[],performance.clearMeasures("loadTime"),performance.clearMeasures("fullLoadTime");for(const e in t.PerformanceMarkers)performance.clearMarks(t.PerformanceMarkers[e])},getPerformanceMetrics(){performance.measure("loadTime",t.PerformanceMarkers.create,t.PerformanceMarkers.load),performance.measure("fullLoadTime",t.PerformanceMarkers.create,t.PerformanceMarkers.fullLoad);const e=performance.getEntriesByName("loadTime")[0].duration,i=performance.getEntriesByName("fullLoadTime")[0].duration,r=yh.length,n=1/(yh.reduce(((t,e)=>t+e),0)/r/1e3),s=yh.filter((t=>t>xh)).reduce(((t,e)=>t+(e-xh)/xh),0);return{loadTime:e,fullLoadTime:i,fps:n,percentDroppedFrames:s/(r+s)*100}}};t.AJAXError=G,t.ARRAY_TYPE=po,t.Actor=class{constructor(t,e,i){this.target=t,this.parent=e,this.mapId=i,this.callbacks={},this.tasks={},this.taskQueue=[],this.cancelCallbacks={},c(["receive","process"],this),this.invoker=new Xc(this.process),this.target.addEventListener("message",this.receive,!1),this.globalScope=_()?t:window}send(t,e,i,r,n=!1){const s=Math.round(1e18*Math.random()).toString(36).substring(0,10);i&&(this.callbacks[s]=i);const o=w(this.globalScope)?void 0:[];return this.target.postMessage({id:s,type:t,hasCallback:!!i,targetMapId:r,mustQueue:n,sourceMapId:this.mapId,data:zr(e,o)},o),{cancel:()=>{i&&delete this.callbacks[s],this.target.postMessage({id:s,type:"",targetMapId:r,sourceMapId:this.mapId})}}}receive(t){const e=t.data,i=e.id;if(i&&(!e.targetMapId||this.mapId===e.targetMapId))if(""===e.type){delete this.tasks[i];const t=this.cancelCallbacks[i];delete this.cancelCallbacks[i],t&&t()}else _()||e.mustQueue?(this.tasks[i]=e,this.taskQueue.push(i),this.invoker.trigger()):this.processTask(i,e)}process(){if(!this.taskQueue.length)return;const t=this.taskQueue.shift(),e=this.tasks[t];delete this.tasks[t],this.taskQueue.length&&this.invoker.trigger(),e&&this.processTask(t,e)}processTask(t,e){if(""===e.type){const i=this.callbacks[t];delete this.callbacks[t],i&&(e.error?i(Cr(e.error)):i(null,Cr(e.data)))}else{let i=!1;const r=w(this.globalScope)?void 0:[],n=e.hasCallback?(e,n)=>{i=!0,delete this.cancelCallbacks[t],this.target.postMessage({id:t,type:"",sourceMapId:this.mapId,error:e?zr(e):null,data:zr(n,r)},r)}:t=>{i=!0};let s=null;const o=Cr(e.data);if(this.parent[e.type])s=this.parent[e.type](e.sourceMapId,o,n);else if(this.parent.getWorkerSource){const t=e.type.split(".");s=this.parent.getWorkerSource(e.sourceMapId,t[0],o.source)[t[1]](o,n)}else n(new Error(`Could not find function ${e.type}`));!i&&s&&s.cancel&&(this.cancelCallbacks[t]=s.cancel)}}remove(){this.invoker.remove(),this.target.removeEventListener("message",this.receive,!1)}},t.AlphaImage=Co,t.CanonicalTileID=sh,t.CollisionBoxArray=qn,t.CollisionCircleLayoutArray=class extends Pn{},t.Color=Lt,t.DEMData=ch,t.DataConstantProperty=ln,t.DictionaryCoder=hh,t.EXTENT=Gs,t.ErrorEvent=et,t.EvaluationParameters=Yr,t.Event=tt,t.Evented=it,t.FeatureIndex=ph,t.FillBucket=ga,t.FillExtrusionBucket=Fa,t.GeoJSONFeature=uh,t.ImageAtlas=Fl,t.ImagePosition=Rl,t.LineBucket=Ka,t.LineStripIndexArray=class extends Nn{},t.LngLat=Hc,t.LngLatBounds=Kc,t.MercatorCoordinate=rh,t.ONE_EM=ll,t.OverscaledTileID=ah,t.PerformanceUtils=vh,t.PosArray=Qn,t.Properties=dn,t.QuadTriangleArray=class extends Ln{},t.RGBAImage=Mo,t.RasterBoundsArray=class extends wn{},t.RequestPerformance=class{constructor(t){this._marks={start:[t.url,"start"].join("#"),end:[t.url,"end"].join("#"),measure:t.url.toString()},performance.mark(this._marks.start)}finish(){performance.mark(this._marks.end);let t=performance.getEntriesByName(this._marks.measure);return 0===t.length&&(performance.measure(this._marks.measure,this._marks.start,this._marks.end),t=performance.getEntriesByName(this._marks.measure),performance.clearMarks(this._marks.start),performance.clearMarks(this._marks.end),performance.clearMeasures(this._marks.measure)),t}},t.ResourceType=N,t.SegmentVector=fs,t.SymbolBucket=Fc,t.Transitionable=en,t.TriangleIndexArray=us,t.Uniform1f=zs,t.Uniform1i=class extends As{constructor(t,e){super(t,e),this.current=0}set(t){this.current!==t&&(this.current=t,this.gl.uniform1i(this.location,t))}},t.Uniform2f=class extends As{constructor(t,e){super(t,e),this.current=[0,0]}set(t){t[0]===this.current[0]&&t[1]===this.current[1]||(this.current=t,this.gl.uniform2f(this.location,t[0],t[1]))}},t.Uniform3f=class extends As{constructor(t,e){super(t,e),this.current=[0,0,0]}set(t){t[0]===this.current[0]&&t[1]===this.current[1]&&t[2]===this.current[2]||(this.current=t,this.gl.uniform3f(this.location,t[0],t[1],t[2]))}},t.Uniform4f=Cs,t.UniformColor=Ms,t.UniformMatrix4f=class extends As{constructor(t,e){super(t,e),this.current=ks}set(t){if(t[12]!==this.current[12]||t[0]!==this.current[0])return this.current=t,void this.gl.uniformMatrix4fv(this.location,!1,t);for(let e=1;e<16;e++)if(t[e]!==this.current[e]){this.current=t,this.gl.uniformMatrix4fv(this.location,!1,t);break}}},t.UnwrappedTileID=oh,t.ValidationError=nt,t.ZoomHistory=Mr,t.add=function(t,e,i){return t[0]=e[0]+i[0],t[1]=e[1]+i[1],t[2]=e[2]+i[2],t},t.addDynamicAttributes=Dc,t.asyncAll=function(t,e,i){if(!t.length)return i(null,[]);let r=t.length;const n=new Array(t.length);let s=null;t.forEach(((t,o)=>{e(t,((t,e)=>{t&&(s=t),n[o]=e,0==--r&&i(s,n)}))}))},t.bezier=r,t.bindAll=c,t.cacheEntryPossiblyAdded=function(t){L++,L>P&&(t.getActor().send("enforceCacheSizeLimit",k),L=0)},t.clamp=s,t.clearTileCache=function(t){const e=caches.delete(z);t&&e.catch(t).then((()=>t()))},t.clipLine=hc,t.clone=function(t){var e=new po(16);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e[9]=t[9],e[10]=t[10],e[11]=t[11],e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},t.clone$1=p,t.clone$2=function(t){var e=new po(3);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e},t.collisionCircleLayout=sl,t.config=A,t.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},t.create=function(){var t=new po(16);return po!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0),t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},t.create$1=mo,t.createExpression=Li,t.createFilter=Wi,t.createLayout=xn,t.createStyleLayer=function(t){return"custom"===t.type?new jc(t):new Zc[t.type](t)},t.cross=function(t,e,i){var r=e[0],n=e[1],s=e[2],o=i[0],a=i[1],l=i[2];return t[0]=n*l-s*a,t[1]=s*o-r*l,t[2]=r*a-n*o,t},t.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},t.dot$1=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},t.ease=n,t.emitValidationErrors=Tr,t.enforceCacheSizeLimit=function(t){D(),C&&C.then((e=>{e.keys().then((i=>{for(let r=0;r{}}},t.identity=fo,t.invert=function(t,e){var i=e[0],r=e[1],n=e[2],s=e[3],o=e[4],a=e[5],l=e[6],c=e[7],h=e[8],u=e[9],p=e[10],d=e[11],m=e[12],f=e[13],g=e[14],_=e[15],y=i*a-r*o,x=i*l-n*o,v=i*c-s*o,b=r*l-n*a,w=r*c-s*a,T=n*c-s*l,E=h*f-u*m,S=h*g-p*m,I=h*_-d*m,A=u*g-p*f,z=u*_-d*f,C=p*_-d*g,M=y*C-x*z+v*A+b*I-w*S+T*E;return M?(t[0]=(a*C-l*z+c*A)*(M=1/M),t[1]=(n*z-r*C-s*A)*M,t[2]=(f*T-g*w+_*b)*M,t[3]=(p*w-u*T-d*b)*M,t[4]=(l*I-o*C-c*S)*M,t[5]=(i*C-n*I+s*S)*M,t[6]=(g*v-m*T-_*x)*M,t[7]=(h*T-p*v+d*x)*M,t[8]=(o*z-a*I+c*E)*M,t[9]=(r*I-i*z-s*E)*M,t[10]=(m*w-f*v+_*y)*M,t[11]=(u*v-h*w-d*y)*M,t[12]=(a*S-o*A-l*E)*M,t[13]=(i*A-r*S+n*E)*M,t[14]=(f*x-m*b-g*y)*M,t[15]=(h*b-u*x+p*y)*M,t):null},t.isImageBitmap=T,t.isSafari=w,t.isWorker=_,t.keysDifference=function(t,e){const i=[];for(const r in t)r in e||i.push(r);return i},t.lazyLoadRTLTextPlugin=function(){Jr.isLoading()||Jr.isLoaded()||"deferred"!==Hr()||Kr()},t.makeRequest=j,t.mapObject=h,t.mercatorXfromLng=Qc,t.mercatorYfromLat=th,t.mercatorZfromAltitude=eh,t.mul=yo,t.mul$1=function(t,e,i){return t[0]=e[0]*i[0],t[1]=e[1]*i[1],t[2]=e[2]*i[2],t[3]=e[3]*i[3],t},t.multiply=go,t.nextPowerOfTwo=function(t){return t<=1?1:Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))},t.normalize=function(t,e){var i=e[0],r=e[1],n=e[2],s=i*i+r*r+n*n;return s>0&&(s=1/Math.sqrt(s)),t[0]=e[0]*s,t[1]=e[1]*s,t[2]=e[2]*s,t},t.number=Se,t.ortho=function(t,e,i,r,n,s,o){var a=1/(e-i),l=1/(r-n),c=1/(s-o);return t[0]=-2*a,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*l,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(e+i)*a,t[13]=(n+r)*l,t[14]=(o+s)*c,t[15]=1,t},t.parseCacheControl=y,t.parseGlyphPbf=function(t){return new cl(t).readFields(Pl,[])},t.pbf=cl,t.performSymbolLayout=function(e){e.bucket.createArrays(),e.bucket.tilePixelRatio=Gs/(512*e.bucket.overscaling),e.bucket.compareText={},e.bucket.iconsNeedLinear=!1;const i=e.bucket.layers[0].layout,r=e.bucket.layers[0]._unevaluatedLayout._values,n={layoutIconSize:r["icon-size"].possiblyEvaluate(new Yr(e.bucket.zoom+1),e.canonical),layoutTextSize:r["text-size"].possiblyEvaluate(new Yr(e.bucket.zoom+1),e.canonical),textMaxSize:r["text-size"].possiblyEvaluate(new Yr(18))};if("composite"===e.bucket.textSizeData.kind){const{minZoom:t,maxZoom:i}=e.bucket.textSizeData;n.compositeTextSizes=[r["text-size"].possiblyEvaluate(new Yr(t),e.canonical),r["text-size"].possiblyEvaluate(new Yr(i),e.canonical)]}if("composite"===e.bucket.iconSizeData.kind){const{minZoom:t,maxZoom:i}=e.bucket.iconSizeData;n.compositeIconSizes=[r["icon-size"].possiblyEvaluate(new Yr(t),e.canonical),r["icon-size"].possiblyEvaluate(new Yr(i),e.canonical)]}const s=i.get("text-line-height")*ll,o="viewport"!==i.get("text-rotation-alignment")&&"point"!==i.get("symbol-placement"),a=i.get("text-keep-upright"),l=i.get("text-size");for(const r of e.bucket.features){const c=i.get("text-font").evaluate(r,{},e.canonical).join(","),h=l.evaluate(r,{},e.canonical),u=n.layoutTextSize.evaluate(r,{},e.canonical),p=n.layoutIconSize.evaluate(r,{},e.canonical),d={horizontal:{},vertical:void 0},f=r.text;let g,_=[0,0];if(f){const n=f.toString(),l=i.get("text-letter-spacing").evaluate(r,{},e.canonical)*ll,p=Dr(n)?l:0,m=i.get("text-anchor").evaluate(r,{},e.canonical),g=i.get("text-variable-anchor");if(!g){const t=i.get("text-radial-offset").evaluate(r,{},e.canonical);_=t?Tc(m,[t*ll,wc]):i.get("text-offset").evaluate(r,{},e.canonical).map((t=>t*ll))}let y=o?"center":i.get("text-justify").evaluate(r,{},e.canonical);const x=i.get("symbol-placement"),v="point"===x?i.get("text-max-width").evaluate(r,{},e.canonical)*ll:0,b=()=>{e.bucket.allowVerticalPlacement&&Pr(n)&&(d.vertical=Nl(f,e.glyphMap,e.glyphPositions,e.imagePositions,c,v,s,m,"left",p,_,t.WritingMode.vertical,!0,x,u,h))};if(!o&&g){const i="auto"===y?g.map((t=>Ec(t))):[y];let r=!1;for(let n=0;nthis._layers[t.id])),i=e[0];if("none"===i.visibility)continue;const r=i.source||"";let n=this.familiesBySource[r];n||(n=this.familiesBySource[r]={});const s=i.sourceLayer||"_geojsonTileLayer";let o=n[s];o||(o=n[s]=[]),o.push(e)}}}class n{constructor(e){const i={},r=[];for(const t in e){const n=e[t],s=i[t]={};for(const t in n){const e=n[+t];if(!e||0===e.bitmap.width||0===e.bitmap.height)continue;const i={x:0,y:0,w:e.bitmap.width+2,h:e.bitmap.height+2};r.push(i),s[t]={rect:i,metrics:e.metrics}}}const{w:n,h:s}=t.potpack(r),o=new t.AlphaImage({width:n||1,height:s||1});for(const r in e){const n=e[r];for(const e in n){const s=n[+e];if(!s||0===s.bitmap.width||0===s.bitmap.height)continue;const a=i[r][e].rect;t.AlphaImage.copy(s.bitmap,o,{x:0,y:0},{x:a.x+1,y:a.y+1},s.bitmap)}}this.image=o,this.positions=i}}t.register("GlyphAtlas",n);class s{constructor(e){this.tileID=new t.OverscaledTileID(e.tileID.overscaledZ,e.tileID.wrap,e.tileID.canonical.z,e.tileID.canonical.x,e.tileID.canonical.y),this.uid=e.uid,this.zoom=e.zoom,this.pixelRatio=e.pixelRatio,this.tileSize=e.tileSize,this.source=e.source,this.overscaling=this.tileID.overscaleFactor(),this.showCollisionBoxes=e.showCollisionBoxes,this.collectResourceTiming=!!e.collectResourceTiming,this.returnDependencies=!!e.returnDependencies,this.promoteId=e.promoteId}parse(e,i,r,s,a){this.status="parsing",this.data=e,this.collisionBoxArray=new t.CollisionBoxArray;const l=new t.DictionaryCoder(Object.keys(e.layers).sort()),c=new t.FeatureIndex(this.tileID,this.promoteId);c.bucketLayerIDs=[];const h={},u={featureIndex:c,iconDependencies:{},patternDependencies:{},glyphDependencies:{},availableImages:r},p=i.familiesBySource[this.source];for(const i in p){const n=e.layers[i];if(!n)continue;1===n.version&&t.warnOnce(`Vector tile source "${this.source}" layer "${i}" does not use vector tile spec v2 and therefore may have some rendering errors.`);const s=l.encode(i),a=[];for(let t=0;t=e.maxzoom||"none"!==e.visibility&&(o(t,this.zoom,r),(h[e.id]=e.createBucket({index:c.bucketLayerIDs.length,layers:t,zoom:this.zoom,pixelRatio:this.pixelRatio,overscaling:this.overscaling,collisionBoxArray:this.collisionBoxArray,sourceLayerIndex:s,sourceID:this.source})).populate(a,u,this.tileID.canonical),c.bucketLayerIDs.push(t.map((t=>t.id))))}}let d,m,f,g;const _=t.mapObject(u.glyphDependencies,(t=>Object.keys(t).map(Number)));Object.keys(_).length?s.send("getGlyphs",{uid:this.uid,stacks:_},((t,e)=>{d||(d=t,m=e,v.call(this))})):m={};const y=Object.keys(u.iconDependencies);y.length?s.send("getImages",{icons:y,source:this.source,tileID:this.tileID,type:"icons"},((t,e)=>{d||(d=t,f=e,v.call(this))})):f={};const x=Object.keys(u.patternDependencies);function v(){if(d)return a(d);if(m&&f&&g){const e=new n(m),i=new t.ImageAtlas(f,g);for(const n in h){const s=h[n];s instanceof t.SymbolBucket?(o(s.layers,this.zoom,r),t.performSymbolLayout({bucket:s,glyphMap:m,glyphPositions:e.positions,imageMap:f,imagePositions:i.iconPositions,showCollisionBoxes:this.showCollisionBoxes,canonical:this.tileID.canonical})):s.hasPattern&&(s instanceof t.LineBucket||s instanceof t.FillBucket||s instanceof t.FillExtrusionBucket)&&(o(s.layers,this.zoom,r),s.addFeatures(u,this.tileID.canonical,i.patternPositions))}this.status="done",a(null,{buckets:Object.values(h).filter((t=>!t.isEmpty())),featureIndex:c,collisionBoxArray:this.collisionBoxArray,glyphAtlasImage:e.image,imageAtlas:i,glyphMap:this.returnDependencies?m:null,iconMap:this.returnDependencies?f:null,glyphPositions:this.returnDependencies?e.positions:null})}}x.length?s.send("getImages",{icons:x,source:this.source,tileID:this.tileID,type:"patterns"},((t,e)=>{d||(d=t,g=e,v.call(this))})):g={},v.call(this)}}function o(e,i,r){const n=new t.EvaluationParameters(i);for(const t of e)t.recalculate(n,r)}function a(e,i){const r=t.getArrayBuffer(e.request,((e,r,n,s)=>{e?i(e):r&&i(null,{vectorTile:new t.vectorTile.VectorTile(new t.pbf(r)),rawData:r,cacheControl:n,expires:s})}));return()=>{r.cancel(),i()}}class l{constructor(t,e,i,r){this.actor=t,this.layerIndex=e,this.availableImages=i,this.loadVectorData=r||a,this.loading={},this.loaded={}}loadTile(e,i){const r=e.uid;this.loading||(this.loading={});const n=!!(e&&e.request&&e.request.collectResourceTiming)&&new t.RequestPerformance(e.request),o=this.loading[r]=new s(e);o.abort=this.loadVectorData(e,((e,s)=>{if(delete this.loading[r],e||!s)return o.status="done",this.loaded[r]=o,i(e);const a=s.rawData,l={};s.expires&&(l.expires=s.expires),s.cacheControl&&(l.cacheControl=s.cacheControl);const c={};if(n){const t=n.finish();t&&(c.resourceTiming=JSON.parse(JSON.stringify(t)))}o.vectorTile=s.vectorTile,o.parse(s.vectorTile,this.layerIndex,this.availableImages,this.actor,((e,r)=>{if(e||!r)return i(e);i(null,t.extend({rawTileData:a.slice(0)},r,l,c))})),this.loaded=this.loaded||{},this.loaded[r]=o}))}reloadTile(t,e){const i=this.loaded,r=t.uid,n=this;if(i&&i[r]){const s=i[r];s.showCollisionBoxes=t.showCollisionBoxes;const o=(t,i)=>{const r=s.reloadCallback;r&&(delete s.reloadCallback,s.parse(s.vectorTile,n.layerIndex,this.availableImages,n.actor,r)),e(t,i)};"parsing"===s.status?s.reloadCallback=o:"done"===s.status&&(s.vectorTile?s.parse(s.vectorTile,this.layerIndex,this.availableImages,this.actor,o):o())}}abortTile(t,e){const i=this.loading,r=t.uid;i&&i[r]&&i[r].abort&&(i[r].abort(),delete i[r]),e()}removeTile(t,e){const i=this.loaded,r=t.uid;i&&i[r]&&delete i[r],e()}}class c{constructor(){this.loaded={}}loadTile(e,i){const{uid:r,encoding:n,rawImageData:s}=e,o=t.isImageBitmap(s)?this.getImageData(s):s,a=new t.DEMData(r,o,n);this.loaded=this.loaded||{},this.loaded[r]=a,i(null,a)}getImageData(e){this.offscreenCanvas&&this.offscreenCanvasContext||(this.offscreenCanvas=new OffscreenCanvas(e.width,e.height),this.offscreenCanvasContext=this.offscreenCanvas.getContext("2d")),this.offscreenCanvas.width=e.width,this.offscreenCanvas.height=e.height,this.offscreenCanvasContext.drawImage(e,0,0,e.width,e.height);const i=this.offscreenCanvasContext.getImageData(-1,-1,e.width+2,e.height+2);return this.offscreenCanvasContext.clearRect(0,0,this.offscreenCanvas.width,this.offscreenCanvas.height),new t.RGBAImage({width:i.width,height:i.height},i.data)}removeTile(t){const e=this.loaded,i=t.uid;e&&e[i]&&delete e[i]}}var h=function t(e,i){var r,n=e&&e.type;if("FeatureCollection"===n)for(r=0;r=Math.abs(a)?i-l+a:a-l+i,i=l}i+r>=0!=!!e&&t.reverse()}const d=t.vectorTile.VectorTileFeature.prototype.toGeoJSON;class m{constructor(e){this._feature=e,this.extent=t.EXTENT,this.type=e.type,this.properties=e.tags,"id"in e&&!isNaN(e.id)&&(this.id=parseInt(e.id,10))}loadGeometry(){if(1===this._feature.type){const e=[];for(const i of this._feature.geometry)e.push([new t.pointGeometry(i[0],i[1])]);return e}{const e=[];for(const i of this._feature.geometry){const r=[];for(const e of i)r.push(new t.pointGeometry(e[0],e[1]));e.push(r)}return e}}toGeoJSON(t,e,i){return d.call(this,t,e,i)}}class f{constructor(e){this.layers={_geojsonTileLayer:this},this.name="_geojsonTileLayer",this.extent=t.EXTENT,this.length=e.length,this._features=e}feature(t){return new m(this._features[t])}}var g={exports:{}},_=t.pointGeometry,y=t.vectorTile.VectorTileFeature,x=v;function v(t,e){this.options=e||{},this.features=t,this.length=t.length}function b(t,e){this.id="number"==typeof t.id?t.id:void 0,this.type=t.type,this.rawGeometry=1===t.type?[t.geometry]:t.geometry,this.properties=t.tags,this.extent=e||4096}v.prototype.feature=function(t){return new b(this.features[t],this.options.extent)},b.prototype.loadGeometry=function(){var t=this.rawGeometry;this.geometry=[];for(var e=0;e>31}function M(t,e){for(var i=t.loadGeometry(),r=t.type,n=0,s=0,o=i.length,a=0;a>1;D(t,e,o,r,n,s%2),P(t,e,i,r,o-1,s+1),P(t,e,i,o+1,n,s+1)}function D(t,e,i,r,n,s){for(;n>r;){if(n-r>600){const o=n-r+1,a=i-r+1,l=Math.log(o),c=.5*Math.exp(2*l/3),h=.5*Math.sqrt(l*c*(o-c)/o)*(a-o/2<0?-1:1);D(t,e,i,Math.max(r,Math.floor(i-a*c/o+h)),Math.min(n,Math.floor(i+(o-a)*c/o+h)),s)}const o=e[2*i+s];let a=r,l=n;for(L(t,e,r,i),e[2*n+s]>o&&L(t,e,r,n);ao;)l--}e[2*r+s]===o?L(t,e,r,l):(l++,L(t,e,l,n)),l<=i&&(r=l+1),i<=l&&(n=l-1)}}function L(t,e,i,r){B(t,i,r),B(e,2*i,2*r),B(e,2*i+1,2*r+1)}function B(t,e,i){const r=t[e];t[e]=t[i],t[i]=r}function R(t,e,i,r){const n=t-i,s=e-r;return n*n+s*s}g.exports=E,g.exports.fromVectorTileJs=E,g.exports.fromGeojsonVt=function(t,e){e=e||{};var i={};for(var r in t)i[r]=new T(t[r].features,e),i[r].name=r,i[r].version=e.version,i[r].extent=e.extent;return E({layers:i})},g.exports.GeoJSONWrapper=T;const F=t=>t[0],O=t=>t[1];class U{constructor(t,e=F,i=O,r=64,n=Float64Array){this.nodeSize=r,this.points=t;const s=t.length<65536?Uint16Array:Uint32Array,o=this.ids=new s(t.length),a=this.coords=new n(2*t.length);for(let r=0;r=i&&c<=n&&h>=r&&h<=s&&l.push(t[o]);continue}const m=Math.floor((d+p)/2);c=e[2*m],h=e[2*m+1],c>=i&&c<=n&&h>=r&&h<=s&&l.push(t[m]);const f=(u+1)%2;(0===u?i<=c:r<=h)&&(a.push(d),a.push(m-1),a.push(f)),(0===u?n>=c:s>=h)&&(a.push(m+1),a.push(p),a.push(f))}return l}(this.ids,this.coords,t,e,i,r,this.nodeSize)}within(t,e,i){return function(t,e,i,r,n,s){const o=[0,t.length-1,0],a=[],l=n*n;for(;o.length;){const c=o.pop(),h=o.pop(),u=o.pop();if(h-u<=s){for(let n=u;n<=h;n++)R(e[2*n],e[2*n+1],i,r)<=l&&a.push(t[n]);continue}const p=Math.floor((u+h)/2),d=e[2*p],m=e[2*p+1];R(d,m,i,r)<=l&&a.push(t[p]);const f=(c+1)%2;(0===c?i-n<=d:r-n<=m)&&(o.push(u),o.push(p-1),o.push(f)),(0===c?i+n>=d:r+n>=m)&&(o.push(p+1),o.push(h),o.push(f))}return a}(this.ids,this.coords,t,e,i,this.nodeSize)}}const V={minZoom:0,maxZoom:16,minPoints:2,radius:40,extent:512,nodeSize:64,log:!1,generateId:!1,reduce:null,map:t=>t},N=Math.fround||(G=new Float32Array(1),t=>(G[0]=+t,G[0]));var G;class ${constructor(t){this.options=J(Object.create(V),t),this.trees=new Array(this.options.maxZoom+1)}load(t){const{log:e,minZoom:i,maxZoom:r,nodeSize:n}=this.options;e&&console.time("total time");const s=`prepare ${t.length} points`;e&&console.time(s),this.points=t;let o=[];for(let e=0;e=i;t--){const i=+Date.now();o=this._cluster(o,t),this.trees[t]=new U(o,Y,Q,n,Float32Array),e&&console.log("z%d: %d clusters in %dms",t,o.length,+Date.now()-i)}return e&&console.timeEnd("total time"),this}getClusters(t,e){let i=((t[0]+180)%360+360)%360-180;const r=Math.max(-90,Math.min(90,t[1]));let n=180===t[2]?180:((t[2]+180)%360+360)%360-180;const s=Math.max(-90,Math.min(90,t[3]));if(t[2]-t[0]>=360)i=-180,n=180;else if(i>n){const t=this.getClusters([i,r,180,s],e),o=this.getClusters([-180,r,n,s],e);return t.concat(o)}const o=this.trees[this._limitZoom(e)],a=o.range(W(i),H(s),W(n),H(r)),l=[];for(const t of a){const e=o.points[t];l.push(e.numPoints?Z(e):this.points[e.index])}return l}getChildren(t){const e=this._getOriginId(t),i=this._getOriginZoom(t),r="No cluster with the specified id.",n=this.trees[i];if(!n)throw new Error(r);const s=n.points[e];if(!s)throw new Error(r);const o=this.options.radius/(this.options.extent*Math.pow(2,i-1)),a=n.within(s.x,s.y,o),l=[];for(const e of a){const i=n.points[e];i.parentId===t&&l.push(i.numPoints?Z(i):this.points[i.index])}if(0===l.length)throw new Error(r);return l}getLeaves(t,e,i){const r=[];return this._appendLeaves(r,t,e=e||10,i=i||0,0),r}getTile(t,e,i){const r=this.trees[this._limitZoom(t)],n=Math.pow(2,t),{extent:s,radius:o}=this.options,a=o/s,l=(i-a)/n,c=(i+1+a)/n,h={features:[]};return this._addTileFeatures(r.range((e-a)/n,l,(e+1+a)/n,c),r.points,e,i,n,h),0===e&&this._addTileFeatures(r.range(1-a/n,l,1,c),r.points,n,i,n,h),e===n-1&&this._addTileFeatures(r.range(0,l,a/n,c),r.points,-1,i,n,h),h.features.length?h:null}getClusterExpansionZoom(t){let e=this._getOriginZoom(t)-1;for(;e<=this.options.maxZoom;){const i=this.getChildren(t);if(e++,1!==i.length)break;t=i[0].properties.cluster_id}return e}_appendLeaves(t,e,i,r,n){const s=this.getChildren(e);for(const e of s){const s=e.properties;if(s&&s.cluster?n+s.point_count<=r?n+=s.point_count:n=this._appendLeaves(t,s.cluster_id,i,r,n):ne&&(u+=i.numPoints||1)}if(u>h&&u>=o){let t=n.x*h,o=n.y*h,a=s&&h>1?this._map(n,!0):null;const p=(r<<5)+(e+1)+this.points.length;for(const i of c){const r=l.points[i];if(r.zoom<=e)continue;r.zoom=e;const c=r.numPoints||1;t+=r.x*c,o+=r.y*c,r.parentId=p,s&&(a||(a=this._map(n,!0)),s(a,this._map(r)))}n.parentId=p,i.push(q(t/u,o/u,p,u,a))}else if(i.push(n),u>1)for(const t of c){const r=l.points[t];r.zoom<=e||(r.zoom=e,i.push(r))}}return i}_getOriginId(t){return t-this.points.length>>5}_getOriginZoom(t){return(t-this.points.length)%32}_map(t,e){if(t.numPoints)return e?J({},t.properties):t.properties;const i=this.points[t.index].properties,r=this.options.map(i);return e&&r===i?J({},r):r}}function q(t,e,i,r,n){return{x:N(t),y:N(e),zoom:1/0,id:i,parentId:-1,numPoints:r,properties:n}}function j(t,e){const[i,r]=t.geometry.coordinates;return{x:N(W(i)),y:N(H(r)),zoom:1/0,index:e,parentId:-1}}function Z(t){return{type:"Feature",id:t.id,properties:X(t),geometry:{type:"Point",coordinates:[(e=t.x,360*(e-.5)),K(t.y)]}};var e}function X(t){const e=t.numPoints,i=e>=1e4?`${Math.round(e/1e3)}k`:e>=1e3?Math.round(e/100)/10+"k":e;return J(J({},t.properties),{cluster:!0,cluster_id:t.id,point_count:e,point_count_abbreviated:i})}function W(t){return t/360+.5}function H(t){const e=Math.sin(t*Math.PI/180),i=.5-.25*Math.log((1+e)/(1-e))/Math.PI;return i<0?0:i>1?1:i}function K(t){const e=(180-360*t)*Math.PI/180;return 360*Math.atan(Math.exp(e))/Math.PI-90}function J(t,e){for(const i in e)t[i]=e[i];return t}function Y(t){return t.x}function Q(t){return t.y}function tt(t,e,i,r){for(var n,s=r,o=i-e>>1,a=i-e,l=t[e],c=t[e+1],h=t[i],u=t[i+1],p=e+3;ps)n=p,s=d;else if(d===s){var m=Math.abs(p-o);mr&&(n-e>3&&tt(t,e,n,r),t[n+2]=s,i-n>3&&tt(t,n,i,r))}function et(t,e,i,r,n,s){var o=n-i,a=s-r;if(0!==o||0!==a){var l=((t-i)*o+(e-r)*a)/(o*o+a*a);l>1?(i=n,r=s):l>0&&(i+=o*l,r+=a*l)}return(o=t-i)*o+(a=e-r)*a}function it(t,e,i,r){var n={id:void 0===t?null:t,type:e,geometry:i,tags:r,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0};return function(t){var e=t.geometry,i=t.type;if("Point"===i||"MultiPoint"===i||"LineString"===i)rt(t,e);else if("Polygon"===i||"MultiLineString"===i)for(var r=0;r0&&(o+=r?(n*c-l*s)/2:Math.sqrt(Math.pow(l-n,2)+Math.pow(c-s,2))),n=l,s=c}var h=e.length-3;e[2]=1,tt(e,0,h,i),e[h+2]=1,e.size=Math.abs(o),e.start=0,e.end=e.size}function at(t,e,i,r){for(var n=0;n1?1:i}function ht(t,e,i,r,n,s,o,a){if(r/=e,s>=(i/=e)&&o=r)return null;for(var l=[],c=0;c=i&&m=r)){var f=[];if("Point"===p||"MultiPoint"===p)ut(u,f,i,r,n);else if("LineString"===p)pt(u,f,i,r,n,!1,a.lineMetrics);else if("MultiLineString"===p)mt(u,f,i,r,n,!1);else if("Polygon"===p)mt(u,f,i,r,n,!0);else if("MultiPolygon"===p)for(var g=0;g=i&&o<=r&&(e.push(t[s]),e.push(t[s+1]),e.push(t[s+2]))}}function pt(t,e,i,r,n,s,o){for(var a,l,c=dt(t),h=0===n?gt:_t,u=t.start,p=0;pi&&(l=h(c,d,m,g,_,i),o&&(c.start=u+a*l)):y>r?x=i&&(l=h(c,d,m,g,_,i),v=!0),x>r&&y<=r&&(l=h(c,d,m,g,_,r),v=!0),!s&&v&&(o&&(c.end=u+a*l),e.push(c),c=dt(t)),o&&(u+=a)}var b=t.length-3;d=t[b],m=t[b+1],f=t[b+2],(y=0===n?d:m)>=i&&y<=r&&ft(c,d,m,f),b=c.length-3,s&&b>=3&&(c[b]!==c[0]||c[b+1]!==c[1])&&ft(c,c[0],c[1],c[2]),c.length&&e.push(c)}function dt(t){var e=[];return e.size=t.size,e.start=t.start,e.end=t.end,e}function mt(t,e,i,r,n,s){for(var o=0;oo.maxX&&(o.maxX=h),u>o.maxY&&(o.maxY=u)}return o}function Tt(t,e,i,r){var n=e.geometry,s=e.type,o=[];if("Point"===s||"MultiPoint"===s)for(var a=0;a0&&e.size<(n?o:r))i.numPoints+=e.length/3;else{for(var a=[],l=0;lo)&&(i.numSimplified++,a.push(e[l]),a.push(e[l+1])),i.numPoints++;n&&function(t,e){for(var i=0,r=0,n=t.length,s=n-2;r0===e)for(r=0,n=t.length;r24)throw new Error("maxZoom should be in the 0-24 range");if(e.promoteId&&e.generateId)throw new Error("promoteId and generateId cannot be used together.");var r=function(t,e){var i=[];if("FeatureCollection"===t.type)for(var r=0;r1&&console.time("creation"),p=this.tiles[u]=wt(t,e,i,r,l),this.tileCoords.push({z:e,x:i,y:r}),c)){c>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",e,i,r,p.numFeatures,p.numPoints,p.numSimplified),console.timeEnd("creation"));var d="z"+e;this.stats[d]=(this.stats[d]||0)+1,this.total++}if(p.source=t,n){if(e===l.maxZoom||e===n)continue;var m=1<1&&console.time("clipping");var f,g,_,y,x,v,b=.5*l.buffer/l.extent,w=.5-b,T=.5+b,E=1+b;f=g=_=y=null,x=ht(t,h,i-b,i+T,0,p.minX,p.maxX,l),v=ht(t,h,i+w,i+E,0,p.minX,p.maxX,l),t=null,x&&(f=ht(x,h,r-b,r+T,1,p.minY,p.maxY,l),g=ht(x,h,r+w,r+E,1,p.minY,p.maxY,l),x=null),v&&(_=ht(v,h,r-b,r+T,1,p.minY,p.maxY,l),y=ht(v,h,r+w,r+E,1,p.minY,p.maxY,l),v=null),c>1&&console.timeEnd("clipping"),a.push(f||[],e+1,2*i,2*r),a.push(g||[],e+1,2*i,2*r+1),a.push(_||[],e+1,2*i+1,2*r),a.push(y||[],e+1,2*i+1,2*r+1)}}},St.prototype.getTile=function(t,e,i){var r=this.options,n=r.extent,s=r.debug;if(t<0||t>24)return null;var o=1<1&&console.log("drilling down to z%d-%d-%d",t,e,i);for(var l,c=t,h=e,u=i;!l&&c>0;)c--,h=Math.floor(h/2),u=Math.floor(u/2),l=this.tiles[It(c,h,u)];return l&&l.source?(s>1&&console.log("found parent tile z%d-%d-%d",c,h,u),s>1&&console.time("drilling down"),this.splitTile(l.source,c,h,u,t,e,i),s>1&&console.timeEnd("drilling down"),this.tiles[a]?vt(this.tiles[a],n):null):null};class zt extends l{constructor(t,e,i,r){super(t,e,i,At),r&&(this.loadGeoJSON=r)}loadData(e,i){var r;null===(r=this._pendingRequest)||void 0===r||r.cancel(),this._pendingCallback&&this._pendingCallback(null,{abandoned:!0});const n=!!(e&&e.request&&e.request.collectResourceTiming)&&new t.RequestPerformance(e.request);this._pendingCallback=i,this._pendingRequest=this.loadGeoJSON(e,((r,s)=>{if(delete this._pendingCallback,delete this._pendingRequest,r||!s)return i(r);if("object"!=typeof s)return i(new Error(`Input data given to '${e.source}' is not a valid GeoJSON object.`));{h(s,!0);try{if(e.filter){const i=t.createExpression(e.filter,{type:"boolean","property-type":"data-driven",overridable:!1,transition:!1});if("error"===i.result)throw new Error(i.value.map((t=>`${t.key}: ${t.message}`)).join(", "));const r=s.features.filter((t=>i.value.evaluate({zoom:0},t)));s={type:"FeatureCollection",features:r}}this._geoJSONIndex=e.cluster?new $(function({superclusterOptions:e,clusterProperties:i}){if(!i||!e)return e;const r={},n={},s={accumulated:null,zoom:0},o={properties:null},a=Object.keys(i);for(const e of a){const[s,o]=i[e],a=t.createExpression(o),l=t.createExpression("string"==typeof s?[s,["accumulated"],["get",e]]:s);r[e]=a.value,n[e]=l.value}return e.map=t=>{o.properties=t;const e={};for(const t of a)e[t]=r[t].evaluate(s,o);return e},e.reduce=(t,e)=>{o.properties=e;for(const e of a)s.accumulated=t[e],t[e]=n[e].evaluate(s,o)},e}(e)).load(s.features):function(t,e){return new St(t,e)}(s,e.geojsonVtOptions)}catch(r){return i(r)}this.loaded={};const o={};if(n){const t=n.finish();t&&(o.resourceTiming={},o.resourceTiming[e.source]=JSON.parse(JSON.stringify(t)))}i(null,o)}}))}reloadTile(t,e){const i=this.loaded;return i&&i[t.uid]?super.reloadTile(t,e):this.loadTile(t,e)}loadGeoJSON(e,i){if(e.request)return t.getJSON(e.request,i);if("string"==typeof e.data)try{i(null,JSON.parse(e.data))}catch(t){i(new Error(`Input data given to '${e.source}' is not a valid GeoJSON object.`))}else i(new Error(`Input data given to '${e.source}' is not a valid GeoJSON object.`));return{cancel:()=>{}}}removeSource(t,e){this._pendingCallback&&this._pendingCallback(null,{abandoned:!0}),e()}getClusterExpansionZoom(t,e){try{e(null,this._geoJSONIndex.getClusterExpansionZoom(t.clusterId))}catch(t){e(t)}}getClusterChildren(t,e){try{e(null,this._geoJSONIndex.getChildren(t.clusterId))}catch(t){e(t)}}getClusterLeaves(t,e){try{e(null,this._geoJSONIndex.getLeaves(t.clusterId,t.limit,t.offset))}catch(t){e(t)}}}class Ct{constructor(e){this.self=e,this.actor=new t.Actor(e,this),this.layerIndexes={},this.availableImages={},this.workerSourceTypes={vector:l,geojson:zt},this.workerSources={},this.demWorkerSources={},this.self.registerWorkerSource=(t,e)=>{if(this.workerSourceTypes[t])throw new Error(`Worker source with name "${t}" already registered.`);this.workerSourceTypes[t]=e},this.self.registerRTLTextPlugin=e=>{if(t.plugin.isParsed())throw new Error("RTL text plugin already registered.");t.plugin.applyArabicShaping=e.applyArabicShaping,t.plugin.processBidirectionalText=e.processBidirectionalText,t.plugin.processStyledBidirectionalText=e.processStyledBidirectionalText}}setReferrer(t,e){this.referrer=e}setImages(t,e,i){this.availableImages[t]=e;for(const i in this.workerSources[t]){const r=this.workerSources[t][i];for(const t in r)r[t].availableImages=e}i()}setLayers(t,e,i){this.getLayerIndex(t).replace(e),i()}updateLayers(t,e,i){this.getLayerIndex(t).update(e.layers,e.removedIds),i()}loadTile(t,e,i){this.getWorkerSource(t,e.type,e.source).loadTile(e,i)}loadDEMTile(t,e,i){this.getDEMWorkerSource(t,e.source).loadTile(e,i)}reloadTile(t,e,i){this.getWorkerSource(t,e.type,e.source).reloadTile(e,i)}abortTile(t,e,i){this.getWorkerSource(t,e.type,e.source).abortTile(e,i)}removeTile(t,e,i){this.getWorkerSource(t,e.type,e.source).removeTile(e,i)}removeDEMTile(t,e){this.getDEMWorkerSource(t,e.source).removeTile(e)}removeSource(t,e,i){if(!this.workerSources[t]||!this.workerSources[t][e.type]||!this.workerSources[t][e.type][e.source])return;const r=this.workerSources[t][e.type][e.source];delete this.workerSources[t][e.type][e.source],void 0!==r.removeSource?r.removeSource(e,i):i()}loadWorkerSource(t,e,i){try{this.self.importScripts(e.url),i()}catch(t){i(t.toString())}}syncRTLPluginState(e,i,r){try{t.plugin.setState(i);const e=t.plugin.getPluginURL();if(t.plugin.isLoaded()&&!t.plugin.isParsed()&&null!=e){this.self.importScripts(e);const i=t.plugin.isParsed();r(i?void 0:new Error(`RTL Text Plugin failed to import scripts from ${e}`),i)}}catch(t){r(t.toString())}}getAvailableImages(t){let e=this.availableImages[t];return e||(e=[]),e}getLayerIndex(t){let e=this.layerIndexes[t];return e||(e=this.layerIndexes[t]=new r),e}getWorkerSource(t,e,i){if(this.workerSources[t]||(this.workerSources[t]={}),this.workerSources[t][e]||(this.workerSources[t][e]={}),!this.workerSources[t][e][i]){const r={send:(e,i,r)=>{this.actor.send(e,i,r,t)}};this.workerSources[t][e][i]=new this.workerSourceTypes[e](r,this.getLayerIndex(t),this.getAvailableImages(t))}return this.workerSources[t][e][i]}getDEMWorkerSource(t,e){return this.demWorkerSources[t]||(this.demWorkerSources[t]={}),this.demWorkerSources[t][e]||(this.demWorkerSources[t][e]=new c),this.demWorkerSources[t][e]}enforceCacheSizeLimit(e,i){t.enforceCacheSizeLimit(i)}}return t.isWorker()&&(self.worker=new Ct(self)),Ct})),r(["./shared"],(function(t){var e=i;function i(t){return!function(t){return"undefined"==typeof window||"undefined"==typeof document?"not a browser":Array.prototype&&Array.prototype.every&&Array.prototype.filter&&Array.prototype.forEach&&Array.prototype.indexOf&&Array.prototype.lastIndexOf&&Array.prototype.map&&Array.prototype.some&&Array.prototype.reduce&&Array.prototype.reduceRight&&Array.isArray?Function.prototype&&Function.prototype.bind?Object.keys&&Object.create&&Object.getPrototypeOf&&Object.getOwnPropertyNames&&Object.isSealed&&Object.isFrozen&&Object.isExtensible&&Object.getOwnPropertyDescriptor&&Object.defineProperty&&Object.defineProperties&&Object.seal&&Object.freeze&&Object.preventExtensions?"JSON"in window&&"parse"in JSON&&"stringify"in JSON?function(){if(!("Worker"in window&&"Blob"in window&&"URL"in window))return!1;var t,e,i=new Blob([""],{type:"text/javascript"}),r=URL.createObjectURL(i);try{e=new Worker(r),t=!0}catch(e){t=!1}return e&&e.terminate(),URL.revokeObjectURL(r),t}()?"Uint8ClampedArray"in window?ArrayBuffer.isView?function(){var t=document.createElement("canvas");t.width=t.height=1;var e=t.getContext("2d");if(!e)return!1;var i=e.getImageData(0,0,1,1);return i&&i.width===t.width}()?(void 0===r[e=t&&t.failIfMajorPerformanceCaveat]&&(r[e]=function(t){var e,r=function(t){var e=document.createElement("canvas"),r=Object.create(i.webGLContextAttributes);return r.failIfMajorPerformanceCaveat=t,e.getContext("webgl",r)||e.getContext("experimental-webgl",r)}(t);if(!r)return!1;try{e=r.createShader(r.VERTEX_SHADER)}catch(t){return!1}return!(!e||r.isContextLost())&&(r.shaderSource(e,"void main() {}"),r.compileShader(e),!0===r.getShaderParameter(e,r.COMPILE_STATUS))}(e)),r[e]?document.documentMode?"insufficient ECMAScript 6 support":void 0:"insufficient WebGL support"):"insufficient Canvas/getImageData support":"insufficient ArrayBuffer support":"insufficient Uint8ClampedArray support":"insufficient worker support":"insufficient JSON support":"insufficient Object support":"insufficient Function support":"insufficent Array support";var e}(t)}var r={};function n(t,e){if(Array.isArray(t)){if(!Array.isArray(e)||t.length!==e.length)return!1;for(let i=0;i{window.removeEventListener("click",s.suppressClickInternal,!0)}),0)}static mousePos(e,i){const r=e.getBoundingClientRect();return new t.pointGeometry(i.clientX-r.left-e.clientLeft,i.clientY-r.top-e.clientTop)}static touchPos(e,i){const r=e.getBoundingClientRect(),n=[];for(let s=0;s-1);l++,s[l]=a,o[l]=c,o[l+1]=u}for(let a=0,l=0;a{let r=this.entries[t];r||(r=this.entries[t]={glyphs:{},requests:{},ranges:{}});let n=r.glyphs[e];if(void 0!==n)return void i(null,{stack:t,id:e,glyph:n});if(n=this._tinySDF(r,t,e),n)return r.glyphs[e]=n,void i(null,{stack:t,id:e,glyph:n});const s=Math.floor(e/256);if(256*s>65535)return void i(new Error("glyphs > 65535 not supported"));if(r.ranges[s])return void i(null,{stack:t,id:e,glyph:n});let o=r.requests[s];o||(o=r.requests[s]=[],m.loadGlyphRange(t,s,this.url,this.requestManager,((t,e)=>{if(e){for(const t in e)this._doesCharSupportLocalGlyph(+t)||(r.glyphs[+t]=e[+t]);r.ranges[s]=!0}for(const i of o)i(t,e);delete r.requests[s]}))),o.push(((r,n)=>{r?i(r):n&&i(null,{stack:t,id:e,glyph:n[e]||null})}))}),((t,e)=>{if(t)i(t);else if(e){const t={};for(const{stack:i,id:r,glyph:n}of e)(t[i]||(t[i]={}))[r]=n&&{id:n.id,bitmap:n.bitmap.clone(),metrics:n.metrics};i(null,t)}}))}_doesCharSupportLocalGlyph(e){return!!this.localIdeographFontFamily&&(t.unicodeBlockLookup["CJK Unified Ideographs"](e)||t.unicodeBlockLookup["Hangul Syllables"](e)||t.unicodeBlockLookup.Hiragana(e)||t.unicodeBlockLookup.Katakana(e))}_tinySDF(e,i,r){const n=this.localIdeographFontFamily;if(!n)return;if(!this._doesCharSupportLocalGlyph(r))return;let s=e.tinySDF;if(!s){let t="400";/bold/i.test(i)?t="900":/medium/i.test(i)?t="500":/light/i.test(i)&&(t="200"),s=e.tinySDF=new m.TinySDF({fontSize:24,buffer:3,radius:8,cutoff:.25,fontFamily:n,fontWeight:t})}const o=s.draw(String.fromCharCode(r));return{id:r,bitmap:new t.AlphaImage({width:o.width||30,height:o.height||30},o.data),metrics:{width:o.glyphWidth||24,height:o.glyphHeight||24,left:o.glyphLeft||0,top:o.glyphTop-27||-8,advance:o.glyphAdvance||24}}}}m.loadGlyphRange=function(e,i,r,n,s){const o=256*i,a=o+255,l=n.transformRequest(r.replace("{fontstack}",e).replace("{range}",`${o}-${a}`),t.ResourceType.Glyphs);t.getArrayBuffer(l,((e,i)=>{if(e)s(e);else if(i){const e={};for(const r of t.parseGlyphPbf(i))e[r.id]=r;s(null,e)}}))},m.TinySDF=class{constructor({fontSize:t=24,buffer:e=3,radius:i=8,cutoff:r=.25,fontFamily:n="sans-serif",fontWeight:s="normal",fontStyle:o="normal"}={}){this.buffer=e,this.cutoff=r,this.radius=i;const a=this.size=t+4*e,l=this._createCanvas(a),c=this.ctx=l.getContext("2d",{willReadFrequently:!0});c.font=`${o} ${s} ${t}px ${n}`,c.textBaseline="alphabetic",c.textAlign="left",c.fillStyle="black",this.gridOuter=new Float64Array(a*a),this.gridInner=new Float64Array(a*a),this.f=new Float64Array(a),this.z=new Float64Array(a+1),this.v=new Uint16Array(a)}_createCanvas(t){const e=document.createElement("canvas");return e.width=e.height=t,e}draw(t){const{width:e,actualBoundingBoxAscent:i,actualBoundingBoxDescent:r,actualBoundingBoxLeft:n,actualBoundingBoxRight:s}=this.ctx.measureText(t),o=Math.ceil(i),a=Math.min(this.size-this.buffer,Math.ceil(s-n)),l=Math.min(this.size-this.buffer,o+Math.ceil(r)),c=a+2*this.buffer,h=l+2*this.buffer,d=Math.max(c*h,0),m=new Uint8ClampedArray(d),f={data:m,width:c,height:h,glyphWidth:a,glyphHeight:l,glyphTop:o,glyphLeft:0,glyphAdvance:e};if(0===a||0===l)return f;const{ctx:g,buffer:_,gridInner:y,gridOuter:x}=this;g.clearRect(_,_,a,l),g.fillText(t,_,_+o);const v=g.getImageData(_,_,a,l);x.fill(u,0,d),y.fill(0,0,d);for(let t=0;t0?t*t:0,y[r]=t<0?t*t:0}}p(x,0,0,c,h,c,this.f,this.v,this.z),p(y,_,_,a,l,c,this.f,this.v,this.z);for(let t=0;t1&&(o=t[++s]);const l=Math.abs(a-o.left),c=Math.abs(a-o.right),h=Math.min(l,c);let u;const p=e/i*(r+1);if(o.isDash){const t=r-Math.abs(p);u=Math.sqrt(h*h+t*t)}else u=r-Math.sqrt(h*h+p*p);this.data[n+a]=Math.max(0,Math.min(255,u+128))}}}addRegularDash(t){for(let e=t.length-1;e>=0;--e){const i=t[e],r=t[e+1];i.zeroLength?t.splice(e,1):r&&r.isDash===i.isDash&&(r.left=i.left,t.splice(e,1))}const e=t[0],i=t[t.length-1];e.isDash===i.isDash&&(e.left=i.left-this.width,i.right=e.right+this.width);const r=this.width*this.nextRow;let n=0,s=t[n];for(let e=0;e1&&(s=t[++n]);const i=Math.abs(e-s.left),o=Math.abs(e-s.right),a=Math.min(i,o);this.data[r+e]=Math.max(0,Math.min(255,(s.isDash?a:-a)+128))}}addDash(e,i){const r=i?7:0,n=2*r+1;if(this.nextRow+n>this.height)return t.warnOnce("LineAtlas out of space"),null;let s=0;for(let t=0;t{t.send(e,i,r)}),r=r||function(){})}getActor(){return this.currentActor=(this.currentActor+1)%this.actors.length,this.actors[this.currentActor]}remove(){this.actors.forEach((t=>{t.remove()})),this.actors=[],this.workerPool.release(this.id)}}function v(e,i,r){const n=function(i,n){if(i)return r(i);if(n){const i=t.pick(t.extend(n,e),["tiles","minzoom","maxzoom","attribution","bounds","scheme","tileSize","encoding"]);n.vector_layers&&(i.vectorLayers=n.vector_layers,i.vectorLayerIds=i.vectorLayers.map((t=>t.id))),r(null,i)}};return e.url?t.getJSON(i.transformRequest(e.url,t.ResourceType.Source),n):t.exported.frame((()=>n(null,e)))}x.Actor=t.Actor;class b{constructor(e,i,r){this.bounds=t.LngLatBounds.convert(this.validateBounds(e)),this.minzoom=i||0,this.maxzoom=r||24}validateBounds(t){return Array.isArray(t)&&4===t.length?[Math.max(-180,t[0]),Math.max(-90,t[1]),Math.min(180,t[2]),Math.min(90,t[3])]:[-180,-90,180,90]}contains(e){const i=Math.pow(2,e.z),r=Math.floor(t.mercatorXfromLng(this.bounds.getWest())*i),n=Math.floor(t.mercatorYfromLat(this.bounds.getNorth())*i),s=Math.ceil(t.mercatorXfromLng(this.bounds.getEast())*i),o=Math.ceil(t.mercatorYfromLat(this.bounds.getSouth())*i);return e.x>=r&&e.x=n&&e.y{this._tileJSONRequest=null,this._loaded=!0,this.map.style.sourceCaches[this.id].clearTiles(),e?this.fire(new t.ErrorEvent(e)):i&&(t.extend(this,i),i.bounds&&(this.tileBounds=new b(i.bounds,this.minzoom,this.maxzoom)),this.fire(new t.Event("data",{dataType:"source",sourceDataType:"metadata"})),this.fire(new t.Event("data",{dataType:"source",sourceDataType:"content"})))}))}loaded(){return this._loaded}hasTile(t){return!this.tileBounds||this.tileBounds.contains(t.canonical)}onAdd(t){this.map=t,this.load()}setSourceProperty(t){this._tileJSONRequest&&this._tileJSONRequest.cancel(),t(),this.load()}setTiles(t){return this.setSourceProperty((()=>{this._options.tiles=t})),this}setUrl(t){return this.setSourceProperty((()=>{this.url=t,this._options.url=t})),this}onRemove(){this._tileJSONRequest&&(this._tileJSONRequest.cancel(),this._tileJSONRequest=null)}serialize(){return t.extend({},this._options)}loadTile(e,i){const r=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme),n={request:this.map._requestManager.transformRequest(r,t.ResourceType.Tile),uid:e.uid,tileID:e.tileID,zoom:e.tileID.overscaledZ,tileSize:this.tileSize*e.tileID.overscaleFactor(),type:this.type,source:this.id,pixelRatio:this.map.getPixelRatio(),showCollisionBoxes:this.map.showCollisionBoxes,promoteId:this.promoteId};function s(r,n){return delete e.request,e.aborted?i(null):r&&404!==r.status?i(r):(n&&n.resourceTiming&&(e.resourceTiming=n.resourceTiming),this.map._refreshExpiredTiles&&n&&e.setExpiryData(n),e.loadVectorData(n,this.map.painter),t.cacheEntryPossiblyAdded(this.dispatcher),i(null),void(e.reloadCallback&&(this.loadTile(e,e.reloadCallback),e.reloadCallback=null)))}n.request.collectResourceTiming=this._collectResourceTiming,e.actor&&"expired"!==e.state?"loading"===e.state?e.reloadCallback=i:e.request=e.actor.send("reloadTile",n,s.bind(this)):(e.actor=this.dispatcher.getActor(),e.request=e.actor.send("loadTile",n,s.bind(this)))}abortTile(t){t.request&&(t.request.cancel(),delete t.request),t.actor&&t.actor.send("abortTile",{uid:t.uid,type:this.type,source:this.id},void 0)}unloadTile(t){t.unloadVectorData(),t.actor&&t.actor.send("removeTile",{uid:t.uid,type:this.type,source:this.id},void 0)}hasTransition(){return!1}}class T extends t.Evented{constructor(e,i,r,n){super(),this.id=e,this.dispatcher=r,this.setEventedParent(n),this.type="raster",this.minzoom=0,this.maxzoom=22,this.roundZoom=!0,this.scheme="xyz",this.tileSize=512,this._loaded=!1,this._options=t.extend({type:"raster"},i),t.extend(this,t.pick(i,["url","scheme","tileSize"]))}load(){this._loaded=!1,this.fire(new t.Event("dataloading",{dataType:"source"})),this._tileJSONRequest=v(this._options,this.map._requestManager,((e,i)=>{this._tileJSONRequest=null,this._loaded=!0,e?this.fire(new t.ErrorEvent(e)):i&&(t.extend(this,i),i.bounds&&(this.tileBounds=new b(i.bounds,this.minzoom,this.maxzoom)),this.fire(new t.Event("data",{dataType:"source",sourceDataType:"metadata"})),this.fire(new t.Event("data",{dataType:"source",sourceDataType:"content"})))}))}loaded(){return this._loaded}onAdd(t){this.map=t,this.load()}onRemove(){this._tileJSONRequest&&(this._tileJSONRequest.cancel(),this._tileJSONRequest=null)}serialize(){return t.extend({},this._options)}hasTile(t){return!this.tileBounds||this.tileBounds.contains(t.canonical)}loadTile(e,i){const r=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme);e.request=t.getImage(this.map._requestManager.transformRequest(r,t.ResourceType.Tile),((r,n,s)=>{if(delete e.request,e.aborted)e.state="unloaded",i(null);else if(r)e.state="errored",i(r);else if(n){this.map._refreshExpiredTiles&&e.setExpiryData(s);const r=this.map.painter.context,o=r.gl;e.texture=this.map.painter.getTileTexture(n.width),e.texture?e.texture.update(n,{useMipmap:!0}):(e.texture=new l(r,n,o.RGBA,{useMipmap:!0}),e.texture.bind(o.LINEAR,o.CLAMP_TO_EDGE,o.LINEAR_MIPMAP_NEAREST),r.extTextureFilterAnisotropic&&o.texParameterf(o.TEXTURE_2D,r.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT,r.extTextureFilterAnisotropicMax)),e.state="loaded",t.cacheEntryPossiblyAdded(this.dispatcher),i(null)}}))}abortTile(t,e){t.request&&(t.request.cancel(),delete t.request),e()}unloadTile(t,e){t.texture&&this.map.painter.saveTileTexture(t.texture),e()}hasTransition(){return!1}}let E;class S extends T{constructor(e,i,r,n){super(e,i,r,n),this.type="raster-dem",this.maxzoom=22,this._options=t.extend({type:"raster-dem"},i),this.encoding=i.encoding||"mapbox"}serialize(){return{type:"raster-dem",url:this.url,tileSize:this.tileSize,tiles:this.tiles,bounds:this.bounds,encoding:this.encoding}}loadTile(e,i){const r=e.tileID.canonical.url(this.tiles,this.map.getPixelRatio(),this.scheme);function n(t,r){t&&(e.state="errored",i(t)),r&&(e.dem=r,e.needsHillshadePrepare=!0,e.needsTerrainPrepare=!0,e.state="loaded",i(null))}e.request=t.getImage(this.map._requestManager.transformRequest(r,t.ResourceType.Tile),function(r,s){if(delete e.request,e.aborted)e.state="unloaded",i(null);else if(r)e.state="errored",i(r);else if(s){this.map._refreshExpiredTiles&&e.setExpiryData(s),delete s.cacheControl,delete s.expires;const i=t.isImageBitmap(s)&&(null==E&&(E="undefined"!=typeof OffscreenCanvas&&new OffscreenCanvas(1,1).getContext("2d")&&"function"==typeof createImageBitmap),E)?s:t.exported.getImageData(s,1),r={uid:e.uid,coord:e.tileID,source:this.id,rawImageData:i,encoding:this.encoding};e.actor&&"expired"!==e.state||(e.actor=this.dispatcher.getActor(),e.actor.send("loadDEMTile",r,n.bind(this)))}}.bind(this)),e.neighboringTiles=this._getNeighboringTiles(e.tileID)}_getNeighboringTiles(e){const i=e.canonical,r=Math.pow(2,i.z),n=(i.x-1+r)%r,s=0===i.x?e.wrap-1:e.wrap,o=(i.x+1+r)%r,a=i.x+1===r?e.wrap+1:e.wrap,l={};return l[new t.OverscaledTileID(e.overscaledZ,s,i.z,n,i.y).key]={backfilled:!1},l[new t.OverscaledTileID(e.overscaledZ,a,i.z,o,i.y).key]={backfilled:!1},i.y>0&&(l[new t.OverscaledTileID(e.overscaledZ,s,i.z,n,i.y-1).key]={backfilled:!1},l[new t.OverscaledTileID(e.overscaledZ,e.wrap,i.z,i.x,i.y-1).key]={backfilled:!1},l[new t.OverscaledTileID(e.overscaledZ,a,i.z,o,i.y-1).key]={backfilled:!1}),i.y+1{if(this._pendingLoads--,this._removed||r&&r.abandoned)return void this.fire(new t.Event("dataabort",{dataType:"source",sourceDataType:e}));let n=null;if(r&&r.resourceTiming&&r.resourceTiming[this.id]&&(n=r.resourceTiming[this.id].slice(0)),i)return void this.fire(new t.ErrorEvent(i));const s={dataType:"source",sourceDataType:e};this._collectResourceTiming&&n&&n.length>0&&t.extend(s,{resourceTiming:n}),this.fire(new t.Event("data",s))}))}loaded(){return 0===this._pendingLoads}loadTile(t,e){const i=t.actor?"reloadTile":"loadTile";t.actor=this.actor;const r={type:this.type,uid:t.uid,tileID:t.tileID,zoom:t.tileID.overscaledZ,maxZoom:this.maxzoom,tileSize:this.tileSize,source:this.id,pixelRatio:this.map.getPixelRatio(),showCollisionBoxes:this.map.showCollisionBoxes,promoteId:this.promoteId};t.request=this.actor.send(i,r,((r,n)=>(delete t.request,t.unloadVectorData(),t.aborted?e(null):r?e(r):(t.loadVectorData(n,this.map.painter,"reloadTile"===i),e(null)))))}abortTile(t){t.request&&(t.request.cancel(),delete t.request),t.aborted=!0}unloadTile(t){t.unloadVectorData(),this.actor.send("removeTile",{uid:t.uid,type:this.type,source:this.id})}onRemove(){this._removed=!0,this.actor.send("removeSource",{type:this.type,source:this.id})}serialize(){return t.extend({},this._options,{type:this.type,data:this._data})}hasTransition(){return!1}}var A=t.createLayout([{name:"a_pos",type:"Int16",components:2},{name:"a_texture_pos",type:"Int16",components:2}]);class z extends t.Evented{constructor(t,e,i,r){super(),this.id=t,this.dispatcher=i,this.coordinates=e.coordinates,this.type="image",this.minzoom=0,this.maxzoom=22,this.tileSize=512,this.tiles={},this._loaded=!1,this.setEventedParent(r),this.options=e}load(e,i){this._loaded=!1,this.fire(new t.Event("dataloading",{dataType:"source"})),this.url=this.options.url,t.getImage(this.map._requestManager.transformRequest(this.url,t.ResourceType.Image),((r,n)=>{this._loaded=!0,r?this.fire(new t.ErrorEvent(r)):n&&(this.image=n,e&&(this.coordinates=e),i&&i(),this._finishLoading())}))}loaded(){return this._loaded}updateImage(t){return this.image&&t.url?(this.options.url=t.url,this.load(t.coordinates,(()=>{this.texture=null})),this):this}_finishLoading(){this.map&&(this.setCoordinates(this.coordinates),this.fire(new t.Event("data",{dataType:"source",sourceDataType:"metadata"})))}onAdd(t){this.map=t,this.load()}setCoordinates(e){this.coordinates=e;const i=e.map(t.MercatorCoordinate.fromLngLat);this.tileID=function(e){let i=1/0,r=1/0,n=-1/0,s=-1/0;for(const t of e)i=Math.min(i,t.x),r=Math.min(r,t.y),n=Math.max(n,t.x),s=Math.max(s,t.y);const o=Math.max(n-i,s-r),a=Math.max(0,Math.floor(-Math.log(o)/Math.LN2)),l=Math.pow(2,a);return new t.CanonicalTileID(a,Math.floor((i+n)/2*l),Math.floor((r+s)/2*l))}(i),this.minzoom=this.maxzoom=this.tileID.z;const r=i.map((t=>this.tileID.getTilePoint(t)._round()));return this._boundsArray=new t.RasterBoundsArray,this._boundsArray.emplaceBack(r[0].x,r[0].y,0,0),this._boundsArray.emplaceBack(r[1].x,r[1].y,t.EXTENT,0),this._boundsArray.emplaceBack(r[3].x,r[3].y,0,t.EXTENT),this._boundsArray.emplaceBack(r[2].x,r[2].y,t.EXTENT,t.EXTENT),this.boundsBuffer&&(this.boundsBuffer.destroy(),delete this.boundsBuffer),this.fire(new t.Event("data",{dataType:"source",sourceDataType:"content"})),this}prepare(){if(0===Object.keys(this.tiles).length||!this.image)return;const e=this.map.painter.context,i=e.gl;this.boundsBuffer||(this.boundsBuffer=e.createVertexBuffer(this._boundsArray,A.members)),this.boundsSegments||(this.boundsSegments=t.SegmentVector.simpleSegment(0,0,4,2)),this.texture||(this.texture=new l(e,this.image,i.RGBA),this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE));for(const t in this.tiles){const e=this.tiles[t];"loaded"!==e.state&&(e.state="loaded",e.texture=this.texture)}}loadTile(t,e){this.tileID&&this.tileID.equals(t.tileID.canonical)?(this.tiles[String(t.tileID.wrap)]=t,t.buckets={},e(null)):(t.state="errored",e(null))}serialize(){return{type:"image",url:this.options.url,coordinates:this.coordinates}}hasTransition(){return!1}}class C extends z{constructor(t,e,i,r){super(t,e,i,r),this.roundZoom=!0,this.type="video",this.options=e}load(){this._loaded=!1;const e=this.options;this.urls=[];for(const i of e.urls)this.urls.push(this.map._requestManager.transformRequest(i,t.ResourceType.Source).url);t.getVideo(this.urls,((e,i)=>{this._loaded=!0,e?this.fire(new t.ErrorEvent(e)):i&&(this.video=i,this.video.loop=!0,this.video.addEventListener("playing",(()=>{this.map.triggerRepaint()})),this.map&&this.video.play(),this._finishLoading())}))}pause(){this.video&&this.video.pause()}play(){this.video&&this.video.play()}seek(e){if(this.video){const i=this.video.seekable;ei.end(0)?this.fire(new t.ErrorEvent(new t.ValidationError(`sources.${this.id}`,null,`Playback for this video can be set only between the ${i.start(0)} and ${i.end(0)}-second mark.`))):this.video.currentTime=e}}getVideo(){return this.video}onAdd(t){this.map||(this.map=t,this.load(),this.video&&(this.video.play(),this.setCoordinates(this.coordinates)))}prepare(){if(0===Object.keys(this.tiles).length||this.video.readyState<2)return;const e=this.map.painter.context,i=e.gl;this.boundsBuffer||(this.boundsBuffer=e.createVertexBuffer(this._boundsArray,A.members)),this.boundsSegments||(this.boundsSegments=t.SegmentVector.simpleSegment(0,0,4,2)),this.texture?this.video.paused||(this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE),i.texSubImage2D(i.TEXTURE_2D,0,0,0,i.RGBA,i.UNSIGNED_BYTE,this.video)):(this.texture=new l(e,this.video,i.RGBA),this.texture.bind(i.LINEAR,i.CLAMP_TO_EDGE));for(const t in this.tiles){const e=this.tiles[t];"loaded"!==e.state&&(e.state="loaded",e.texture=this.texture)}}serialize(){return{type:"video",urls:this.urls,coordinates:this.coordinates}}hasTransition(){return this.video&&!this.video.paused}}class M extends z{constructor(e,i,r,n){super(e,i,r,n),i.coordinates?Array.isArray(i.coordinates)&&4===i.coordinates.length&&!i.coordinates.some((t=>!Array.isArray(t)||2!==t.length||t.some((t=>"number"!=typeof t))))||this.fire(new t.ErrorEvent(new t.ValidationError(`sources.${e}`,null,'"coordinates" property must be an array of 4 longitude/latitude array pairs'))):this.fire(new t.ErrorEvent(new t.ValidationError(`sources.${e}`,null,'missing required property "coordinates"'))),i.animate&&"boolean"!=typeof i.animate&&this.fire(new t.ErrorEvent(new t.ValidationError(`sources.${e}`,null,'optional "animate" property must be a boolean value'))),i.canvas?"string"==typeof i.canvas||i.canvas instanceof HTMLCanvasElement||this.fire(new t.ErrorEvent(new t.ValidationError(`sources.${e}`,null,'"canvas" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance'))):this.fire(new t.ErrorEvent(new t.ValidationError(`sources.${e}`,null,'missing required property "canvas"'))),this.options=i,this.animate=void 0===i.animate||i.animate}load(){this._loaded=!0,this.canvas||(this.canvas=this.options.canvas instanceof HTMLCanvasElement?this.options.canvas:document.getElementById(this.options.canvas)),this.width=this.canvas.width,this.height=this.canvas.height,this._hasInvalidDimensions()?this.fire(new t.ErrorEvent(new Error("Canvas dimensions cannot be less than or equal to zero."))):(this.play=function(){this._playing=!0,this.map.triggerRepaint()},this.pause=function(){this._playing&&(this.prepare(),this._playing=!1)},this._finishLoading())}getCanvas(){return this.canvas}onAdd(t){this.map=t,this.load(),this.canvas&&this.animate&&this.play()}onRemove(){this.pause()}prepare(){let e=!1;if(this.canvas.width!==this.width&&(this.width=this.canvas.width,e=!0),this.canvas.height!==this.height&&(this.height=this.canvas.height,e=!0),this._hasInvalidDimensions())return;if(0===Object.keys(this.tiles).length)return;const i=this.map.painter.context,r=i.gl;this.boundsBuffer||(this.boundsBuffer=i.createVertexBuffer(this._boundsArray,A.members)),this.boundsSegments||(this.boundsSegments=t.SegmentVector.simpleSegment(0,0,4,2)),this.texture?(e||this._playing)&&this.texture.update(this.canvas,{premultiply:!0}):this.texture=new l(i,this.canvas,r.RGBA,{premultiply:!0});for(const t in this.tiles){const e=this.tiles[t];"loaded"!==e.state&&(e.state="loaded",e.texture=this.texture)}}serialize(){return{type:"canvas",coordinates:this.coordinates}}hasTransition(){return this._playing}_hasInvalidDimensions(){for(const t of[this.canvas.width,this.canvas.height])if(isNaN(t)||t<=0)return!0;return!1}}const k={vector:w,raster:T,"raster-dem":S,geojson:I,video:C,image:z,canvas:M};function P(e,i){const r=t.create();return t.translate(r,r,[1,1,0]),t.scale(r,r,[.5*e.width,.5*e.height,1]),t.multiply(r,r,e.calculatePosMatrix(i.toUnwrapped()))}function D(t,e,i,r,n,s){const o=function(t,e,i){if(t)for(const r of t){const t=e[r];if(t&&t.source===i&&"fill-extrusion"===t.type)return!0}else for(const t in e){const r=e[t];if(r.source===i&&"fill-extrusion"===r.type)return!0}return!1}(n&&n.layers,e,t.id),a=s.maxPitchScaleFactor(),l=t.tilesIn(r,a,o);l.sort(L);const c=[];for(const r of l)c.push({wrappedTileID:r.tileID.wrapped().key,queryResults:r.tile.queryRenderedFeatures(e,i,t._state,r.queryGeometry,r.cameraQueryGeometry,r.scale,n,s,a,P(t.transform,r.tileID))});const h=function(t){const e={},i={};for(const r of t){const t=r.queryResults,n=r.wrappedTileID,s=i[n]=i[n]||{};for(const i in t){const r=t[i],n=s[i]=s[i]||{},o=e[i]=e[i]||[];for(const t of r)n[t.featureIndex]||(n[t.featureIndex]=!0,o.push(t))}}return e}(c);for(const e in h)h[e].forEach((e=>{const i=e.feature,r=t.getFeatureState(i.layer["source-layer"],i.id);i.source=i.layer.source,i.layer["source-layer"]&&(i.sourceLayer=i.layer["source-layer"]),i.state=r}));return h}function L(t,e){const i=t.tileID,r=e.tileID;return i.overscaledZ-r.overscaledZ||i.canonical.y-r.canonical.y||i.wrap-r.wrap||i.canonical.x-r.canonical.x}class B{constructor(e,i){this.tileID=e,this.uid=t.uniqueId(),this.uses=0,this.tileSize=i,this.buckets={},this.expirationTime=null,this.queryPadding=0,this.hasSymbolBuckets=!1,this.hasRTLText=!1,this.dependencies={},this.textures=[],this.textureCoords={},this.expiredRequestCount=0,this.state="loading"}registerFadeDuration(e){const i=e+this.timeAdded;it.saveTileTexture(e))),this.demTexture=null,this.textures=[],this.textureCoords={}}loadVectorData(e,i,r){if(this.hasData()&&this.unloadVectorData(),this.state="loaded",e){e.featureIndex&&(this.latestFeatureIndex=e.featureIndex,e.rawTileData?(this.latestRawTileData=e.rawTileData,this.latestFeatureIndex.rawTileData=e.rawTileData):this.latestRawTileData&&(this.latestFeatureIndex.rawTileData=this.latestRawTileData)),this.collisionBoxArray=e.collisionBoxArray,this.buckets=function(t,e){const i={};if(!e)return i;for(const r of t){const t=r.layerIds.map((t=>e.getLayer(t))).filter(Boolean);if(0!==t.length){r.layers=t,r.stateDependentLayerIds&&(r.stateDependentLayers=r.stateDependentLayerIds.map((e=>t.filter((t=>t.id===e))[0])));for(const e of t)i[e.id]=r}}return i}(e.buckets,i.style),this.hasSymbolBuckets=!1;for(const e in this.buckets){const i=this.buckets[e];if(i instanceof t.SymbolBucket){if(this.hasSymbolBuckets=!0,!r)break;i.justReloaded=!0}}if(this.hasRTLText=!1,this.hasSymbolBuckets)for(const e in this.buckets){const i=this.buckets[e];if(i instanceof t.SymbolBucket&&i.hasRTLText){this.hasRTLText=!0,t.lazyLoadRTLTextPlugin();break}}this.queryPadding=0;for(const t in this.buckets){const e=this.buckets[t];this.queryPadding=Math.max(this.queryPadding,i.style.getLayer(t).queryRadius(e))}e.imageAtlas&&(this.imageAtlas=e.imageAtlas),e.glyphAtlasImage&&(this.glyphAtlasImage=e.glyphAtlasImage)}else this.collisionBoxArray=new t.CollisionBoxArray}unloadVectorData(){for(const t in this.buckets)this.buckets[t].destroy();this.buckets={},this.imageAtlasTexture&&this.imageAtlasTexture.destroy(),this.imageAtlas&&(this.imageAtlas=null),this.glyphAtlasTexture&&this.glyphAtlasTexture.destroy(),this.latestFeatureIndex=null,this.state="unloaded"}getBucket(t){return this.buckets[t.id]}upload(t){for(const e in this.buckets){const i=this.buckets[e];i.uploadPending()&&i.upload(t)}const e=t.gl;this.imageAtlas&&!this.imageAtlas.uploaded&&(this.imageAtlasTexture=new l(t,this.imageAtlas.image,e.RGBA),this.imageAtlas.uploaded=!0),this.glyphAtlasImage&&(this.glyphAtlasTexture=new l(t,this.glyphAtlasImage,e.ALPHA),this.glyphAtlasImage=null)}prepare(t){this.imageAtlas&&this.imageAtlas.patchUpdatedImages(t,this.imageAtlasTexture)}queryRenderedFeatures(t,e,i,r,n,s,o,a,l,c){return this.latestFeatureIndex&&this.latestFeatureIndex.rawTileData?this.latestFeatureIndex.query({queryGeometry:r,cameraQueryGeometry:n,scale:s,tileSize:this.tileSize,pixelPosMatrix:c,transform:a,params:o,queryPadding:this.queryPadding*l},t,e,i):{}}querySourceFeatures(e,i){const r=this.latestFeatureIndex;if(!r||!r.rawTileData)return;const n=r.loadVTLayers(),s=i?i.sourceLayer:"",o=n._geojsonTileLayer||n[s];if(!o)return;const a=t.createFilter(i&&i.filter),{z:l,x:c,y:h}=this.tileID.canonical,u={z:l,x:c,y:h};for(let i=0;it)e=!1;else if(i)if(this.expirationTime{this.remove(t,n)}),i)),this.data[r].push(n),this.order.push(r),this.order.length>this.max){const t=this._getAndRemoveByKey(this.order[0]);t&&this.onRemove(t)}return this}has(t){return t.wrapped().key in this.data}getAndRemove(t){return this.has(t)?this._getAndRemoveByKey(t.wrapped().key):null}_getAndRemoveByKey(t){const e=this.data[t].shift();return e.timeout&&clearTimeout(e.timeout),0===this.data[t].length&&delete this.data[t],this.order.splice(this.order.indexOf(t),1),e.value}getByKey(t){const e=this.data[t];return e?e[0].value:null}get(t){return this.has(t)?this.data[t.wrapped().key][0].value:null}remove(t,e){if(!this.has(t))return this;const i=t.wrapped().key,r=void 0===e?0:this.data[i].indexOf(e),n=this.data[i][r];return this.data[i].splice(r,1),n.timeout&&clearTimeout(n.timeout),0===this.data[i].length&&delete this.data[i],this.onRemove(n.value),this.order.splice(this.order.indexOf(i),1),this}setMaxSize(t){for(this.max=t;this.order.length>this.max;){const t=this._getAndRemoveByKey(this.order[0]);t&&this.onRemove(t)}return this}filter(t){const e=[];for(const i in this.data)for(const r of this.data[i])t(r.value)||e.push(r);for(const t of e)this.remove(t.value.tileID,t)}}class F{constructor(){this.state={},this.stateChanges={},this.deletedStates={}}updateState(e,i,r){const n=String(i);if(this.stateChanges[e]=this.stateChanges[e]||{},this.stateChanges[e][n]=this.stateChanges[e][n]||{},t.extend(this.stateChanges[e][n],r),null===this.deletedStates[e]){this.deletedStates[e]={};for(const t in this.state[e])t!==n&&(this.deletedStates[e][t]=null)}else if(this.deletedStates[e]&&null===this.deletedStates[e][n]){this.deletedStates[e][n]={};for(const t in this.state[e][n])r[t]||(this.deletedStates[e][n][t]=null)}else for(const t in r)this.deletedStates[e]&&this.deletedStates[e][n]&&null===this.deletedStates[e][n][t]&&delete this.deletedStates[e][n][t]}removeFeatureState(t,e,i){if(null===this.deletedStates[t])return;const r=String(e);if(this.deletedStates[t]=this.deletedStates[t]||{},i&&void 0!==e)null!==this.deletedStates[t][r]&&(this.deletedStates[t][r]=this.deletedStates[t][r]||{},this.deletedStates[t][r][i]=null);else if(void 0!==e)if(this.stateChanges[t]&&this.stateChanges[t][r])for(i in this.deletedStates[t][r]={},this.stateChanges[t][r])this.deletedStates[t][r][i]=null;else this.deletedStates[t][r]=null;else this.deletedStates[t]=null}getState(e,i){const r=String(i),n=t.extend({},(this.state[e]||{})[r],(this.stateChanges[e]||{})[r]);if(null===this.deletedStates[e])return{};if(this.deletedStates[e]){const t=this.deletedStates[e][i];if(null===t)return{};for(const e in t)delete n[e]}return n}initializeTileState(t,e){t.setFeatureState(this.state,e)}coalesceChanges(e,i){const r={};for(const e in this.stateChanges){this.state[e]=this.state[e]||{};const i={};for(const r in this.stateChanges[e])this.state[e][r]||(this.state[e][r]={}),t.extend(this.state[e][r],this.stateChanges[e][r]),i[r]=this.state[e][r];r[e]=i}for(const e in this.deletedStates){this.state[e]=this.state[e]||{};const i={};if(null===this.deletedStates[e])for(const t in this.state[e])i[t]={},this.state[e][t]={};else for(const t in this.deletedStates[e]){if(null===this.deletedStates[e][t])this.state[e][t]={};else for(const i of Object.keys(this.deletedStates[e][t]))delete this.state[e][t][i];i[t]=this.state[e][t]}r[e]=r[e]||{},t.extend(r[e],i)}if(this.stateChanges={},this.deletedStates={},0!==Object.keys(r).length)for(const t in e)e[t].setFeatureState(r,i)}}class O extends t.Evented{constructor(e,i,r){super(),this.id=e,this.dispatcher=r,this.on("data",(t=>{"source"===t.dataType&&"metadata"===t.sourceDataType&&(this._sourceLoaded=!0),this._sourceLoaded&&!this._paused&&"source"===t.dataType&&"content"===t.sourceDataType&&(this.reload(),this.transform&&this.update(this.transform,this.terrain))})),this.on("dataloading",(()=>{this._sourceErrored=!1})),this.on("error",(()=>{this._sourceErrored=this._source.loaded()})),this._source=function(e,i,r,n){const s=new k[i.type](e,i,r,n);if(s.id!==e)throw new Error(`Expected Source id to be ${e} instead of ${s.id}`);return t.bindAll(["load","abort","unload","serialize","prepare"],s),s}(e,i,r,this),this._tiles={},this._cache=new R(0,this._unloadTile.bind(this)),this._timers={},this._cacheTimers={},this._maxTileCacheSize=null,this._loadedParentTiles={},this._coveredTiles={},this._state=new F}onAdd(t){this.map=t,this._maxTileCacheSize=t?t._maxTileCacheSize:null,this._source&&this._source.onAdd&&this._source.onAdd(t)}onRemove(t){this.clearTiles(),this._source&&this._source.onRemove&&this._source.onRemove(t)}loaded(){if(this._sourceErrored)return!0;if(!this._sourceLoaded)return!1;if(!this._source.loaded())return!1;for(const t in this._tiles){const e=this._tiles[t];if("loaded"!==e.state&&"errored"!==e.state)return!1}return!0}getSource(){return this._source}pause(){this._paused=!0}resume(){if(!this._paused)return;const t=this._shouldReloadOnResume;this._paused=!1,this._shouldReloadOnResume=!1,t&&this.reload(),this.transform&&this.update(this.transform,this.terrain)}_loadTile(t,e){return this._source.loadTile(t,e)}_unloadTile(t){if(this._source.unloadTile)return this._source.unloadTile(t,(()=>{}))}_abortTile(e){this._source.abortTile&&this._source.abortTile(e,(()=>{})),this._source.fire(new t.Event("dataabort",{tile:e,coord:e.tileID,dataType:"source"}))}serialize(){return this._source.serialize()}prepare(t){this._source.prepare&&this._source.prepare(),this._state.coalesceChanges(this._tiles,this.map?this.map.painter:null);for(const e in this._tiles){const i=this._tiles[e];i.upload(t),i.prepare(this.map.style.imageManager)}}getIds(){return Object.values(this._tiles).map((t=>t.tileID)).sort(U).map((t=>t.key))}getRenderableIds(e){const i=[];for(const t in this._tiles)this._isIdRenderable(t,e)&&i.push(this._tiles[t]);return e?i.sort(((e,i)=>{const r=e.tileID,n=i.tileID,s=new t.pointGeometry(r.canonical.x,r.canonical.y)._rotate(this.transform.angle),o=new t.pointGeometry(n.canonical.x,n.canonical.y)._rotate(this.transform.angle);return r.overscaledZ-n.overscaledZ||o.y-s.y||o.x-s.x})).map((t=>t.tileID.key)):i.map((t=>t.tileID)).sort(U).map((t=>t.key))}hasRenderableParent(t){const e=this.findLoadedParent(t,0);return!!e&&this._isIdRenderable(e.tileID.key)}_isIdRenderable(t,e){return this._tiles[t]&&this._tiles[t].hasData()&&!this._coveredTiles[t]&&(e||!this._tiles[t].holdingForFade())}reload(){if(this._paused)this._shouldReloadOnResume=!0;else{this._cache.reset();for(const t in this._tiles)"errored"!==this._tiles[t].state&&this._reloadTile(t,"reloading")}}_reloadTile(t,e){const i=this._tiles[t];i&&("loading"!==i.state&&(i.state=e),this._loadTile(i,this._tileLoaded.bind(this,i,t,e)))}_tileLoaded(e,i,r,n){if(n)return e.state="errored",void(404!==n.status?this._source.fire(new t.ErrorEvent(n,{tile:e})):this.update(this.transform,this.terrain));e.timeAdded=t.exported.now(),"expired"===r&&(e.refreshedUponExpiration=!0),this._setTileReloadTimer(i,e),"raster-dem"===this.getSource().type&&e.dem&&this._backfillDEM(e),this._state.initializeTileState(e,this.map?this.map.painter:null),e.aborted||this._source.fire(new t.Event("data",{dataType:"source",tile:e,coord:e.tileID}))}_backfillDEM(t){const e=this.getRenderableIds();for(let r=0;r1||(Math.abs(i)>1&&(1===Math.abs(i+n)?i+=n:1===Math.abs(i-n)&&(i-=n)),e.dem&&t.dem&&(t.dem.backfillBorder(e.dem,i,r),t.neighboringTiles&&t.neighboringTiles[s]&&(t.neighboringTiles[s].backfilled=!0)))}}getTile(t){return this.getTileByID(t.key)}getTileByID(t){return this._tiles[t]}_retainLoadedChildren(t,e,i,r){for(const n in this._tiles){let s=this._tiles[n];if(r[n]||!s.hasData()||s.tileID.overscaledZ<=e||s.tileID.overscaledZ>i)continue;let o=s.tileID;for(;s&&s.tileID.overscaledZ>e+1;){const t=s.tileID.scaledTo(s.tileID.overscaledZ-1);s=this._tiles[t.key],s&&s.hasData()&&(o=t)}let a=o;for(;a.overscaledZ>e;)if(a=a.scaledTo(a.overscaledZ-1),t[a.key]){r[o.key]=o;break}}}findLoadedParent(t,e){if(t.key in this._loadedParentTiles){const i=this._loadedParentTiles[t.key];return i&&i.tileID.overscaledZ>=e?i:null}for(let i=t.overscaledZ-1;i>=e;i--){const e=t.scaledTo(i),r=this._getLoadedTile(e);if(r)return r}}_getLoadedTile(t){const e=this._tiles[t.key];return e&&e.hasData()?e:this._cache.getByKey(t.wrapped().key)}updateCacheSize(t){const e=Math.ceil(t.width/this._source.tileSize)+1,i=Math.ceil(t.height/this._source.tileSize)+1,r=Math.floor(e*i*5),n="number"==typeof this._maxTileCacheSize?Math.min(this._maxTileCacheSize,r):r;this._cache.setMaxSize(n)}handleWrapJump(t){const e=Math.round((t-(void 0===this._prevLng?t:this._prevLng))/360);if(this._prevLng=t,e){const t={};for(const i in this._tiles){const r=this._tiles[i];r.tileID=r.tileID.unwrapTo(r.tileID.wrap+e),t[r.tileID.key]=r}this._tiles=t;for(const t in this._timers)clearTimeout(this._timers[t]),delete this._timers[t];for(const t in this._tiles)this._setTileReloadTimer(t,this._tiles[t])}}update(e,i){if(this.transform=e,this.terrain=i,!this._sourceLoaded||this._paused)return;let r;this.updateCacheSize(e),this.handleWrapJump(this.transform.center.lng),this._coveredTiles={},this.used||this.usedForTerrain?this._source.tileID?r=e.getVisibleUnwrappedCoordinates(this._source.tileID).map((e=>new t.OverscaledTileID(e.canonical.z,e.wrap,e.canonical.z,e.canonical.x,e.canonical.y))):(r=e.coveringTiles({tileSize:this.usedForTerrain?this.tileSize:this._source.tileSize,minzoom:this._source.minzoom,maxzoom:this._source.maxzoom,roundZoom:!this.usedForTerrain&&this._source.roundZoom,reparseOverscaled:this._source.reparseOverscaled,terrain:i}),this._source.hasTile&&(r=r.filter((t=>this._source.hasTile(t))))):r=[];const n=e.coveringZoomLevel(this._source),s=Math.max(n-O.maxOverzooming,this._source.minzoom),o=Math.max(n+O.maxUnderzooming,this._source.minzoom);if(this.usedForTerrain){const t={};for(const e of r)if(e.canonical.z>this._source.minzoom){const i=e.scaledTo(e.canonical.z-1);t[i.key]=i;const r=e.scaledTo(Math.max(this._source.minzoom,Math.min(e.canonical.z,5)));t[r.key]=r}r=r.concat(Object.values(t))}const a=this._updateRetainedTiles(r,n);if(V(this._source.type)){const e={},l={},c=Object.keys(a);for(const i of c){const r=a[i],n=this._tiles[i];if(!n||n.fadeEndTime&&n.fadeEndTime<=t.exported.now())continue;const o=this.findLoadedParent(r,s);o&&(this._addTile(o.tileID),e[o.tileID.key]=o.tileID),l[i]=r}this._retainLoadedChildren(l,n,o,a);for(const t in e)a[t]||(this._coveredTiles[t]=!0,a[t]=e[t]);if(i){const t={},e={};for(const i of r)this._tiles[i.key].hasData()?t[i.key]=i:e[i.key]=i;for(const i in e){const r=e[i].children(this._source.maxzoom);this._tiles[r[0].key]&&this._tiles[r[1].key]&&this._tiles[r[2].key]&&this._tiles[r[3].key]&&(t[r[0].key]=a[r[0].key]=r[0],t[r[1].key]=a[r[1].key]=r[1],t[r[2].key]=a[r[2].key]=r[2],t[r[3].key]=a[r[3].key]=r[3],delete e[i])}for(const i in e){const r=this.findLoadedParent(e[i],this._source.minzoom);if(r){t[r.tileID.key]=a[r.tileID.key]=r.tileID;for(const e in t)t[e].isChildOf(r.tileID)&&delete t[e]}}for(const e in this._tiles)t[e]||(this._coveredTiles[e]=!0)}}for(const t in a)this._tiles[t].clearFadeHold();const l=t.keysDifference(this._tiles,a);for(const t of l){const e=this._tiles[t];e.hasSymbolBuckets&&!e.holdingForFade()?e.setHoldDuration(this.map._fadeDuration):e.hasSymbolBuckets&&!e.symbolFadeFinished()||this._removeTile(t)}this._updateLoadedParentTileCache()}releaseSymbolFadeTiles(){for(const t in this._tiles)this._tiles[t].holdingForFade()&&this._removeTile(t)}_updateRetainedTiles(t,e){const i={},r={},n=Math.max(e-O.maxOverzooming,this._source.minzoom),s=Math.max(e+O.maxUnderzooming,this._source.minzoom),o={};for(const r of t){const t=this._addTile(r);i[r.key]=r,t.hasData()||ethis._source.maxzoom){const t=s.children(this._source.maxzoom)[0],e=this.getTile(t);if(e&&e.hasData()){i[t.key]=t;continue}}else{const t=s.children(this._source.maxzoom);if(i[t[0].key]&&i[t[1].key]&&i[t[2].key]&&i[t[3].key])continue}let o=t.wasRequested();for(let e=s.overscaledZ-1;e>=n;--e){const n=s.scaledTo(e);if(r[n.key])break;if(r[n.key]=!0,t=this.getTile(n),!t&&o&&(t=this._addTile(n)),t&&(i[n.key]=n,o=t.wasRequested(),t.hasData()))break}}return i}_updateLoadedParentTileCache(){this._loadedParentTiles={};for(const t in this._tiles){const e=[];let i,r=this._tiles[t].tileID;for(;r.overscaledZ>0;){if(r.key in this._loadedParentTiles){i=this._loadedParentTiles[r.key];break}e.push(r.key);const t=r.scaledTo(r.overscaledZ-1);if(i=this._getLoadedTile(t),i)break;r=t}for(const t of e)this._loadedParentTiles[t]=i}}_addTile(e){let i=this._tiles[e.key];if(i)return i;i=this._cache.getAndRemove(e),i&&(this._setTileReloadTimer(e.key,i),i.tileID=e,this._state.initializeTileState(i,this.map?this.map.painter:null),this._cacheTimers[e.key]&&(clearTimeout(this._cacheTimers[e.key]),delete this._cacheTimers[e.key],this._setTileReloadTimer(e.key,i)));const r=i;return i||(i=new B(e,this._source.tileSize*e.overscaleFactor()),this._loadTile(i,this._tileLoaded.bind(this,i,e.key,i.state))),i.uses++,this._tiles[e.key]=i,r||this._source.fire(new t.Event("dataloading",{tile:i,coord:i.tileID,dataType:"source"})),i}_setTileReloadTimer(t,e){t in this._timers&&(clearTimeout(this._timers[t]),delete this._timers[t]);const i=e.getExpiryTimeout();i&&(this._timers[t]=setTimeout((()=>{this._reloadTile(t,"expired"),delete this._timers[t]}),i))}_removeTile(t){const e=this._tiles[t];e&&(e.uses--,delete this._tiles[t],this._timers[t]&&(clearTimeout(this._timers[t]),delete this._timers[t]),e.uses>0||(e.hasData()&&"reloading"!==e.state?this._cache.add(e.tileID,e,e.getExpiryTimeout()):(e.aborted=!0,this._abortTile(e),this._unloadTile(e))))}clearTiles(){this._shouldReloadOnResume=!1,this._paused=!1;for(const t in this._tiles)this._removeTile(t);this._cache.reset()}tilesIn(e,i,r){const n=[],s=this.transform;if(!s)return n;const o=r?s.getCameraQueryGeometry(e):e,a=e.map((t=>s.pointCoordinate(t,this.terrain))),l=o.map((t=>s.pointCoordinate(t,this.terrain))),c=this.getIds();let h=1/0,u=1/0,p=-1/0,d=-1/0;for(const t of l)h=Math.min(h,t.x),u=Math.min(u,t.y),p=Math.max(p,t.x),d=Math.max(d,t.y);for(let e=0;e=0&&g[1].y+f>=0){const t=a.map((t=>o.getTilePoint(t))),e=l.map((t=>o.getTilePoint(t)));n.push({tile:r,tileID:o,queryGeometry:t,cameraQueryGeometry:e,scale:m})}}return n}getVisibleCoordinates(t){const e=this.getRenderableIds(t).map((t=>this._tiles[t].tileID));for(const t of e)t.posMatrix=this.transform.calculatePosMatrix(t.toUnwrapped());return e}hasTransition(){if(this._source.hasTransition())return!0;if(V(this._source.type))for(const e in this._tiles){const i=this._tiles[e];if(void 0!==i.fadeEndTime&&i.fadeEndTime>=t.exported.now())return!0}return!1}setFeatureState(t,e,i){this._state.updateState(t=t||"_geojsonTileLayer",e,i)}removeFeatureState(t,e,i){this._state.removeFeatureState(t=t||"_geojsonTileLayer",e,i)}getFeatureState(t,e){return this._state.getState(t=t||"_geojsonTileLayer",e)}setDependencies(t,e,i){const r=this._tiles[t];r&&r.setDependencies(e,i)}reloadTilesForDependencies(t,e){for(const i in this._tiles)this._tiles[i].hasDependency(t,e)&&this._reloadTile(i,"reloading");this._cache.filter((i=>!i.hasDependency(t,e)))}}function U(t,e){const i=Math.abs(2*t.wrap)-+(t.wrap<0),r=Math.abs(2*e.wrap)-+(e.wrap<0);return t.overscaledZ-e.overscaledZ||r-i||e.canonical.y-t.canonical.y||e.canonical.x-t.canonical.x}function V(t){return"raster"===t||"image"===t||"video"===t}O.maxOverzooming=10,O.maxUnderzooming=3;const N="mapboxgl_preloaded_worker_pool";class G{constructor(){this.active={}}acquire(t){if(!this.workers)for(this.workers=[];this.workers.length{t.terminate()})),this.workers=null)}isPreloaded(){return!!this.active[N]}numActive(){return Object.keys(this.active).length}}const $=Math.floor(t.exported.hardwareConcurrency/2);let q;function j(){return q||(q=new G),q}function Z(e,i){const r={};for(const t in e)"ref"!==t&&(r[t]=e[t]);return t.refProperties.forEach((t=>{t in i&&(r[t]=i[t])})),r}function X(t){t=t.slice();const e=Object.create(null);for(let i=0;i0?(n-o)/a:0;return this.points[s].mult(1-l).add(this.points[i].mult(l))}}function rt(t,e){let i=!0;return"always"===t||"never"!==t&&"never"!==e||(i=!1),i}class nt{constructor(t,e,i){const r=this.boxCells=[],n=this.circleCells=[];this.xCellCount=Math.ceil(t/i),this.yCellCount=Math.ceil(e/i);for(let t=0;tthis.width||r<0||e>this.height)return[];const a=[];if(t<=0&&e<=0&&this.width<=i&&this.height<=r){if(n)return[{key:null,x1:t,y1:e,x2:i,y2:r}];for(let t=0;t0}hitTestCircle(t,e,i,r,n){const s=t-i,o=t+i,a=e-i,l=e+i;if(o<0||s>this.width||l<0||a>this.height)return!1;const c=[];return this._forEachCell(s,a,o,l,this._queryCellCircle,c,{hitTest:!0,overlapMode:r,circle:{x:t,y:e,radius:i},seenUids:{box:{},circle:{}}},n),c.length>0}_queryCell(t,e,i,r,n,s,o,a){const{seenUids:l,hitTest:c,overlapMode:h}=o,u=this.boxCells[n];if(null!==u){const n=this.bboxes;for(const o of u)if(!l.box[o]){l.box[o]=!0;const u=4*o,p=this.boxKeys[o];if(t<=n[u+2]&&e<=n[u+3]&&i>=n[u+0]&&r>=n[u+1]&&(!a||a(p))&&(!c||!rt(h,p.overlapMode))&&(s.push({key:p,x1:n[u],y1:n[u+1],x2:n[u+2],y2:n[u+3]}),c))return!0}}const p=this.circleCells[n];if(null!==p){const n=this.circles;for(const o of p)if(!l.circle[o]){l.circle[o]=!0;const u=3*o,p=this.circleKeys[o];if(this._circleAndRectCollide(n[u],n[u+1],n[u+2],t,e,i,r)&&(!a||a(p))&&(!c||!rt(h,p.overlapMode))){const t=n[u],e=n[u+1],i=n[u+2];if(s.push({key:p,x1:t-i,y1:e-i,x2:t+i,y2:e+i}),c)return!0}}}return!1}_queryCellCircle(t,e,i,r,n,s,o,a){const{circle:l,seenUids:c,overlapMode:h}=o,u=this.boxCells[n];if(null!==u){const t=this.bboxes;for(const e of u)if(!c.box[e]){c.box[e]=!0;const i=4*e,r=this.boxKeys[e];if(this._circleAndRectCollide(l.x,l.y,l.radius,t[i+0],t[i+1],t[i+2],t[i+3])&&(!a||a(r))&&!rt(h,r.overlapMode))return s.push(!0),!0}}const p=this.circleCells[n];if(null!==p){const t=this.circles;for(const e of p)if(!c.circle[e]){c.circle[e]=!0;const i=3*e,r=this.circleKeys[e];if(this._circlesCollide(t[i],t[i+1],t[i+2],l.x,l.y,l.radius)&&(!a||a(r))&&!rt(h,r.overlapMode))return s.push(!0),!0}}}_forEachCell(t,e,i,r,n,s,o,a){const l=this._convertToXCellCoord(t),c=this._convertToYCellCoord(e),h=this._convertToXCellCoord(i),u=this._convertToYCellCoord(r);for(let p=l;p<=h;p++)for(let l=c;l<=u;l++)if(n.call(this,t,e,i,r,this.xCellCount*l+p,s,o,a))return}_convertToXCellCoord(t){return Math.max(0,Math.min(this.xCellCount-1,Math.floor(t*this.xScale)))}_convertToYCellCoord(t){return Math.max(0,Math.min(this.yCellCount-1,Math.floor(t*this.yScale)))}_circlesCollide(t,e,i,r,n,s){const o=r-t,a=n-e,l=i+s;return l*l>o*o+a*a}_circleAndRectCollide(t,e,i,r,n,s,o){const a=(s-r)/2,l=Math.abs(t-(r+a));if(l>a+i)return!1;const c=(o-n)/2,h=Math.abs(e-(n+c));if(h>c+i)return!1;if(l<=a||h<=c)return!0;const u=l-a,p=h-c;return u*u+p*p<=i*i}}function st(e,i,r,n,s){const o=t.create();return i?(t.scale(o,o,[1/s,1/s,1]),r||t.rotateZ(o,o,n.angle)):t.multiply(o,n.labelPlaneMatrix,e),o}function ot(e,i,r,n,s){if(i){const i=t.clone(e);return t.scale(i,i,[s,s,1]),r||t.rotateZ(i,i,-n.angle),i}return n.glCoordMatrix}function at(e,i,r){let n;r?(n=[e.x,e.y,r(e.x,e.y),1],t.transformMat4(n,n,i)):(n=[e.x,e.y,0,1],yt(n,n,i));const s=n[3];return{point:new t.pointGeometry(n[0]/s,n[1]/s),signedDistanceFromCamera:s}}function lt(t,e){return.5+t/e*.5}function ct(t,e){const i=t[0]/t[3],r=t[1]/t[3];return i>=-e[0]&&i<=e[0]&&r>=-e[1]&&r<=e[1]}function ht(e,i,r,n,s,o,a,l,c,h){const u=n?e.textSizeData:e.iconSizeData,p=t.evaluateSizeForZoom(u,r.transform.zoom),d=[256/r.width*2+1,256/r.height*2+1],m=n?e.text.dynamicLayoutVertexArray:e.icon.dynamicLayoutVertexArray;m.clear();const f=e.lineVertexArray,g=n?e.text.placedSymbolArray:e.icon.placedSymbolArray,_=r.transform.width/r.transform.height;let y=!1;for(let n=0;nMath.abs(r.x-i.x)*n?{useVertical:!0}:(e===t.WritingMode.vertical?i.yr.x)?{needsFlipping:!0}:null}function dt(e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g){const _=i/24,y=e.lineOffsetX*_,x=e.lineOffsetY*_;let v;if(e.numGlyphs>1){const t=e.glyphStartIndex+e.numGlyphs,i=e.lineStartIndex,s=e.lineStartIndex+e.lineLength,h=ut(_,l,y,x,r,u,p,e,c,o,d,f,g);if(!h)return{notEnoughRoom:!0};const b=at(h.first.point,a,g).point,w=at(h.last.point,a,g).point;if(n&&!r){const t=pt(e.writingMode,b,w,m);if(t)return t}v=[h.first];for(let n=e.glyphStartIndex+1;n0?o.point:mt(p,n,i,1,s,g),l=pt(e.writingMode,i,a,m);if(l)return l}const i=ft(_*l.getoffsetX(e.glyphStartIndex),y,x,r,u,p,e.segment,e.lineStartIndex,e.lineStartIndex+e.lineLength,c,o,d,f,g);if(!i)return{notEnoughRoom:!0};v=[i]}for(const e of v)t.addDynamicAttributes(h,e.point,e.angle);return{}}function mt(t,e,i,r,n,s){const o=at(t.add(t.sub(e)._unit()),n,s).point,a=i.sub(o);return i.add(a._mult(r/a.mag()))}function ft(e,i,r,n,s,o,a,l,c,h,u,p,d,m){const f=n?e-i:e+i;let g=f>0?1:-1,_=0;n&&(g*=-1,_=Math.PI),g<0&&(_+=Math.PI);let y=g>0?l+a:l+a+1,x=s,v=s,b=0,w=0;const T=Math.abs(f),E=[];for(;b+w<=T;){if(y+=g,y=c)return null;if(v=x,E.push(x),x=p[y],void 0===x){const e=new t.pointGeometry(h.getx(y),h.gety(y)),i=at(e,u,m);if(i.signedDistanceFromCamera>0)x=p[y]=i.point;else{const i=y-g;x=mt(0===b?o:new t.pointGeometry(h.getx(i),h.gety(i)),e,v,T-b+1,u,m)}}b+=w,w=v.dist(x)}const S=(T-b)/w,I=x.sub(v),A=I.mult(S)._add(v);A._add(I._unit()._perp()._mult(r*g));const z=_+Math.atan2(x.y-v.y,x.x-v.x);return E.push(A),{point:A,angle:d?z:0,path:E}}const gt=new Float32Array([-1/0,-1/0,0,-1/0,-1/0,0,-1/0,-1/0,0,-1/0,-1/0,0]);function _t(t,e){for(let i=0;i=1;t--)h.push(o.path[t]);for(let t=1;tat(t,l,m)));h=t.some((t=>t.signedDistanceFromCamera<=0))?[]:t.map((t=>t.point))}let _=[];if(h.length>0){const e=h[0].clone(),i=h[0].clone();for(let t=1;t=r.x&&i.x<=n.x&&e.y>=r.y&&i.y<=n.y?[h]:i.xn.x||i.yn.y?[]:t.clipLine([h],r.x,r.y,n.x,n.y)}for(const t of _){s.reset(t,.25*i);let r=0;r=s.length<=.5*i?1:Math.ceil(s.paddedLength/g)+1;for(let t=0;t=this.screenRightBoundary||rthis.screenBottomBoundary}isInsideGrid(t,e,i,r){return i>=0&&t=0&&et.collisionGroupID===e}}return this.collisionGroups[t]}}function zt(e,i,r,n,s){const{horizontalAlign:o,verticalAlign:a}=t.getAnchorAlignment(e),l=-(o-.5)*i,c=-(a-.5)*r,h=t.evaluateVariableOffset(e,n);return new t.pointGeometry(l+h[0]*s,c+h[1]*s)}function Ct(e,i,r,n,s,o){const{x1:a,x2:l,y1:c,y2:h,anchorPointX:u,anchorPointY:p}=e,d=new t.pointGeometry(i,r);return n&&d._rotate(s?o:-o),{x1:a+d.x,y1:c+d.y,x2:l+d.x,y2:h+d.y,anchorPointX:u,anchorPointY:p}}class Mt{constructor(t,e,i,r,n){this.transform=t.clone(),this.terrain=e,this.collisionIndex=new vt(this.transform),this.placements={},this.opacities={},this.variableOffsets={},this.stale=!1,this.commitTime=0,this.fadeDuration=i,this.retainedQueryData={},this.collisionGroups=new At(r),this.collisionCircleArrays={},this.prevPlacement=n,n&&(n.prevPlacement=void 0),this.placedOrientations={}}getBucketParts(e,i,r,n){const s=r.getBucket(i),o=r.latestFeatureIndex;if(!s||!o||i.id!==s.layerIds[0])return;const a=r.collisionBoxArray,l=s.layers[0].layout,c=Math.pow(2,this.transform.zoom-r.tileID.overscaledZ),h=r.tileSize/t.EXTENT,u=this.transform.calculatePosMatrix(r.tileID.toUnwrapped()),p="map"===l.get("text-pitch-alignment"),d="map"===l.get("text-rotation-alignment"),m=bt(r,1,this.transform.zoom),f=st(u,p,d,this.transform,m);let g=null;if(p){const e=ot(u,p,d,this.transform,m);g=t.multiply([],this.transform.labelPlaneMatrix,e)}this.retainedQueryData[s.bucketInstanceId]=new It(s.bucketInstanceId,o,s.sourceLayerIndex,s.index,r.tileID);const _={bucket:s,layout:l,posMatrix:u,textLabelPlaneMatrix:f,labelToScreenMatrix:g,scale:c,textPixelRatio:h,holdingForFade:r.holdingForFade(),collisionBoxArray:a,partiallyEvaluatedTextSize:t.evaluateSizeForZoom(s.textSizeData,this.transform.zoom),collisionGroup:this.collisionGroups.get(s.sourceID)};if(n)for(const t of s.sortKeyRanges){const{sortKey:i,symbolInstanceStart:r,symbolInstanceEnd:n}=t;e.push({sortKey:i,symbolInstanceStart:r,symbolInstanceEnd:n,parameters:_})}else e.push({symbolInstanceStart:0,symbolInstanceEnd:s.symbolInstances.length,parameters:_})}attemptAnchorPlacement(t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f){const g=[u.textOffset0,u.textOffset1],_=zt(t,i,r,g,n),y=this.collisionIndex.placeCollisionBox(Ct(e,_.x,_.y,s,o,this.transform.angle),h,a,l,c.predicate,f);if((!m||0!==this.collisionIndex.placeCollisionBox(Ct(m,_.x,_.y,s,o,this.transform.angle),h,a,l,c.predicate,f).box.length)&&y.box.length>0){let e;return this.prevPlacement&&this.prevPlacement.variableOffsets[u.crossTileID]&&this.prevPlacement.placements[u.crossTileID]&&this.prevPlacement.placements[u.crossTileID].text&&(e=this.prevPlacement.variableOffsets[u.crossTileID].anchor),this.variableOffsets[u.crossTileID]={textOffset:g,width:i,height:r,anchor:t,textBoxScale:n,prevAnchor:e},this.markUsedJustification(p,t,u,d),p.allowVerticalPlacement&&(this.markUsedOrientation(p,d,u),this.placedOrientations[u.crossTileID]=d),{shift:_,placedGlyphBoxes:y}}}placeLayerBucketPart(e,i,r){const{bucket:n,layout:s,posMatrix:o,textLabelPlaneMatrix:a,labelToScreenMatrix:l,textPixelRatio:c,holdingForFade:h,collisionBoxArray:u,partiallyEvaluatedTextSize:p,collisionGroup:d}=e.parameters,m=s.get("text-optional"),f=s.get("icon-optional"),g=t.getOverlapMode(s,"text-overlap","text-allow-overlap"),_="always"===g,y=t.getOverlapMode(s,"icon-overlap","icon-allow-overlap"),x="always"===y,v="map"===s.get("text-rotation-alignment"),b="map"===s.get("text-pitch-alignment"),w="none"!==s.get("icon-text-fit"),T="viewport-y"===s.get("symbol-z-order"),E=_&&(x||!n.hasIconData()||f),S=x&&(_||!n.hasTextData()||m);!n.collisionArrays&&u&&n.deserializeCollisionBoxes(u);const I=(e,u)=>{if(i[e.crossTileID])return;if(h)return void(this.placements[e.crossTileID]=new Et(!1,!1,!1));let x=!1,T=!1,I=!0,A=null,z={box:null,offscreen:null},C={box:null,offscreen:null},M=null,k=null,P=null,D=0,L=0,B=0;u.textFeatureIndex?D=u.textFeatureIndex:e.useRuntimeCollisionCircles&&(D=e.featureIndex),u.verticalTextFeatureIndex&&(L=u.verticalTextFeatureIndex);const R=this.retainedQueryData[n.bucketInstanceId].tileID,F=this.terrain?(t,e)=>this.terrain.getElevation(R,t,e):null;for(const t of["textBox","verticalTextBox","iconBox","verticalIconBox"]){const e=u[t];e&&(e.elevation=F?F(e.anchorPointX,e.anchorPointY):0)}const O=u.textBox;if(O){const i=i=>{let r=t.WritingMode.horizontal;if(n.allowVerticalPlacement&&!i&&this.prevPlacement){const t=this.prevPlacement.placedOrientations[e.crossTileID];t&&(this.placedOrientations[e.crossTileID]=t,r=t,this.markUsedOrientation(n,r,e))}return r},r=(i,r)=>{if(n.allowVerticalPlacement&&e.numVerticalGlyphVertices>0&&u.verticalTextBox){for(const e of n.writingModes)if(e===t.WritingMode.vertical?(z=r(),C=z):z=i(),z&&z.box&&z.box.length)break}else z=i()};if(s.get("text-variable-anchor")){let a=s.get("text-variable-anchor");if(this.prevPlacement&&this.prevPlacement.variableOffsets[e.crossTileID]){const t=this.prevPlacement.variableOffsets[e.crossTileID];a.indexOf(t.anchor)>0&&(a=a.filter((e=>e!==t.anchor)),a.unshift(t.anchor))}const l=(t,i,r)=>{const s=t.x2-t.x1,l=t.y2-t.y1,h=e.textBoxScale,u=w&&"never"===y?i:null;let p={box:[],offscreen:!1};const m="never"!==g?2*a.length:a.length;for(let i=0;i=a.length?g:"never",e,n,r,u,F);if(m&&(p=m.placedGlyphBoxes,p&&p.box&&p.box.length)){x=!0,A=m.shift;break}}return p};r((()=>l(O,u.iconBox,t.WritingMode.horizontal)),(()=>{const i=u.verticalTextBox;return n.allowVerticalPlacement&&!(z&&z.box&&z.box.length)&&e.numVerticalGlyphVertices>0&&i?l(i,u.verticalIconBox,t.WritingMode.vertical):{box:null,offscreen:null}})),z&&(x=z.box,I=z.offscreen);const h=i(z&&z.box);if(!x&&this.prevPlacement){const t=this.prevPlacement.variableOffsets[e.crossTileID];t&&(this.variableOffsets[e.crossTileID]=t,this.markUsedJustification(n,t.anchor,e,h))}}else{const s=(t,i)=>{const r=this.collisionIndex.placeCollisionBox(t,g,c,o,d.predicate,F);return r&&r.box&&r.box.length&&(this.markUsedOrientation(n,i,e),this.placedOrientations[e.crossTileID]=i),r};r((()=>s(O,t.WritingMode.horizontal)),(()=>{const i=u.verticalTextBox;return n.allowVerticalPlacement&&e.numVerticalGlyphVertices>0&&i?s(i,t.WritingMode.vertical):{box:null,offscreen:null}})),i(z&&z.box&&z.box.length)}}if(M=z,x=M&&M.box&&M.box.length>0,I=M&&M.offscreen,e.useRuntimeCollisionCircles){const i=n.text.placedSymbolArray.get(e.centerJustifiedTextSymbolIndex),c=t.evaluateSizeForFeature(n.textSizeData,p,i),h=s.get("text-padding");k=this.collisionIndex.placeCollisionCircles(g,i,n.lineVertexArray,n.glyphOffsetArray,c,o,a,l,r,b,d.predicate,e.collisionCircleDiameter,h,F),x=_||k.circles.length>0&&!k.collisionDetected,I=I&&k.offscreen}if(u.iconFeatureIndex&&(B=u.iconFeatureIndex),u.iconBox){const t=t=>{const e=w&&A?Ct(t,A.x,A.y,v,b,this.transform.angle):t;return this.collisionIndex.placeCollisionBox(e,y,c,o,d.predicate,F)};C&&C.box&&C.box.length&&u.verticalIconBox?(P=t(u.verticalIconBox),T=P.box.length>0):(P=t(u.iconBox),T=P.box.length>0),I=I&&P.offscreen}const U=m||0===e.numHorizontalGlyphVertices&&0===e.numVerticalGlyphVertices,V=f||0===e.numIconVertices;if(U||V?V?U||(T=T&&x):x=T&&x:T=x=T&&x,x&&M&&M.box&&this.collisionIndex.insertCollisionBox(M.box,g,s.get("text-ignore-placement"),n.bucketInstanceId,C&&C.box&&L?L:D,d.ID),T&&P&&this.collisionIndex.insertCollisionBox(P.box,y,s.get("icon-ignore-placement"),n.bucketInstanceId,B,d.ID),k&&(x&&this.collisionIndex.insertCollisionCircles(k.circles,g,s.get("text-ignore-placement"),n.bucketInstanceId,D,d.ID),r)){const t=n.bucketInstanceId;let e=this.collisionCircleArrays[t];void 0===e&&(e=this.collisionCircleArrays[t]=new St);for(let t=0;t=0;--e){const i=t[e];I(n.symbolInstances.get(i),n.collisionArrays[i])}}else for(let t=e.symbolInstanceStart;t=0&&(e.text.placedSymbolArray.get(t).crossTileID=s>=0&&t!==s?0:r.crossTileID)}markUsedOrientation(e,i,r){const n=i===t.WritingMode.horizontal||i===t.WritingMode.horizontalOnly?i:0,s=i===t.WritingMode.vertical?i:0,o=[r.leftJustifiedTextSymbolIndex,r.centerJustifiedTextSymbolIndex,r.rightJustifiedTextSymbolIndex];for(const t of o)e.text.placedSymbolArray.get(t).placedOrientation=n;r.verticalPlacedTextSymbolIndex&&(e.text.placedSymbolArray.get(r.verticalPlacedTextSymbolIndex).placedOrientation=s)}commit(t){this.commitTime=t,this.zoomAtLastRecencyCheck=this.transform.zoom;const e=this.prevPlacement;let i=!1;this.prevZoomAdjustment=e?e.zoomAdjustment(this.transform.zoom):0;const r=e?e.symbolFadeChange(t):1,n=e?e.opacities:{},s=e?e.variableOffsets:{},o=e?e.placedOrientations:{};for(const t in this.placements){const e=this.placements[t],s=n[t];s?(this.opacities[t]=new Tt(s,r,e.text,e.icon),i=i||e.text!==s.text.placed||e.icon!==s.icon.placed):(this.opacities[t]=new Tt(null,r,e.text,e.icon,e.skipFade),i=i||e.text||e.icon)}for(const t in n){const e=n[t];if(!this.opacities[t]){const n=new Tt(e,r,!1,!1);n.isHidden()||(this.opacities[t]=n,i=i||e.text.placed||e.icon.placed)}}for(const t in s)this.variableOffsets[t]||!this.opacities[t]||this.opacities[t].isHidden()||(this.variableOffsets[t]=s[t]);for(const t in o)this.placedOrientations[t]||!this.opacities[t]||this.opacities[t].isHidden()||(this.placedOrientations[t]=o[t]);i?this.lastPlacementChangeTime=t:"number"!=typeof this.lastPlacementChangeTime&&(this.lastPlacementChangeTime=e?e.lastPlacementChangeTime:t)}updateLayerOpacities(t,e){const i={};for(const r of e){const e=r.getBucket(t);e&&r.latestFeatureIndex&&t.id===e.layerIds[0]&&this.updateBucketOpacities(e,i,r.collisionBoxArray)}}updateBucketOpacities(e,i,r){e.hasTextData()&&e.text.opacityVertexArray.clear(),e.hasIconData()&&e.icon.opacityVertexArray.clear(),e.hasIconCollisionBoxData()&&e.iconCollisionBox.collisionVertexArray.clear(),e.hasTextCollisionBoxData()&&e.textCollisionBox.collisionVertexArray.clear();const n=e.layers[0].layout,s=new Tt(null,0,!1,!1,!0),o=n.get("text-allow-overlap"),a=n.get("icon-allow-overlap"),l=n.get("text-variable-anchor"),c="map"===n.get("text-rotation-alignment"),h="map"===n.get("text-pitch-alignment"),u="none"!==n.get("icon-text-fit"),p=new Tt(null,0,o&&(a||!e.hasIconData()||n.get("icon-optional")),a&&(o||!e.hasTextData()||n.get("text-optional")),!0);!e.collisionArrays&&r&&(e.hasIconCollisionBoxData()||e.hasTextCollisionBoxData())&&e.deserializeCollisionBoxes(r);const d=(t,e,i)=>{for(let r=0;r0,_=this.placedOrientations[n.crossTileID],y=_===t.WritingMode.vertical,x=_===t.WritingMode.horizontal||_===t.WritingMode.horizontalOnly;if(o>0||a>0){const t=Ut(f.text);d(e.text,o,y?Vt:t),d(e.text,a,x?Vt:t);const i=f.text.isHidden();[n.rightJustifiedTextSymbolIndex,n.centerJustifiedTextSymbolIndex,n.leftJustifiedTextSymbolIndex].forEach((t=>{t>=0&&(e.text.placedSymbolArray.get(t).hidden=i||y?1:0)})),n.verticalPlacedTextSymbolIndex>=0&&(e.text.placedSymbolArray.get(n.verticalPlacedTextSymbolIndex).hidden=i||x?1:0);const r=this.variableOffsets[n.crossTileID];r&&this.markUsedJustification(e,r.anchor,n,_);const s=this.placedOrientations[n.crossTileID];s&&(this.markUsedJustification(e,"left",n,s),this.markUsedOrientation(e,s,n))}if(g){const t=Ut(f.icon),i=!(u&&n.verticalPlacedIconSymbolIndex&&y);n.placedIconSymbolIndex>=0&&(d(e.icon,n.numIconVertices,i?t:Vt),e.icon.placedSymbolArray.get(n.placedIconSymbolIndex).hidden=f.icon.isHidden()),n.verticalPlacedIconSymbolIndex>=0&&(d(e.icon,n.numVerticalIconVertices,i?Vt:t),e.icon.placedSymbolArray.get(n.verticalPlacedIconSymbolIndex).hidden=f.icon.isHidden())}if(e.hasIconCollisionBoxData()||e.hasTextCollisionBoxData()){const i=e.collisionArrays[r];if(i){let r=new t.pointGeometry(0,0);if(i.textBox||i.verticalTextBox){let t=!0;if(l){const e=this.variableOffsets[m];e?(r=zt(e.anchor,e.width,e.height,e.textOffset,e.textBoxScale),c&&r._rotate(h?this.transform.angle:-this.transform.angle)):t=!1}i.textBox&&kt(e.textCollisionBox.collisionVertexArray,f.text.placed,!t||y,r.x,r.y),i.verticalTextBox&&kt(e.textCollisionBox.collisionVertexArray,f.text.placed,!t||x,r.x,r.y)}const n=Boolean(!x&&i.verticalIconBox);i.iconBox&&kt(e.iconCollisionBox.collisionVertexArray,f.icon.placed,n,u?r.x:0,u?r.y:0),i.verticalIconBox&&kt(e.iconCollisionBox.collisionVertexArray,f.icon.placed,!n,u?r.x:0,u?r.y:0)}}}if(e.sortFeatures(this.transform.angle),this.retainedQueryData[e.bucketInstanceId]&&(this.retainedQueryData[e.bucketInstanceId].featureSortOrder=e.featureSortOrder),e.hasTextData()&&e.text.opacityVertexBuffer&&e.text.opacityVertexBuffer.updateData(e.text.opacityVertexArray),e.hasIconData()&&e.icon.opacityVertexBuffer&&e.icon.opacityVertexBuffer.updateData(e.icon.opacityVertexArray),e.hasIconCollisionBoxData()&&e.iconCollisionBox.collisionVertexBuffer&&e.iconCollisionBox.collisionVertexBuffer.updateData(e.iconCollisionBox.collisionVertexArray),e.hasTextCollisionBoxData()&&e.textCollisionBox.collisionVertexBuffer&&e.textCollisionBox.collisionVertexBuffer.updateData(e.textCollisionBox.collisionVertexArray),e.bucketInstanceId in this.collisionCircleArrays){const t=this.collisionCircleArrays[e.bucketInstanceId];e.placementInvProjMatrix=t.invProjMatrix,e.placementViewportMatrix=t.viewportMatrix,e.collisionCircleArray=t.circles,delete this.collisionCircleArrays[e.bucketInstanceId]}}symbolFadeChange(t){return 0===this.fadeDuration?1:(t-this.commitTime)/this.fadeDuration+this.prevZoomAdjustment}zoomAdjustment(t){return Math.max(0,(this.transform.zoom-t)/1.5)}hasTransitions(t){return this.stale||t-this.lastPlacementChangeTimet}setStale(){this.stale=!0}}function kt(t,e,i,r,n){t.emplaceBack(e?1:0,i?1:0,r||0,n||0),t.emplaceBack(e?1:0,i?1:0,r||0,n||0),t.emplaceBack(e?1:0,i?1:0,r||0,n||0),t.emplaceBack(e?1:0,i?1:0,r||0,n||0)}const Pt=Math.pow(2,25),Dt=Math.pow(2,24),Lt=Math.pow(2,17),Bt=Math.pow(2,16),Rt=Math.pow(2,9),Ft=Math.pow(2,8),Ot=Math.pow(2,1);function Ut(t){if(0===t.opacity&&!t.placed)return 0;if(1===t.opacity&&t.placed)return 4294967295;const e=t.placed?1:0,i=Math.floor(127*t.opacity);return i*Pt+e*Dt+i*Lt+e*Bt+i*Rt+e*Ft+i*Ot+e}const Vt=0;class Nt{constructor(t){this._sortAcrossTiles="viewport-y"!==t.layout.get("symbol-z-order")&&!t.layout.get("symbol-sort-key").isConstant(),this._currentTileIndex=0,this._currentPartIndex=0,this._seenCrossTileIDs={},this._bucketParts=[]}continuePlacement(t,e,i,r,n){const s=this._bucketParts;for(;this._currentTileIndext.sortKey-e.sortKey)));this._currentPartIndex{const e=t.exported.now()-n;return!this._forceFullPlacement&&e>2};for(;this._currentPlacementIndex>=0;){const t=i[e[this._currentPlacementIndex]],n=this.placement.collisionIndex.transform.zoom;if("symbol"===t.type&&(!t.minzoom||t.minzoom<=n)&&(!t.maxzoom||t.maxzoom>n)){if(this._inProgressLayer||(this._inProgressLayer=new Nt(t)),this._inProgressLayer.continuePlacement(r[t.source],this.placement,this._showCollisionBoxes,t,s))return;delete this._inProgressLayer}this._currentPlacementIndex--}this._done=!0}commit(t){return this.placement.commit(t),this.placement}}const $t=512/t.EXTENT/2;class qt{constructor(t,e,i){this.tileID=t,this.indexedSymbolInstances={},this.bucketInstanceId=i;for(let i=0;it.overscaledZ)for(const i in n){const s=n[i];s.tileID.isChildOf(t)&&s.findMatches(e.symbolInstances,t,r)}else{const s=n[t.scaledTo(Number(i)).key];s&&s.findMatches(e.symbolInstances,t,r)}}for(let t=0;t{e[t]=!0}));for(const t in this.layerIndexes)e[t]||delete this.layerIndexes[t]}}var Wt=t.createLayout([{name:"a_pos",type:"Int16",components:2}]);class Ht extends t.Evented{constructor(t){super(),this.sourceCache=t,this._tiles={},this._renderableTilesKeys=[],this._sourceTileCache={},this.renderHistory=[],this.minzoom=0,this.maxzoom=22,this.tileSize=512,this.deltaZoom=1,this.renderHistorySize=t._cache.max,t.usedForTerrain=!0,t.tileSize=this.tileSize*2**this.deltaZoom}destruct(){this.sourceCache.usedForTerrain=!1,this.sourceCache.tileSize=null;for(const t in this._tiles){const e=this._tiles[t];e.textures.forEach((t=>t.destroy())),e.textures=[]}}update(e,i){this.sourceCache.update(e,i),this._renderableTilesKeys=[];for(const r of e.coveringTiles({tileSize:this.tileSize,minzoom:this.minzoom,maxzoom:this.maxzoom,reparseOverscaled:!1,terrain:i}))this._renderableTilesKeys.push(r.key),this._tiles[r.key]||(r.posMatrix=new Float64Array(16),t.ortho(r.posMatrix,0,t.EXTENT,0,t.EXTENT,0,1),this._tiles[r.key]=new B(r,this.tileSize))}removeOutdated(t){const e={};this.renderHistory=this.renderHistory.filter(((t,e)=>this.renderHistory.indexOf(t)===e)).slice(0,this.renderHistorySize);for(const t of this._renderableTilesKeys)e[t]=!0;for(const t of this.renderHistory)e[t]=!0;for(const i in this._tiles)e[i]||(this._tiles[i].clearTextures(t),delete this._tiles[i])}getRenderableTiles(){return this._renderableTilesKeys.map((t=>this.getTileByID(t)))}getTileByID(t){return this._tiles[t]}getTerrainCoords(e){const i={};for(const r of this._renderableTilesKeys){const n=this._tiles[r].tileID;if(n.canonical.equals(e.canonical)){const n=e.clone();n.posMatrix=new Float64Array(16),t.ortho(n.posMatrix,0,t.EXTENT,0,t.EXTENT,0,1),i[r]=n}else if(n.canonical.isChildOf(e.canonical)){const s=e.clone();s.posMatrix=new Float64Array(16);const o=n.canonical.z-e.canonical.z,a=n.canonical.x-(n.canonical.x>>o<>o<>o;t.ortho(s.posMatrix,0,c,0,c,0,1),t.translate(s.posMatrix,s.posMatrix,[-a*c,-l*c,0]),i[r]=s}else if(e.canonical.isChildOf(n.canonical)){const s=e.clone();s.posMatrix=new Float64Array(16);const o=e.canonical.z-n.canonical.z,a=e.canonical.x-(e.canonical.x>>o<>o<>o;t.ortho(s.posMatrix,0,t.EXTENT,0,t.EXTENT,0,1),t.translate(s.posMatrix,s.posMatrix,[a*c,l*c,0]),t.scale(s.posMatrix,s.posMatrix,[1/2**o,1/2**o,0]),i[r]=s}}return i}getSourceTile(t,e){const i=this.sourceCache._source;let r=t.overscaledZ-this.deltaZoom;if(r>i.maxzoom&&(r=i.maxzoom),r=i.minzoom&&(!n||!n.dem);)n=this.sourceCache.getTileByID(t.scaledTo(r--).key);return n}tilesAfterTime(t=Date.now()){return Object.values(this._tiles).filter((e=>e.timeLoaded>=t))}}class Kt{constructor(t,e,i){this.style=t,this.sourceCache=new Ht(e),this.options=i,this.exaggeration="number"==typeof i.exaggeration?i.exaggeration:1,this.elevationOffset="number"==typeof i.elevationOffset?i.elevationOffset:450,this.qualityFactor=2,this.meshSize=128,this._demMatrixCache={},this.coordsIndex=[],this._coordsTextureSize=1024,this.clearRerenderCache()}getDEMElevation(e,i,r,n=t.EXTENT){if(!(i>=0&&i=0&&re.canonical.z&&(e.canonical.z>=r?n=e.canonical.z-r:t.warnOnce("cannot calculate elevation if elevation maxzoom > source.maxzoom"));const s=e.canonical.x-(e.canonical.x>>n<>n<>8<<4|t>>8,i[e+3]=0;const r=new t.RGBAImage({width:this._coordsTextureSize,height:this._coordsTextureSize},new Uint8Array(i.buffer)),n=new l(e,r,e.gl.RGBA,{premultiply:!1});return n.bind(e.gl.NEAREST,e.gl.CLAMP_TO_EDGE),this._coordsTexture=n,n}pointCoordinate(e){const i=new Uint8Array(4),r=this.style.map.painter,n=r.context,s=n.gl;n.bindFramebuffer.set(this.getFramebuffer("coords").framebuffer),s.readPixels(e.x,r.height/devicePixelRatio-e.y-1,1,1,s.RGBA,s.UNSIGNED_BYTE,i),n.bindFramebuffer.set(null);const o=i[0]+(i[2]>>4<<8),a=i[1]+((15&i[2])<<8),l=this.coordsIndex[255-i[3]],c=l&&this.sourceCache.getTileByID(l);if(!c)return null;const h=this._coordsTextureSize,u=(1<t.emitValidationErrors(e,i&&i.filter((t=>"source.canvas"!==t.identifier))),Yt=t.pick(W,["addLayer","removeLayer","setPaintProperty","setLayoutProperty","setFilter","addSource","removeSource","setLayerZoomRange","setLight","setTransition","setGeoJSONSourceData"]),Qt=t.pick(W,["setCenter","setZoom","setBearing","setPitch"]),te=function(){const e={},i=t.spec.$version;for(const r in t.spec.$root){const n=t.spec.$root[r];if(n.required){let t=null;t="version"===r?i:"array"===n.type?[]:{},null!=t&&(e[r]=t)}}return e}();class ee extends t.Evented{constructor(e,i={}){super(),this.map=e,this.dispatcher=new x(j(),this),this.imageManager=new h,this.imageManager.setEventedParent(this),this.glyphManager=new m(e._requestManager,i.localIdeographFontFamily),this.lineAtlas=new y(256,512),this.crossTileSymbolIndex=new Xt,this._layers={},this._serializedLayers={},this._order=[],this.sourceCaches={},this.zoomHistory=new t.ZoomHistory,this._loaded=!1,this._availableImages=[],this._resetUpdates(),this.dispatcher.broadcast("setReferrer",t.getReferrer());const r=this;this._rtlTextPluginCallback=ee.registerForPluginStateChange((e=>{r.dispatcher.broadcast("syncRTLPluginState",{pluginStatus:e.pluginStatus,pluginURL:e.pluginURL},((e,i)=>{if(t.triggerPluginCompletionEvent(e),i&&i.every((t=>t)))for(const t in r.sourceCaches)r.sourceCaches[t].reload()}))})),this.on("data",(t=>{if("source"!==t.dataType||"metadata"!==t.sourceDataType)return;const e=this.sourceCaches[t.sourceId];if(!e)return;const i=e.getSource();if(i&&i.vectorLayerIds)for(const t in this._layers){const e=this._layers[t];e.source===i.id&&this._validateLayer(e)}}))}loadURL(e,i={}){this.fire(new t.Event("dataloading",{dataType:"style"}));const r="boolean"!=typeof i.validate||i.validate,n=this.map._requestManager.transformRequest(e,t.ResourceType.Style);this._request=t.getJSON(n,((e,i)=>{this._request=null,e?this.fire(new t.ErrorEvent(e)):i&&this._load(i,r)}))}loadJSON(e,i={}){this.fire(new t.Event("dataloading",{dataType:"style"})),this._request=t.exported.frame((()=>{this._request=null,this._load(e,!1!==i.validate)}))}loadEmpty(){this.fire(new t.Event("dataloading",{dataType:"style"})),this._load(te,!1)}_load(e,i){if(i&&Jt(this,t.validateStyle(e)))return;this._loaded=!0,this.stylesheet=e;for(const t in e.sources)this.addSource(t,e.sources[t],{validate:!1});e.sprite?this._loadSprite(e.sprite):this.imageManager.setLoaded(!0),this.glyphManager.setURL(e.glyphs);const r=X(this.stylesheet.layers);this._order=r.map((t=>t.id)),this._layers={},this._serializedLayers={};for(let e of r)e=t.createStyleLayer(e),e.setEventedParent(this,{layer:{id:e.id}}),this._layers[e.id]=e,this._serializedLayers[e.id]=e.serialize();this.dispatcher.broadcast("setLayers",this._serializeLayers(this._order)),this.light=new _(this.stylesheet.light),this.setTerrain(this.stylesheet.terrain),this.fire(new t.Event("data",{dataType:"style"})),this.fire(new t.Event("style.load"))}_loadSprite(e){this._spriteRequest=function(e,i,r,n){let s,o,a;const l=r>1?"@2x":"";let c=t.getJSON(i.transformRequest(i.normalizeSpriteURL(e,l,".json"),t.ResourceType.SpriteJSON),((t,e)=>{c=null,a||(a=t,s=e,u())})),h=t.getImage(i.transformRequest(i.normalizeSpriteURL(e,l,".png"),t.ResourceType.SpriteImage),((t,e)=>{h=null,a||(a=t,o=e,u())}));function u(){if(a)n(a);else if(s&&o){const e=t.exported.getImageData(o),i={};for(const r in s){const{width:n,height:o,x:a,y:l,sdf:c,pixelRatio:h,stretchX:u,stretchY:p,content:d}=s[r],m=new t.RGBAImage({width:n,height:o});t.RGBAImage.copy(e,m,{x:a,y:l},{x:0,y:0},{width:n,height:o}),i[r]={data:m,pixelRatio:h,sdf:c,stretchX:u,stretchY:p,content:d}}n(null,i)}}return{cancel(){c&&(c.cancel(),c=null),h&&(h.cancel(),h=null)}}}(e,this.map._requestManager,this.map.getPixelRatio(),((e,i)=>{if(this._spriteRequest=null,e)this.fire(new t.ErrorEvent(e));else if(i)for(const t in i)this.imageManager.addImage(t,i[t]);this.imageManager.setLoaded(!0),this._availableImages=this.imageManager.listImages(),this.dispatcher.broadcast("setImages",this._availableImages),this.fire(new t.Event("data",{dataType:"style"}))}))}_validateLayer(e){const i=this.sourceCaches[e.source];if(!i)return;const r=e.sourceLayer;if(!r)return;const n=i.getSource();("geojson"===n.type||n.vectorLayerIds&&-1===n.vectorLayerIds.indexOf(r))&&this.fire(new t.ErrorEvent(new Error(`Source layer "${r}" does not exist on source "${n.id}" as specified by style layer "${e.id}".`)))}loaded(){if(!this._loaded)return!1;if(Object.keys(this._updatedSources).length)return!1;for(const t in this.sourceCaches)if(!this.sourceCaches[t].loaded())return!1;return!!this.imageManager.isLoaded()}_serializeLayers(t){const e=[];for(const i of t){const t=this._layers[i];"custom"!==t.type&&e.push(t.serialize())}return e}hasTransitions(){if(this.light&&this.light.hasTransition())return!0;for(const t in this.sourceCaches)if(this.sourceCaches[t].hasTransition())return!0;for(const t in this._layers)if(this._layers[t].hasTransition())return!0;return!1}_checkLoaded(){if(!this._loaded)throw new Error("Style is not done loading.")}update(e){if(!this._loaded)return;const i=this._changed;if(this._changed){const t=Object.keys(this._updatedLayers),i=Object.keys(this._removedLayers);(t.length||i.length)&&this._updateWorkerLayers(t,i);for(const t in this._updatedSources){const e=this._updatedSources[t];"reload"===e?this._reloadSource(t):"clear"===e&&this._clearSource(t)}this._updateTilesForChangedImages();for(const t in this._updatedPaintProps)this._layers[t].updateTransitions(e);this.light.updateTransitions(e),this._resetUpdates()}const r={};for(const t in this.sourceCaches){const e=this.sourceCaches[t];r[t]=e.used,e.used=!1}for(const t of this._order){const i=this._layers[t];i.recalculate(e,this._availableImages),!i.isHidden(e.zoom)&&i.source&&(this.sourceCaches[i.source].used=!0)}for(const e in r){const i=this.sourceCaches[e];r[e]!==i.used&&i.fire(new t.Event("data",{sourceDataType:"visibility",dataType:"source",sourceId:e}))}this.light.recalculate(e),this.z=e.zoom,i&&this.fire(new t.Event("data",{dataType:"style"}))}_updateTilesForChangedImages(){const t=Object.keys(this._changedImages);if(t.length){for(const e in this.sourceCaches)this.sourceCaches[e].reloadTilesForDependencies(["icons","patterns"],t);this._changedImages={}}}_updateWorkerLayers(t,e){this.dispatcher.broadcast("updateLayers",{layers:this._serializeLayers(t),removedIds:e})}_resetUpdates(){this._changed=!1,this._updatedLayers={},this._removedLayers={},this._updatedSources={},this._updatedPaintProps={},this._changedImages={}}setTerrain(e){if(this._checkLoaded(),this._terrainDataCallback&&this.off("data",this._terrainDataCallback),this._terrainfreezeElevationCallback&&this.map.off("freezeElevation",this._terrainfreezeElevationCallback),e){const t=this.sourceCaches[e.source];if(!t)throw new Error(`cannot load terrain, because there exists no source with ID: ${e.source}`);this.terrain=new Kt(this,t,e),this.map.transform.updateElevation(this.terrain),this._terrainfreezeElevationCallback=t=>{t.freeze?this.map.transform.freezeElevation=!0:(this.map.transform.freezeElevation=!1,this.map.transform.recalculateZoom(this.terrain))},this._terrainDataCallback=t=>{t.tile&&(t.sourceId===e.source?(this.map.transform.updateElevation(this.terrain),this.terrain.rememberForRerender(t.sourceId,t.tile.tileID)):"geojson"===t.source.type&&this.terrain.rememberForRerender(t.sourceId,t.tile.tileID))},this.on("data",this._terrainDataCallback),this.map.on("freezeElevation",this._terrainfreezeElevationCallback)}else this.terrain=null,this.map.transform.updateElevation(this.terrain);this.map.fire(new t.Event("terrain",{terrain:e}))}setState(e){if(this._checkLoaded(),Jt(this,t.validateStyle(e)))return!1;(e=t.clone$1(e)).layers=X(e.layers);const i=function(t,e){if(!t)return[{command:W.setStyle,args:[e]}];let i=[];try{if(!n(t.version,e.version))return[{command:W.setStyle,args:[e]}];n(t.center,e.center)||i.push({command:W.setCenter,args:[e.center]}),n(t.zoom,e.zoom)||i.push({command:W.setZoom,args:[e.zoom]}),n(t.bearing,e.bearing)||i.push({command:W.setBearing,args:[e.bearing]}),n(t.pitch,e.pitch)||i.push({command:W.setPitch,args:[e.pitch]}),n(t.sprite,e.sprite)||i.push({command:W.setSprite,args:[e.sprite]}),n(t.glyphs,e.glyphs)||i.push({command:W.setGlyphs,args:[e.glyphs]}),n(t.transition,e.transition)||i.push({command:W.setTransition,args:[e.transition]}),n(t.light,e.light)||i.push({command:W.setLight,args:[e.light]});const r={},s=[];!function(t,e,i,r){let s;for(s in e=e||{},t=t||{})Object.prototype.hasOwnProperty.call(t,s)&&(Object.prototype.hasOwnProperty.call(e,s)||K(s,i,r));for(s in e)Object.prototype.hasOwnProperty.call(e,s)&&(Object.prototype.hasOwnProperty.call(t,s)?n(t[s],e[s])||("geojson"===t[s].type&&"geojson"===e[s].type&&Y(t,e,s)?i.push({command:W.setGeoJSONSourceData,args:[s,e[s].data]}):J(s,e,i,r)):H(s,e,i))}(t.sources,e.sources,s,r);const o=[];t.layers&&t.layers.forEach((t=>{r[t.source]?i.push({command:W.removeLayer,args:[t.id]}):o.push(t)})),i=i.concat(s),function(t,e,i){e=e||[];const r=(t=t||[]).map(tt),s=e.map(tt),o=t.reduce(et,{}),a=e.reduce(et,{}),l=r.slice(),c=Object.create(null);let h,u,p,d,m,f,g;for(h=0,u=0;h!(t.command in Qt)));if(0===i.length)return!1;const r=i.filter((t=>!(t.command in Yt)));if(r.length>0)throw new Error(`Unimplemented: ${r.map((t=>t.command)).join(", ")}.`);return i.forEach((t=>{"setTransition"!==t.command&&this[t.command].apply(this,t.args)})),this.stylesheet=e,!0}addImage(e,i){if(this.getImage(e))return this.fire(new t.ErrorEvent(new Error(`An image named "${e}" already exists.`)));this.imageManager.addImage(e,i),this._afterImageUpdated(e)}updateImage(t,e){this.imageManager.updateImage(t,e)}getImage(t){return this.imageManager.getImage(t)}removeImage(e){if(!this.getImage(e))return this.fire(new t.ErrorEvent(new Error(`An image named "${e}" does not exist.`)));this.imageManager.removeImage(e),this._afterImageUpdated(e)}_afterImageUpdated(e){this._availableImages=this.imageManager.listImages(),this._changedImages[e]=!0,this._changed=!0,this.dispatcher.broadcast("setImages",this._availableImages),this.fire(new t.Event("data",{dataType:"style"}))}listImages(){return this._checkLoaded(),this.imageManager.listImages()}addSource(e,i,r={}){if(this._checkLoaded(),void 0!==this.sourceCaches[e])throw new Error(`Source "${e}" already exists.`);if(!i.type)throw new Error(`The type property must be defined, but only the following properties were given: ${Object.keys(i).join(", ")}.`);if(["vector","raster","geojson","video","image"].indexOf(i.type)>=0&&this._validate(t.validateStyle.source,`sources.${e}`,i,null,r))return;this.map&&this.map._collectResourceTiming&&(i.collectResourceTiming=!0);const n=this.sourceCaches[e]=new O(e,i,this.dispatcher);n.style=this,n.setEventedParent(this,(()=>({isSourceLoaded:this.loaded(),source:n.serialize(),sourceId:e}))),n.onAdd(this.map),this._changed=!0}removeSource(e){if(this._checkLoaded(),void 0===this.sourceCaches[e])throw new Error("There is no source with this ID");for(const i in this._layers)if(this._layers[i].source===e)return this.fire(new t.ErrorEvent(new Error(`Source "${e}" cannot be removed while layer "${i}" is using it.`)));const i=this.sourceCaches[e];delete this.sourceCaches[e],delete this._updatedSources[e],i.fire(new t.Event("data",{sourceDataType:"metadata",dataType:"source",sourceId:e})),i.setEventedParent(null),i.onRemove(this.map),this._changed=!0}setGeoJSONSourceData(t,e){this._checkLoaded(),this.sourceCaches[t].getSource().setData(e),this._changed=!0}getSource(t){return this.sourceCaches[t]&&this.sourceCaches[t].getSource()}addLayer(e,i,r={}){this._checkLoaded();const n=e.id;if(this.getLayer(n))return void this.fire(new t.ErrorEvent(new Error(`Layer "${n}" already exists on this map.`)));let s;if("custom"===e.type){if(Jt(this,t.validateCustomStyleLayer(e)))return;s=t.createStyleLayer(e)}else{if("object"==typeof e.source&&(this.addSource(n,e.source),e=t.clone$1(e),e=t.extend(e,{source:n})),this._validate(t.validateStyle.layer,`layers.${n}`,e,{arrayIndex:-1},r))return;s=t.createStyleLayer(e),this._validateLayer(s),s.setEventedParent(this,{layer:{id:n}}),this._serializedLayers[s.id]=s.serialize()}const o=i?this._order.indexOf(i):this._order.length;if(i&&-1===o)this.fire(new t.ErrorEvent(new Error(`Cannot add layer "${n}" before non-existing layer "${i}".`)));else{if(this._order.splice(o,0,n),this._layerOrderChanged=!0,this._layers[n]=s,this._removedLayers[n]&&s.source&&"custom"!==s.type){const t=this._removedLayers[n];delete this._removedLayers[n],t.type!==s.type?this._updatedSources[s.source]="clear":(this._updatedSources[s.source]="reload",this.sourceCaches[s.source].pause())}this._updateLayer(s),s.onAdd&&s.onAdd(this.map)}}moveLayer(e,i){if(this._checkLoaded(),this._changed=!0,!this._layers[e])return void this.fire(new t.ErrorEvent(new Error(`The layer '${e}' does not exist in the map's style and cannot be moved.`)));if(e===i)return;const r=this._order.indexOf(e);this._order.splice(r,1);const n=i?this._order.indexOf(i):this._order.length;i&&-1===n?this.fire(new t.ErrorEvent(new Error(`Cannot move layer "${e}" before non-existing layer "${i}".`))):(this._order.splice(n,0,e),this._layerOrderChanged=!0)}removeLayer(e){this._checkLoaded();const i=this._layers[e];if(!i)return void this.fire(new t.ErrorEvent(new Error(`Cannot remove non-existing layer "${e}".`)));i.setEventedParent(null);const r=this._order.indexOf(e);this._order.splice(r,1),this._layerOrderChanged=!0,this._changed=!0,this._removedLayers[e]=i,delete this._layers[e],delete this._serializedLayers[e],delete this._updatedLayers[e],delete this._updatedPaintProps[e],i.onRemove&&i.onRemove(this.map)}getLayer(t){return this._layers[t]}hasLayer(t){return t in this._layers}setLayerZoomRange(e,i,r){this._checkLoaded();const n=this.getLayer(e);n?n.minzoom===i&&n.maxzoom===r||(null!=i&&(n.minzoom=i),null!=r&&(n.maxzoom=r),this._updateLayer(n)):this.fire(new t.ErrorEvent(new Error(`Cannot set the zoom range of non-existing layer "${e}".`)))}setFilter(e,i,r={}){this._checkLoaded();const s=this.getLayer(e);if(s){if(!n(s.filter,i))return null==i?(s.filter=void 0,void this._updateLayer(s)):void(this._validate(t.validateStyle.filter,`layers.${s.id}.filter`,i,null,r)||(s.filter=t.clone$1(i),this._updateLayer(s)))}else this.fire(new t.ErrorEvent(new Error(`Cannot filter non-existing layer "${e}".`)))}getFilter(e){return t.clone$1(this.getLayer(e).filter)}setLayoutProperty(e,i,r,s={}){this._checkLoaded();const o=this.getLayer(e);o?n(o.getLayoutProperty(i),r)||(o.setLayoutProperty(i,r,s),this._updateLayer(o)):this.fire(new t.ErrorEvent(new Error(`Cannot style non-existing layer "${e}".`)))}getLayoutProperty(e,i){const r=this.getLayer(e);if(r)return r.getLayoutProperty(i);this.fire(new t.ErrorEvent(new Error(`Cannot get style of non-existing layer "${e}".`)))}setPaintProperty(e,i,r,s={}){this._checkLoaded();const o=this.getLayer(e);o?n(o.getPaintProperty(i),r)||(o.setPaintProperty(i,r,s)&&this._updateLayer(o),this._changed=!0,this._updatedPaintProps[e]=!0):this.fire(new t.ErrorEvent(new Error(`Cannot style non-existing layer "${e}".`)))}getPaintProperty(t,e){return this.getLayer(t).getPaintProperty(e)}setFeatureState(e,i){this._checkLoaded();const r=e.source,n=e.sourceLayer,s=this.sourceCaches[r];if(void 0===s)return void this.fire(new t.ErrorEvent(new Error(`The source '${r}' does not exist in the map's style.`)));const o=s.getSource().type;"geojson"===o&&n?this.fire(new t.ErrorEvent(new Error("GeoJSON sources cannot have a sourceLayer parameter."))):"vector"!==o||n?(void 0===e.id&&this.fire(new t.ErrorEvent(new Error("The feature id parameter must be provided."))),s.setFeatureState(n,e.id,i)):this.fire(new t.ErrorEvent(new Error("The sourceLayer parameter must be provided for vector source types.")))}removeFeatureState(e,i){this._checkLoaded();const r=e.source,n=this.sourceCaches[r];if(void 0===n)return void this.fire(new t.ErrorEvent(new Error(`The source '${r}' does not exist in the map's style.`)));const s=n.getSource().type,o="vector"===s?e.sourceLayer:void 0;"vector"!==s||o?i&&"string"!=typeof e.id&&"number"!=typeof e.id?this.fire(new t.ErrorEvent(new Error("A feature id is required to remove its specific state property."))):n.removeFeatureState(o,e.id,i):this.fire(new t.ErrorEvent(new Error("The sourceLayer parameter must be provided for vector source types.")))}getFeatureState(e){this._checkLoaded();const i=e.source,r=e.sourceLayer,n=this.sourceCaches[i];if(void 0!==n)return"vector"!==n.getSource().type||r?(void 0===e.id&&this.fire(new t.ErrorEvent(new Error("The feature id parameter must be provided."))),n.getFeatureState(r,e.id)):void this.fire(new t.ErrorEvent(new Error("The sourceLayer parameter must be provided for vector source types.")));this.fire(new t.ErrorEvent(new Error(`The source '${i}' does not exist in the map's style.`)))}getTransition(){return t.extend({duration:300,delay:0},this.stylesheet&&this.stylesheet.transition)}serialize(){return t.filterObject({version:this.stylesheet.version,name:this.stylesheet.name,metadata:this.stylesheet.metadata,light:this.stylesheet.light,center:this.stylesheet.center,zoom:this.stylesheet.zoom,bearing:this.stylesheet.bearing,pitch:this.stylesheet.pitch,sprite:this.stylesheet.sprite,glyphs:this.stylesheet.glyphs,transition:this.stylesheet.transition,sources:t.mapObject(this.sourceCaches,(t=>t.serialize())),layers:this._serializeLayers(this._order)},(t=>void 0!==t))}_updateLayer(t){this._updatedLayers[t.id]=!0,t.source&&!this._updatedSources[t.source]&&"raster"!==this.sourceCaches[t.source].getSource().type&&(this._updatedSources[t.source]="reload",this.sourceCaches[t.source].pause()),this._changed=!0}_flattenAndSortRenderedFeatures(t){const e=t=>"fill-extrusion"===this._layers[t].type,i={},r=[];for(let n=this._order.length-1;n>=0;n--){const s=this._order[n];if(e(s)){i[s]=n;for(const e of t){const t=e[s];if(t)for(const e of t)r.push(e)}}}r.sort(((t,e)=>e.intersectionZ-t.intersectionZ));const n=[];for(let s=this._order.length-1;s>=0;s--){const o=this._order[s];if(e(o))for(let t=r.length-1;t>=0;t--){const e=r[t].feature;if(i[e.layer.id]{const r=i.featureSortOrder;if(r){const i=r.indexOf(t.featureIndex);return r.indexOf(e.featureIndex)-i}return e.featureIndex-t.featureIndex}));for(const t of n)e.push(t)}}for(const e in a)a[e].forEach((r=>{const n=r.feature,s=i[t[e].source].getFeatureState(n.layer["source-layer"],n.id);n.source=n.layer.source,n.layer["source-layer"]&&(n.sourceLayer=n.layer["source-layer"]),n.state=s}));return a}(this._layers,this._serializedLayers,this.sourceCaches,e,i,this.placement.collisionIndex,this.placement.retainedQueryData)),this._flattenAndSortRenderedFeatures(s)}querySourceFeatures(e,i){i&&i.filter&&this._validate(t.validateStyle.filter,"querySourceFeatures.filter",i.filter,null,i);const r=this.sourceCaches[e];return r?function(t,e){const i=t.getRenderableIds().map((e=>t.getTileByID(e))),r=[],n={};for(let t=0;tt.getTileByID(e))).sort(((t,e)=>e.tileID.overscaledZ-t.tileID.overscaledZ||(t.tileID.isLessThan(e.tileID)?-1:1)))}const r=this.crossTileSymbolIndex.addLayer(i,l[i.source],e.center.lng);o=o||r}if(this.crossTileSymbolIndex.pruneUnusedLayers(this._order),((s=s||this._layerOrderChanged||0===r)||!this.pauseablePlacement||this.pauseablePlacement.isDone()&&!this.placement.stillRecent(t.exported.now(),e.zoom))&&(this.pauseablePlacement=new Gt(e,this.terrain,this._order,s,i,r,n,this.placement),this._layerOrderChanged=!1),this.pauseablePlacement.isDone()?this.placement.setStale():(this.pauseablePlacement.continuePlacement(this._order,this._layers,l),this.pauseablePlacement.isDone()&&(this.placement=this.pauseablePlacement.commit(t.exported.now()),a=!0),o&&this.pauseablePlacement.placement.setStale()),a||o)for(const t of this._order){const e=this._layers[t];"symbol"===e.type&&this.placement.updateLayerOpacities(e,l[e.source])}return!this.pauseablePlacement.isDone()||this.placement.hasTransitions(t.exported.now())}_releaseSymbolFadeTiles(){for(const t in this.sourceCaches)this.sourceCaches[t].releaseSymbolFadeTiles()}getImages(t,e,i){this.imageManager.getImages(e.icons,i),this._updateTilesForChangedImages();const r=this.sourceCaches[e.source];r&&r.setDependencies(e.tileID.key,e.type,e.icons)}getGlyphs(t,e,i){this.glyphManager.getGlyphs(e.stacks,i)}getResource(e,i,r){return t.makeRequest(i,r)}}ee.getSourceType=function(t){return k[t]},ee.setSourceType=function(t,e){k[t]=e},ee.registerForPluginStateChange=t.registerForPluginStateChange;var ie="attribute vec2 a_pos;uniform mat4 u_matrix;varying vec2 v_texture_pos;varying float v_depth;void main() {v_texture_pos=a_pos/8192.0;gl_Position=u_matrix*vec4(a_pos,get_elevation(a_pos),1.0);v_depth=gl_Position.z/gl_Position.w;}";const re={prelude:ne("#ifdef GL_ES\nprecision mediump float;\n#else\n#if !defined(lowp)\n#define lowp\n#endif\n#if !defined(mediump)\n#define mediump\n#endif\n#if !defined(highp)\n#define highp\n#endif\n#endif","#ifdef GL_ES\nprecision highp float;\n#else\n#if !defined(lowp)\n#define lowp\n#endif\n#if !defined(mediump)\n#define mediump\n#endif\n#if !defined(highp)\n#define highp\n#endif\n#endif\nvec2 unpack_float(const float packedValue) {int packedIntValue=int(packedValue);int v0=packedIntValue/256;return vec2(v0,packedIntValue-v0*256);}vec2 unpack_opacity(const float packedOpacity) {int intOpacity=int(packedOpacity)/2;return vec2(float(intOpacity)/127.0,mod(packedOpacity,2.0));}vec4 decode_color(const vec2 encodedColor) {return vec4(unpack_float(encodedColor[0])/255.0,unpack_float(encodedColor[1])/255.0\n);}float unpack_mix_vec2(const vec2 packedValue,const float t) {return mix(packedValue[0],packedValue[1],t);}vec4 unpack_mix_color(const vec4 packedColors,const float t) {vec4 minColor=decode_color(vec2(packedColors[0],packedColors[1]));vec4 maxColor=decode_color(vec2(packedColors[2],packedColors[3]));return mix(minColor,maxColor,t);}vec2 get_pattern_pos(const vec2 pixel_coord_upper,const vec2 pixel_coord_lower,const vec2 pattern_size,const float tile_units_to_pixels,const vec2 pos) {vec2 offset=mod(mod(mod(pixel_coord_upper,pattern_size)*256.0,pattern_size)*256.0+pixel_coord_lower,pattern_size);return (tile_units_to_pixels*pos+offset)/pattern_size;}\n#ifdef TERRAIN3D\nuniform sampler2D u_terrain;uniform float u_terrain_dim;uniform mat4 u_terrain_matrix;uniform vec4 u_terrain_unpack;uniform float u_terrain_offset;uniform float u_terrain_exaggeration;uniform highp sampler2D u_depth;\n#endif\nconst highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitShifts=vec4(1.)/bitSh;highp float unpack(highp vec4 color) {return dot(color,bitShifts);}highp float depthOpacity(vec3 frag) {\n#ifdef TERRAIN3D\nhighp float d=unpack(texture2D(u_depth,frag.xy*0.5+0.5))+0.0001-frag.z;return 1.0-max(0.0,min(1.0,-d*500.0));\n#else\nreturn 1.0;\n#endif\n}float calculate_visibility(vec4 pos) {\n#ifdef TERRAIN3D\nvec3 frag=pos.xyz/pos.w;highp float d=depthOpacity(frag);if (d > 0.95) return 1.0;return (d+depthOpacity(frag+vec3(0.0,0.01,0.0)))/2.0;\n#else\nreturn 1.0;\n#endif\n}float ele(vec2 pos) {\n#ifdef TERRAIN3D\nvec4 rgb=(texture2D(u_terrain,pos)*255.0)*u_terrain_unpack;return rgb.r+rgb.g+rgb.b-u_terrain_unpack.a;\n#else\nreturn 0.0;\n#endif\n}float get_elevation(vec2 pos) {\n#ifdef TERRAIN3D\nvec2 coord=(u_terrain_matrix*vec4(pos,0.0,1.0)).xy*u_terrain_dim+1.0;vec2 f=fract(coord);vec2 c=(floor(coord)+0.5)/(u_terrain_dim+2.0);float d=1.0/(u_terrain_dim+2.0);float tl=ele(c);float tr=ele(c+vec2(d,0.0));float bl=ele(c+vec2(0.0,d));float br=ele(c+vec2(d,d));float elevation=mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);return (elevation+u_terrain_offset)*u_terrain_exaggeration;\n#else\nreturn 0.0;\n#endif\n}"),background:ne("uniform vec4 u_color;uniform float u_opacity;void main() {gl_FragColor=u_color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","attribute vec2 a_pos;uniform mat4 u_matrix;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);}"),backgroundPattern:ne("uniform vec2 u_pattern_tl_a;uniform vec2 u_pattern_br_a;uniform vec2 u_pattern_tl_b;uniform vec2 u_pattern_br_b;uniform vec2 u_texsize;uniform float u_mix;uniform float u_opacity;uniform sampler2D u_image;varying vec2 v_pos_a;varying vec2 v_pos_b;void main() {vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(u_pattern_tl_a/u_texsize,u_pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(u_pattern_tl_b/u_texsize,u_pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);gl_FragColor=mix(color1,color2,u_mix)*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_pattern_size_a;uniform vec2 u_pattern_size_b;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_scale_a;uniform float u_scale_b;uniform float u_tile_units_to_pixels;attribute vec2 a_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_a*u_pattern_size_a,u_tile_units_to_pixels,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_b*u_pattern_size_b,u_tile_units_to_pixels,a_pos);}"),circle:ne("varying vec3 v_data;varying float v_visibility;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define mediump float radius\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define highp vec4 stroke_color\n#pragma mapbox: define mediump float stroke_width\n#pragma mapbox: define lowp float stroke_opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize mediump float radius\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize highp vec4 stroke_color\n#pragma mapbox: initialize mediump float stroke_width\n#pragma mapbox: initialize lowp float stroke_opacity\nvec2 extrude=v_data.xy;float extrude_length=length(extrude);lowp float antialiasblur=v_data.z;float antialiased_blur=-max(blur,antialiasblur);float opacity_t=smoothstep(0.0,antialiased_blur,extrude_length-1.0);float color_t=stroke_width < 0.01 ? 0.0 : smoothstep(antialiased_blur,0.0,extrude_length-radius/(radius+stroke_width));gl_FragColor=v_visibility*opacity_t*mix(color*opacity,stroke_color*stroke_opacity,color_t);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform bool u_scale_with_map;uniform bool u_pitch_with_map;uniform vec2 u_extrude_scale;uniform lowp float u_device_pixel_ratio;uniform highp float u_camera_to_center_distance;attribute vec2 a_pos;varying vec3 v_data;varying float v_visibility;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define mediump float radius\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define highp vec4 stroke_color\n#pragma mapbox: define mediump float stroke_width\n#pragma mapbox: define lowp float stroke_opacity\nvoid main(void) {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize mediump float radius\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize highp vec4 stroke_color\n#pragma mapbox: initialize mediump float stroke_width\n#pragma mapbox: initialize lowp float stroke_opacity\nvec2 extrude=vec2(mod(a_pos,2.0)*2.0-1.0);vec2 circle_center=floor(a_pos*0.5);float ele=get_elevation(circle_center);v_visibility=calculate_visibility(u_matrix*vec4(circle_center,ele,1.0));if (u_pitch_with_map) {vec2 corner_position=circle_center;if (u_scale_with_map) {corner_position+=extrude*(radius+stroke_width)*u_extrude_scale;} else {vec4 projected_center=u_matrix*vec4(circle_center,0,1);corner_position+=extrude*(radius+stroke_width)*u_extrude_scale*(projected_center.w/u_camera_to_center_distance);}gl_Position=u_matrix*vec4(corner_position,ele,1);} else {gl_Position=u_matrix*vec4(circle_center,ele,1);if (u_scale_with_map) {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*u_camera_to_center_distance;} else {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*gl_Position.w;}}lowp float antialiasblur=1.0/u_device_pixel_ratio/(radius+stroke_width);v_data=vec3(extrude.x,extrude.y,antialiasblur);}"),clippingMask:ne("void main() {gl_FragColor=vec4(1.0);}","attribute vec2 a_pos;uniform mat4 u_matrix;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);}"),heatmap:ne("uniform highp float u_intensity;varying vec2 v_extrude;\n#pragma mapbox: define highp float weight\n#define GAUSS_COEF 0.3989422804014327\nvoid main() {\n#pragma mapbox: initialize highp float weight\nfloat d=-0.5*3.0*3.0*dot(v_extrude,v_extrude);float val=weight*u_intensity*GAUSS_COEF*exp(d);gl_FragColor=vec4(val,1.0,1.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform float u_extrude_scale;uniform float u_opacity;uniform float u_intensity;attribute vec2 a_pos;varying vec2 v_extrude;\n#pragma mapbox: define highp float weight\n#pragma mapbox: define mediump float radius\nconst highp float ZERO=1.0/255.0/16.0;\n#define GAUSS_COEF 0.3989422804014327\nvoid main(void) {\n#pragma mapbox: initialize highp float weight\n#pragma mapbox: initialize mediump float radius\nvec2 unscaled_extrude=vec2(mod(a_pos,2.0)*2.0-1.0);float S=sqrt(-2.0*log(ZERO/weight/u_intensity/GAUSS_COEF))/3.0;v_extrude=S*unscaled_extrude;vec2 extrude=v_extrude*radius*u_extrude_scale;vec4 pos=vec4(floor(a_pos*0.5)+extrude,0,1);gl_Position=u_matrix*pos;}"),heatmapTexture:ne("uniform sampler2D u_image;uniform sampler2D u_color_ramp;uniform float u_opacity;varying vec2 v_pos;void main() {float t=texture2D(u_image,v_pos).r;vec4 color=texture2D(u_color_ramp,vec2(t,0.5));gl_FragColor=color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(0.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_world;attribute vec2 a_pos;varying vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos*u_world,0,1);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}"),collisionBox:ne("varying float v_placed;varying float v_notUsed;void main() {float alpha=0.5;gl_FragColor=vec4(1.0,0.0,0.0,1.0)*alpha;if (v_placed > 0.5) {gl_FragColor=vec4(0.0,0.0,1.0,0.5)*alpha;}if (v_notUsed > 0.5) {gl_FragColor*=.1;}}","attribute vec2 a_pos;attribute vec2 a_anchor_pos;attribute vec2 a_extrude;attribute vec2 a_placed;attribute vec2 a_shift;uniform mat4 u_matrix;uniform vec2 u_extrude_scale;uniform float u_camera_to_center_distance;varying float v_placed;varying float v_notUsed;void main() {vec4 projectedPoint=u_matrix*vec4(a_anchor_pos,0,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float collision_perspective_ratio=clamp(0.5+0.5*(u_camera_to_center_distance/camera_to_anchor_distance),0.0,4.0);gl_Position=u_matrix*vec4(a_pos,get_elevation(a_pos),1.0);gl_Position.xy+=(a_extrude+a_shift)*u_extrude_scale*gl_Position.w*collision_perspective_ratio;v_placed=a_placed.x;v_notUsed=a_placed.y;}"),collisionCircle:ne("varying float v_radius;varying vec2 v_extrude;varying float v_perspective_ratio;varying float v_collision;void main() {float alpha=0.5*min(v_perspective_ratio,1.0);float stroke_radius=0.9*max(v_perspective_ratio,1.0);float distance_to_center=length(v_extrude);float distance_to_edge=abs(distance_to_center-v_radius);float opacity_t=smoothstep(-stroke_radius,0.0,-distance_to_edge);vec4 color=mix(vec4(0.0,0.0,1.0,0.5),vec4(1.0,0.0,0.0,1.0),v_collision);gl_FragColor=color*alpha*opacity_t;}","attribute vec2 a_pos;attribute float a_radius;attribute vec2 a_flags;uniform mat4 u_matrix;uniform mat4 u_inv_matrix;uniform vec2 u_viewport_size;uniform float u_camera_to_center_distance;varying float v_radius;varying vec2 v_extrude;varying float v_perspective_ratio;varying float v_collision;vec3 toTilePosition(vec2 screenPos) {vec4 rayStart=u_inv_matrix*vec4(screenPos,-1.0,1.0);vec4 rayEnd =u_inv_matrix*vec4(screenPos, 1.0,1.0);rayStart.xyz/=rayStart.w;rayEnd.xyz /=rayEnd.w;highp float t=(0.0-rayStart.z)/(rayEnd.z-rayStart.z);return mix(rayStart.xyz,rayEnd.xyz,t);}void main() {vec2 quadCenterPos=a_pos;float radius=a_radius;float collision=a_flags.x;float vertexIdx=a_flags.y;vec2 quadVertexOffset=vec2(mix(-1.0,1.0,float(vertexIdx >=2.0)),mix(-1.0,1.0,float(vertexIdx >=1.0 && vertexIdx <=2.0)));vec2 quadVertexExtent=quadVertexOffset*radius;vec3 tilePos=toTilePosition(quadCenterPos);vec4 clipPos=u_matrix*vec4(tilePos,1.0);highp float camera_to_anchor_distance=clipPos.w;highp float collision_perspective_ratio=clamp(0.5+0.5*(u_camera_to_center_distance/camera_to_anchor_distance),0.0,4.0);float padding_factor=1.2;v_radius=radius;v_extrude=quadVertexExtent*padding_factor;v_perspective_ratio=collision_perspective_ratio;v_collision=collision;gl_Position=vec4(clipPos.xyz/clipPos.w,1.0)+vec4(quadVertexExtent*padding_factor/u_viewport_size*2.0,0.0,0.0);}"),debug:ne("uniform highp vec4 u_color;uniform sampler2D u_overlay;varying vec2 v_uv;void main() {vec4 overlay_color=texture2D(u_overlay,v_uv);gl_FragColor=mix(u_color,overlay_color,overlay_color.a);}","attribute vec2 a_pos;varying vec2 v_uv;uniform mat4 u_matrix;uniform float u_overlay_scale;void main() {v_uv=a_pos/8192.0;gl_Position=u_matrix*vec4(a_pos*u_overlay_scale,get_elevation(a_pos),1);}"),fill:ne("#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float opacity\ngl_FragColor=color*opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","attribute vec2 a_pos;uniform mat4 u_matrix;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float opacity\ngl_Position=u_matrix*vec4(a_pos,0,1);}"),fillOutline:ne("varying vec2 v_pos;\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 outline_color\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);gl_FragColor=outline_color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","attribute vec2 a_pos;uniform mat4 u_matrix;uniform vec2 u_world;varying vec2 v_pos;\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 outline_color\n#pragma mapbox: initialize lowp float opacity\ngl_Position=u_matrix*vec4(a_pos,0,1);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;}"),fillOutlinePattern:ne("uniform vec2 u_texsize;uniform sampler2D u_image;uniform float u_fade;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec2 v_pos;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);float dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);gl_FragColor=mix(color1,color2,u_fade)*alpha*opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_world;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;attribute vec2 a_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec2 v_pos;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;gl_Position=u_matrix*vec4(a_pos,0,1);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;}"),fillPattern:ne("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;varying vec2 v_pos_a;varying vec2 v_pos_b;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);gl_FragColor=mix(color1,color2,u_fade)*opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;attribute vec2 a_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;gl_Position=u_matrix*vec4(a_pos,0,1);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileZoomRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileZoomRatio,a_pos);}"),fillExtrusion:ne("varying vec4 v_color;void main() {gl_FragColor=v_color;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp float u_lightintensity;uniform float u_vertical_gradient;uniform lowp float u_opacity;attribute vec2 a_pos;attribute vec4 a_normal_ed;\n#ifdef TERRAIN3D\nattribute vec2 a_centroid;\n#endif\nvarying vec4 v_color;\n#pragma mapbox: define highp float base\n#pragma mapbox: define highp float height\n#pragma mapbox: define highp vec4 color\nvoid main() {\n#pragma mapbox: initialize highp float base\n#pragma mapbox: initialize highp float height\n#pragma mapbox: initialize highp vec4 color\nvec3 normal=a_normal_ed.xyz;\n#ifdef TERRAIN3D\nfloat baseDelta=10.0;float ele=get_elevation(a_centroid);\n#else\nfloat baseDelta=0.0;float ele=0.0;\n#endif\nbase=max(0.0,ele+base-baseDelta);height=max(0.0,ele+height);float t=mod(normal.x,2.0);gl_Position=u_matrix*vec4(a_pos,t > 0.0 ? height : base,1);float colorvalue=color.r*0.2126+color.g*0.7152+color.b*0.0722;v_color=vec4(0.0,0.0,0.0,1.0);vec4 ambientlight=vec4(0.03,0.03,0.03,1.0);color+=ambientlight;float directional=clamp(dot(normal/16384.0,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((1.0-colorvalue+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_color.r+=clamp(color.r*directional*u_lightcolor.r,mix(0.0,0.3,1.0-u_lightcolor.r),1.0);v_color.g+=clamp(color.g*directional*u_lightcolor.g,mix(0.0,0.3,1.0-u_lightcolor.g),1.0);v_color.b+=clamp(color.b*directional*u_lightcolor.b,mix(0.0,0.3,1.0-u_lightcolor.b),1.0);v_color*=u_opacity;}"),fillExtrusionPattern:ne("uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec4 v_lighting;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float base\n#pragma mapbox: initialize lowp float height\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);vec4 mixedColor=mix(color1,color2,u_fade);gl_FragColor=mixedColor*v_lighting;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_height_factor;uniform vec3 u_scale;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp float u_lightintensity;attribute vec2 a_pos;attribute vec4 a_normal_ed;\n#ifdef TERRAIN3D\nattribute vec2 a_centroid;\n#endif\nvarying vec2 v_pos_a;varying vec2 v_pos_b;varying vec4 v_lighting;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float base\n#pragma mapbox: initialize lowp float height\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec3 normal=a_normal_ed.xyz;float edgedistance=a_normal_ed.w;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;\n#ifdef TERRAIN3D\nfloat baseDelta=10.0;float ele=get_elevation(a_centroid);\n#else\nfloat baseDelta=0.0;float ele=0.0;\n#endif\nbase=max(0.0,ele+base-baseDelta);height=max(0.0,ele+height);float t=mod(normal.x,2.0);float z=t > 0.0 ? height : base;gl_Position=u_matrix*vec4(a_pos,z,1);vec2 pos=normal.x==1.0 && normal.y==0.0 && normal.z==16384.0\n? a_pos\n: vec2(edgedistance,z*u_height_factor);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,pos);v_lighting=vec4(0.0,0.0,0.0,1.0);float directional=clamp(dot(normal/16383.0,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((0.5+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_lighting.rgb+=clamp(directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_lighting*=u_opacity;}"),hillshadePrepare:ne("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;varying vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(vec2 coord,float bias) {vec4 data=texture2D(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack)/4.0;}void main() {vec2 epsilon=1.0/u_dimension;float a=getElevation(v_pos+vec2(-epsilon.x,-epsilon.y),0.0);float b=getElevation(v_pos+vec2(0,-epsilon.y),0.0);float c=getElevation(v_pos+vec2(epsilon.x,-epsilon.y),0.0);float d=getElevation(v_pos+vec2(-epsilon.x,0),0.0);float e=getElevation(v_pos,0.0);float f=getElevation(v_pos+vec2(epsilon.x,0),0.0);float g=getElevation(v_pos+vec2(-epsilon.x,epsilon.y),0.0);float h=getElevation(v_pos+vec2(0,epsilon.y),0.0);float i=getElevation(v_pos+vec2(epsilon.x,epsilon.y),0.0);float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2((c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c))/pow(2.0,exaggeration+(19.2562-u_zoom));gl_FragColor=clamp(vec4(deriv.x/2.0+0.5,deriv.y/2.0+0.5,1.0,1.0),0.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_dimension;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}"),hillshade:ne("uniform sampler2D u_image;varying vec2 v_pos;uniform vec2 u_latrange;uniform vec2 u_light;uniform vec4 u_shadow;uniform vec4 u_highlight;uniform vec4 u_accent;\n#define PI 3.141592653589793\nvoid main() {vec4 pixel=texture2D(u_image,v_pos);vec2 deriv=((pixel.rg*2.0)-1.0);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));float slope=atan(1.25*length(deriv)/scaleFactor);float aspect=deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);float intensity=u_light.x;float azimuth=u_light.y+PI;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadow,u_highlight,shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);gl_FragColor=accent_color*(1.0-shade_color.a)+shade_color;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);v_pos=a_texture_pos/8192.0;}"),line:ne("uniform lowp float u_device_pixel_ratio;varying vec2 v_width2;varying vec2 v_normal;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);gl_FragColor=color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\nattribute vec2 a_pos_normal;attribute vec4 a_data;uniform mat4 u_matrix;uniform mediump float u_ratio;uniform vec2 u_units_to_pixels;uniform lowp float u_device_pixel_ratio;varying vec2 v_normal;varying vec2 v_width2;varying float v_gamma_scale;varying highp float v_linesofar;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;v_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*2.0;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_width2=vec2(outset,inset);}"),lineGradient:ne("uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;varying vec2 v_width2;varying vec2 v_normal;varying float v_gamma_scale;varying highp vec2 v_uv;\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture2D(u_image,v_uv);gl_FragColor=color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\nattribute vec2 a_pos_normal;attribute vec4 a_data;attribute float a_uv_x;attribute float a_split_index;uniform mat4 u_matrix;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;varying vec2 v_normal;varying vec2 v_width2;varying float v_gamma_scale;varying highp vec2 v_uv;\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;highp float texel_height=1.0/u_image_height;highp float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_width2=vec2(outset,inset);}"),linePattern:ne("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform lowp float u_device_pixel_ratio;uniform vec2 u_texsize;uniform float u_fade;uniform mediump vec3 u_scale;uniform sampler2D u_image;varying vec2 v_normal;varying vec2 v_width2;varying float v_linesofar;varying float v_gamma_scale;varying float v_width;\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;vec2 pattern_size_a=vec2(display_size_a.x*fromScale/tileZoomRatio,display_size_a.y);vec2 pattern_size_b=vec2(display_size_b.x*toScale/tileZoomRatio,display_size_b.y);float aspect_a=display_size_a.y/v_width;float aspect_b=display_size_b.y/v_width;float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float x_a=mod(v_linesofar/pattern_size_a.x*aspect_a,1.0);float x_b=mod(v_linesofar/pattern_size_b.x*aspect_b,1.0);float y=0.5*v_normal.y+0.5;vec2 texel_size=1.0/u_texsize;vec2 pos_a=mix(pattern_tl_a*texel_size-texel_size,pattern_br_a*texel_size+texel_size,vec2(x_a,y));vec2 pos_b=mix(pattern_tl_b*texel_size-texel_size,pattern_br_b*texel_size+texel_size,vec2(x_b,y));vec4 color=mix(texture2D(u_image,pos_a),texture2D(u_image,pos_b),u_fade);gl_FragColor=color*alpha*opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nattribute vec2 a_pos_normal;attribute vec4 a_data;uniform mat4 u_matrix;uniform vec2 u_units_to_pixels;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;varying vec2 v_normal;varying vec2 v_width2;varying float v_linesofar;varying float v_gamma_scale;varying float v_width;\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_linesofar=a_linesofar;v_width2=vec2(outset,inset);v_width=floorwidth;}"),lineSDF:ne("uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform float u_sdfgamma;uniform float u_mix;varying vec2 v_normal;varying vec2 v_width2;varying vec2 v_tex_a;varying vec2 v_tex_b;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture2D(u_image,v_tex_a).a;float sdfdist_b=texture2D(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);alpha*=smoothstep(0.5-u_sdfgamma/floorwidth,0.5+u_sdfgamma/floorwidth,sdfdist);gl_FragColor=color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nattribute vec2 a_pos_normal;attribute vec4 a_data;uniform mat4 u_matrix;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_patternscale_a;uniform float u_tex_y_a;uniform vec2 u_patternscale_b;uniform float u_tex_y_b;uniform vec2 u_units_to_pixels;varying vec2 v_normal;varying vec2 v_width2;varying vec2 v_tex_a;varying vec2 v_tex_b;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_tex_a=vec2(a_linesofar*u_patternscale_a.x/floorwidth,normal.y*u_patternscale_a.y+u_tex_y_a);v_tex_b=vec2(a_linesofar*u_patternscale_b.x/floorwidth,normal.y*u_patternscale_b.y+u_tex_y_b);v_width2=vec2(outset,inset);}"),raster:ne("uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;varying vec2 v_pos0;varying vec2 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture2D(u_image0,v_pos0);vec4 color1=texture2D(u_image1,v_pos1);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(dot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);gl_FragColor=vec4(mix(u_high_vec,u_low_vec,rgb)*color.a,color.a);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","uniform mat4 u_matrix;uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos0;varying vec2 v_pos1;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);v_pos0=(((a_texture_pos/8192.0)-0.5)/u_buffer_scale )+0.5;v_pos1=(v_pos0*u_scale_parent)+u_tl_parent;}"),symbolIcon:ne("uniform sampler2D u_texture;varying vec2 v_tex;varying float v_fade_opacity;\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\nlowp float alpha=opacity*v_fade_opacity;gl_FragColor=texture2D(u_texture,v_tex)*alpha;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","const float PI=3.141592653589793;attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec4 a_pixeloffset;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;varying vec2 v_tex;varying float v_fade_opacity;\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec4 projectedPoint=u_matrix*vec4(a_pos,ele,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),ele,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,ele,1.0);float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;gl_Position=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0),z,1.0);v_tex=a_tex/u_texsize;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));}"),symbolSDF:ne("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;varying vec2 v_data0;varying vec3 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float fade_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;lowp vec4 color=fill_color;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;if (u_is_halo) {color=halo_color;gamma=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);buff=(6.0-halo_width/fontScale)/SDF_PX;}lowp float dist=texture2D(u_texture,tex).a;highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);gl_FragColor=color*(alpha*opacity*fade_opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","const float PI=3.141592653589793;attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec4 a_pixeloffset;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;varying vec2 v_data0;varying vec3 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec4 projectedPoint=u_matrix*vec4(a_pos,ele,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),ele,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,ele,1.0);float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;gl_Position=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale+a_pxoffset),z,1.0);float gamma_scale=gl_Position.w;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,interpolated_fade_opacity);}"),symbolTextAndIcon:ne("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;varying vec4 v_data0;varying vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nfloat fade_opacity=v_data1[2];if (v_data1.w==ICON) {vec2 tex_icon=v_data0.zw;lowp float alpha=opacity*fade_opacity;gl_FragColor=texture2D(u_texture_icon,tex_icon)*alpha;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;lowp vec4 color=fill_color;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;if (u_is_halo) {color=halo_color;gamma=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);buff=(6.0-halo_width/fontScale)/SDF_PX;}lowp float dist=texture2D(u_texture,tex).a;highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);gl_FragColor=color*(alpha*opacity*fade_opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}","const float PI=3.141592653589793;attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;varying vec4 v_data0;varying vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);float is_sdf=a_size[0]-2.0*a_size_min;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec4 projectedPoint=u_matrix*vec4(a_pos,ele,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),ele,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,ele,1.0);float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;gl_Position=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale),z,1.0);float gamma_scale=gl_Position.w;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec4(gamma_scale,size,interpolated_fade_opacity,is_sdf);}"),terrain:ne("uniform sampler2D u_texture;varying vec2 v_texture_pos;void main() {gl_FragColor=texture2D(u_texture,v_texture_pos);}",ie),terrainDepth:ne("varying float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {gl_FragColor=pack(v_depth);}",ie),terrainCoords:ne("precision mediump float;uniform sampler2D u_texture;uniform float u_terrain_coords_id;varying vec2 v_texture_pos;void main() {vec4 rgba=texture2D(u_texture,v_texture_pos);gl_FragColor=vec4(rgba.r,rgba.g,rgba.b,u_terrain_coords_id);}",ie)};function ne(t,e){const i=/#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g,r=e.match(/attribute ([\w]+) ([\w]+)/g),n=t.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g),s=e.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g),o=s?s.concat(n):n,a={};return{fragmentSource:t=t.replace(i,((t,e,i,r,n)=>(a[n]=!0,"define"===e?`\n#ifndef HAS_UNIFORM_u_${n}\nvarying ${i} ${r} ${n};\n#else\nuniform ${i} ${r} u_${n};\n#endif\n`:`\n#ifdef HAS_UNIFORM_u_${n}\n ${i} ${r} ${n} = u_${n};\n#endif\n`))),vertexSource:e=e.replace(i,((t,e,i,r,n)=>{const s="float"===r?"vec2":"vec4",o=n.match(/color/)?"color":s;return a[n]?"define"===e?`\n#ifndef HAS_UNIFORM_u_${n}\nuniform lowp float u_${n}_t;\nattribute ${i} ${s} a_${n};\nvarying ${i} ${r} ${n};\n#else\nuniform ${i} ${r} u_${n};\n#endif\n`:"vec4"===o?`\n#ifndef HAS_UNIFORM_u_${n}\n ${n} = a_${n};\n#else\n ${i} ${r} ${n} = u_${n};\n#endif\n`:`\n#ifndef HAS_UNIFORM_u_${n}\n ${n} = unpack_mix_${o}(a_${n}, u_${n}_t);\n#else\n ${i} ${r} ${n} = u_${n};\n#endif\n`:"define"===e?`\n#ifndef HAS_UNIFORM_u_${n}\nuniform lowp float u_${n}_t;\nattribute ${i} ${s} a_${n};\n#else\nuniform ${i} ${r} u_${n};\n#endif\n`:"vec4"===o?`\n#ifndef HAS_UNIFORM_u_${n}\n ${i} ${r} ${n} = a_${n};\n#else\n ${i} ${r} ${n} = u_${n};\n#endif\n`:`\n#ifndef HAS_UNIFORM_u_${n}\n ${i} ${r} ${n} = unpack_mix_${o}(a_${n}, u_${n}_t);\n#else\n ${i} ${r} ${n} = u_${n};\n#endif\n`})),staticAttributes:r,staticUniforms:o}}class se{constructor(){this.boundProgram=null,this.boundLayoutVertexBuffer=null,this.boundPaintVertexBuffers=[],this.boundIndexBuffer=null,this.boundVertexOffset=null,this.boundDynamicVertexBuffer=null,this.vao=null}bind(t,e,i,r,n,s,o,a,l){this.context=t;let c=this.boundPaintVertexBuffers.length!==r.length;for(let t=0;!c&&t({u_depth:new t.Uniform1i(e,i.u_depth),u_terrain:new t.Uniform1i(e,i.u_terrain),u_terrain_dim:new t.Uniform1f(e,i.u_terrain_dim),u_terrain_matrix:new t.UniformMatrix4f(e,i.u_terrain_matrix),u_terrain_unpack:new t.Uniform4f(e,i.u_terrain_unpack),u_terrain_offset:new t.Uniform1f(e,i.u_terrain_offset),u_terrain_exaggeration:new t.Uniform1f(e,i.u_terrain_exaggeration)}))(e,w),this.binderUniforms=n?n.getUniforms(e,w):[]}draw(t,e,i,r,n,s,o,a,l,c,h,u,p,d,m,f,g,_){const y=t.gl;if(this.failedToCreate)return;if(t.program.set(this.program),t.setDepthMode(i),t.setStencilMode(r),t.setColorMode(n),t.setCullFace(s),a){t.activeTexture.set(y.TEXTURE2),y.bindTexture(y.TEXTURE_2D,a.depthTexture),t.activeTexture.set(y.TEXTURE3),y.bindTexture(y.TEXTURE_2D,a.texture);for(const t in this.terrainUniforms)this.terrainUniforms[t].set(a[t])}for(const t in this.fixedUniforms)this.fixedUniforms[t].set(o[t]);m&&m.setUniforms(t,this.binderUniforms,p,{zoom:d});const x={[y.LINES]:2,[y.TRIANGLES]:3,[y.LINE_STRIP]:1}[e];for(const i of u.get()){const r=i.vaos||(i.vaos={});(r[l]||(r[l]=new se)).bind(t,this,c,m?m.getPaintVertexBuffers():[],h,i.vertexOffset,f,g,_),y.drawElements(e,i.primitiveLength*x,y.UNSIGNED_SHORT,i.primitiveOffset*x*2)}}}function le(t,e,i){const r=1/bt(i,1,e.transform.tileZoom),n=Math.pow(2,i.tileID.overscaledZ),s=i.tileSize*Math.pow(2,e.transform.tileZoom)/n,o=s*(i.tileID.canonical.x+i.tileID.wrap*n),a=s*i.tileID.canonical.y;return{u_image:0,u_texsize:i.imageAtlasTexture.size,u_scale:[r,t.fromScale,t.toScale],u_fade:t.t,u_pixel_coord_upper:[o>>16,a>>16],u_pixel_coord_lower:[65535&o,65535&a]}}const ce=(e,i,r,n)=>{const s=i.style.light,o=s.properties.get("position"),a=[o.x,o.y,o.z],l=t.create$1();"viewport"===s.properties.get("anchor")&&t.fromRotation(l,-i.transform.angle),t.transformMat3(a,a,l);const c=s.properties.get("color");return{u_matrix:e,u_lightpos:a,u_lightintensity:s.properties.get("intensity"),u_lightcolor:[c.r,c.g,c.b],u_vertical_gradient:+r,u_opacity:n}},he=(e,i,r,n,s,o,a)=>t.extend(ce(e,i,r,n),le(o,i,a),{u_height_factor:-Math.pow(2,s.overscaledZ)/a.tileSize/8}),ue=t=>({u_matrix:t}),pe=(e,i,r,n)=>t.extend(ue(e),le(r,i,n)),de=(t,e)=>({u_matrix:t,u_world:e}),me=(e,i,r,n,s)=>t.extend(pe(e,i,r,n),{u_world:s}),fe=(t,e,i,r)=>{const n=t.transform;let s,o;if("map"===r.paint.get("circle-pitch-alignment")){const t=bt(i,1,n.zoom);s=!0,o=[t,t]}else s=!1,o=n.pixelsToGLUnits;return{u_camera_to_center_distance:n.cameraToCenterDistance,u_scale_with_map:+("map"===r.paint.get("circle-pitch-scale")),u_matrix:t.translatePosMatrix(e.posMatrix,i,r.paint.get("circle-translate"),r.paint.get("circle-translate-anchor")),u_pitch_with_map:+s,u_device_pixel_ratio:t.pixelRatio,u_extrude_scale:o}},ge=(t,e,i)=>{const r=bt(i,1,e.zoom),n=Math.pow(2,e.zoom-i.tileID.overscaledZ),s=i.tileID.overscaleFactor();return{u_matrix:t,u_camera_to_center_distance:e.cameraToCenterDistance,u_pixels_to_tile_units:r,u_extrude_scale:[e.pixelsToGLUnits[0]/(r*n),e.pixelsToGLUnits[1]/(r*n)],u_overscale_factor:s}},_e=(t,e,i=1)=>({u_matrix:t,u_color:e,u_overlay:0,u_overlay_scale:i}),ye=t=>({u_matrix:t}),xe=(t,e,i,r)=>({u_matrix:t,u_extrude_scale:bt(e,1,i),u_intensity:r});function ve(e,i){const r=Math.pow(2,i.canonical.z),n=i.canonical.y;return[new t.MercatorCoordinate(0,n/r).toLngLat().lat,new t.MercatorCoordinate(0,(n+1)/r).toLngLat().lat]}const be=(t,e,i,r)=>{const n=t.transform;return{u_matrix:Ie(t,e,i,r),u_ratio:1/bt(e,1,n.zoom),u_device_pixel_ratio:t.pixelRatio,u_units_to_pixels:[1/n.pixelsToGLUnits[0],1/n.pixelsToGLUnits[1]]}},we=(e,i,r,n,s)=>t.extend(be(e,i,r,s),{u_image:0,u_image_height:n}),Te=(t,e,i,r,n)=>{const s=t.transform,o=Se(e,s);return{u_matrix:Ie(t,e,i,n),u_texsize:e.imageAtlasTexture.size,u_ratio:1/bt(e,1,s.zoom),u_device_pixel_ratio:t.pixelRatio,u_image:0,u_scale:[o,r.fromScale,r.toScale],u_fade:r.t,u_units_to_pixels:[1/s.pixelsToGLUnits[0],1/s.pixelsToGLUnits[1]]}},Ee=(e,i,r,n,s,o)=>{const a=e.lineAtlas,l=Se(i,e.transform),c="round"===r.layout.get("line-cap"),h=a.getDash(n.from,c),u=a.getDash(n.to,c),p=h.width*s.fromScale,d=u.width*s.toScale;return t.extend(be(e,i,r,o),{u_patternscale_a:[l/p,-h.height/2],u_patternscale_b:[l/d,-u.height/2],u_sdfgamma:a.width/(256*Math.min(p,d)*e.pixelRatio)/2,u_image:0,u_tex_y_a:h.y,u_tex_y_b:u.y,u_mix:s.t})};function Se(t,e){return 1/bt(t,1,e.tileZoom)}function Ie(t,e,i,r){return t.translatePosMatrix(r?r.posMatrix:e.tileID.posMatrix,e,i.paint.get("line-translate"),i.paint.get("line-translate-anchor"))}const Ae=(t,e,i,r,n)=>{return{u_matrix:t,u_tl_parent:e,u_scale_parent:i,u_buffer_scale:1,u_fade_t:r.mix,u_opacity:r.opacity*n.paint.get("raster-opacity"),u_image0:0,u_image1:1,u_brightness_low:n.paint.get("raster-brightness-min"),u_brightness_high:n.paint.get("raster-brightness-max"),u_saturation_factor:(o=n.paint.get("raster-saturation"),o>0?1-1/(1.001-o):-o),u_contrast_factor:(s=n.paint.get("raster-contrast"),s>0?1/(1-s):1+s),u_spin_weights:ze(n.paint.get("raster-hue-rotate"))};var s,o};function ze(t){t*=Math.PI/180;const e=Math.sin(t),i=Math.cos(t);return[(2*i+1)/3,(-Math.sqrt(3)*e-i+1)/3,(Math.sqrt(3)*e-i+1)/3]}const Ce=(t,e,i,r,n,s,o,a,l,c)=>{const h=n.transform;return{u_is_size_zoom_constant:+("constant"===t||"source"===t),u_is_size_feature_constant:+("constant"===t||"camera"===t),u_size_t:e?e.uSizeT:0,u_size:e?e.uSize:0,u_camera_to_center_distance:h.cameraToCenterDistance,u_pitch:h.pitch/360*2*Math.PI,u_rotate_symbol:+i,u_aspect_ratio:h.width/h.height,u_fade_change:n.options.fadeDuration?n.symbolFadeChange:1,u_matrix:s,u_label_plane_matrix:o,u_coord_matrix:a,u_is_text:+l,u_pitch_with_map:+r,u_texsize:c,u_texture:0}},Me=(e,i,r,n,s,o,a,l,c,h,u)=>{const p=s.transform;return t.extend(Ce(e,i,r,n,s,o,a,l,c,h),{u_gamma_scale:n?Math.cos(p._pitch)*p.cameraToCenterDistance:1,u_device_pixel_ratio:s.pixelRatio,u_is_halo:+u})},ke=(e,i,r,n,s,o,a,l,c,h)=>t.extend(Me(e,i,r,n,s,o,a,l,!0,c,!0),{u_texsize_icon:h,u_texture_icon:1}),Pe=(t,e,i)=>({u_matrix:t,u_opacity:e,u_color:i}),De=(e,i,r,n,s,o)=>t.extend(function(t,e,i,r){const n=i.imageManager.getPattern(t.from.toString()),s=i.imageManager.getPattern(t.to.toString()),{width:o,height:a}=i.imageManager.getPixelSize(),l=Math.pow(2,r.tileID.overscaledZ),c=r.tileSize*Math.pow(2,i.transform.tileZoom)/l,h=c*(r.tileID.canonical.x+r.tileID.wrap*l),u=c*r.tileID.canonical.y;return{u_image:0,u_pattern_tl_a:n.tl,u_pattern_br_a:n.br,u_pattern_tl_b:s.tl,u_pattern_br_b:s.br,u_texsize:[o,a],u_mix:e.t,u_pattern_size_a:n.displaySize,u_pattern_size_b:s.displaySize,u_scale_a:e.fromScale,u_scale_b:e.toScale,u_tile_units_to_pixels:1/bt(r,1,i.transform.tileZoom),u_pixel_coord_upper:[h>>16,u>>16],u_pixel_coord_lower:[65535&h,65535&u]}}(n,o,r,s),{u_matrix:e,u_opacity:i}),Le={fillExtrusion:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_lightpos:new t.Uniform3f(e,i.u_lightpos),u_lightintensity:new t.Uniform1f(e,i.u_lightintensity),u_lightcolor:new t.Uniform3f(e,i.u_lightcolor),u_vertical_gradient:new t.Uniform1f(e,i.u_vertical_gradient),u_opacity:new t.Uniform1f(e,i.u_opacity)}),fillExtrusionPattern:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_lightpos:new t.Uniform3f(e,i.u_lightpos),u_lightintensity:new t.Uniform1f(e,i.u_lightintensity),u_lightcolor:new t.Uniform3f(e,i.u_lightcolor),u_vertical_gradient:new t.Uniform1f(e,i.u_vertical_gradient),u_height_factor:new t.Uniform1f(e,i.u_height_factor),u_image:new t.Uniform1i(e,i.u_image),u_texsize:new t.Uniform2f(e,i.u_texsize),u_pixel_coord_upper:new t.Uniform2f(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.Uniform2f(e,i.u_pixel_coord_lower),u_scale:new t.Uniform3f(e,i.u_scale),u_fade:new t.Uniform1f(e,i.u_fade),u_opacity:new t.Uniform1f(e,i.u_opacity)}),fill:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix)}),fillPattern:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_image:new t.Uniform1i(e,i.u_image),u_texsize:new t.Uniform2f(e,i.u_texsize),u_pixel_coord_upper:new t.Uniform2f(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.Uniform2f(e,i.u_pixel_coord_lower),u_scale:new t.Uniform3f(e,i.u_scale),u_fade:new t.Uniform1f(e,i.u_fade)}),fillOutline:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_world:new t.Uniform2f(e,i.u_world)}),fillOutlinePattern:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_world:new t.Uniform2f(e,i.u_world),u_image:new t.Uniform1i(e,i.u_image),u_texsize:new t.Uniform2f(e,i.u_texsize),u_pixel_coord_upper:new t.Uniform2f(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.Uniform2f(e,i.u_pixel_coord_lower),u_scale:new t.Uniform3f(e,i.u_scale),u_fade:new t.Uniform1f(e,i.u_fade)}),circle:(e,i)=>({u_camera_to_center_distance:new t.Uniform1f(e,i.u_camera_to_center_distance),u_scale_with_map:new t.Uniform1i(e,i.u_scale_with_map),u_pitch_with_map:new t.Uniform1i(e,i.u_pitch_with_map),u_extrude_scale:new t.Uniform2f(e,i.u_extrude_scale),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_matrix:new t.UniformMatrix4f(e,i.u_matrix)}),collisionBox:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_camera_to_center_distance:new t.Uniform1f(e,i.u_camera_to_center_distance),u_pixels_to_tile_units:new t.Uniform1f(e,i.u_pixels_to_tile_units),u_extrude_scale:new t.Uniform2f(e,i.u_extrude_scale),u_overscale_factor:new t.Uniform1f(e,i.u_overscale_factor)}),collisionCircle:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_inv_matrix:new t.UniformMatrix4f(e,i.u_inv_matrix),u_camera_to_center_distance:new t.Uniform1f(e,i.u_camera_to_center_distance),u_viewport_size:new t.Uniform2f(e,i.u_viewport_size)}),debug:(e,i)=>({u_color:new t.UniformColor(e,i.u_color),u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_overlay:new t.Uniform1i(e,i.u_overlay),u_overlay_scale:new t.Uniform1f(e,i.u_overlay_scale)}),clippingMask:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix)}),heatmap:(e,i)=>({u_extrude_scale:new t.Uniform1f(e,i.u_extrude_scale),u_intensity:new t.Uniform1f(e,i.u_intensity),u_matrix:new t.UniformMatrix4f(e,i.u_matrix)}),heatmapTexture:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_world:new t.Uniform2f(e,i.u_world),u_image:new t.Uniform1i(e,i.u_image),u_color_ramp:new t.Uniform1i(e,i.u_color_ramp),u_opacity:new t.Uniform1f(e,i.u_opacity)}),hillshade:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_image:new t.Uniform1i(e,i.u_image),u_latrange:new t.Uniform2f(e,i.u_latrange),u_light:new t.Uniform2f(e,i.u_light),u_shadow:new t.UniformColor(e,i.u_shadow),u_highlight:new t.UniformColor(e,i.u_highlight),u_accent:new t.UniformColor(e,i.u_accent)}),hillshadePrepare:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_image:new t.Uniform1i(e,i.u_image),u_dimension:new t.Uniform2f(e,i.u_dimension),u_zoom:new t.Uniform1f(e,i.u_zoom),u_unpack:new t.Uniform4f(e,i.u_unpack)}),line:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_ratio:new t.Uniform1f(e,i.u_ratio),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.Uniform2f(e,i.u_units_to_pixels)}),lineGradient:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_ratio:new t.Uniform1f(e,i.u_ratio),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.Uniform2f(e,i.u_units_to_pixels),u_image:new t.Uniform1i(e,i.u_image),u_image_height:new t.Uniform1f(e,i.u_image_height)}),linePattern:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_texsize:new t.Uniform2f(e,i.u_texsize),u_ratio:new t.Uniform1f(e,i.u_ratio),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_image:new t.Uniform1i(e,i.u_image),u_units_to_pixels:new t.Uniform2f(e,i.u_units_to_pixels),u_scale:new t.Uniform3f(e,i.u_scale),u_fade:new t.Uniform1f(e,i.u_fade)}),lineSDF:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_ratio:new t.Uniform1f(e,i.u_ratio),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_units_to_pixels:new t.Uniform2f(e,i.u_units_to_pixels),u_patternscale_a:new t.Uniform2f(e,i.u_patternscale_a),u_patternscale_b:new t.Uniform2f(e,i.u_patternscale_b),u_sdfgamma:new t.Uniform1f(e,i.u_sdfgamma),u_image:new t.Uniform1i(e,i.u_image),u_tex_y_a:new t.Uniform1f(e,i.u_tex_y_a),u_tex_y_b:new t.Uniform1f(e,i.u_tex_y_b),u_mix:new t.Uniform1f(e,i.u_mix)}),raster:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_tl_parent:new t.Uniform2f(e,i.u_tl_parent),u_scale_parent:new t.Uniform1f(e,i.u_scale_parent),u_buffer_scale:new t.Uniform1f(e,i.u_buffer_scale),u_fade_t:new t.Uniform1f(e,i.u_fade_t),u_opacity:new t.Uniform1f(e,i.u_opacity),u_image0:new t.Uniform1i(e,i.u_image0),u_image1:new t.Uniform1i(e,i.u_image1),u_brightness_low:new t.Uniform1f(e,i.u_brightness_low),u_brightness_high:new t.Uniform1f(e,i.u_brightness_high),u_saturation_factor:new t.Uniform1f(e,i.u_saturation_factor),u_contrast_factor:new t.Uniform1f(e,i.u_contrast_factor),u_spin_weights:new t.Uniform3f(e,i.u_spin_weights)}),symbolIcon:(e,i)=>({u_is_size_zoom_constant:new t.Uniform1i(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.Uniform1i(e,i.u_is_size_feature_constant),u_size_t:new t.Uniform1f(e,i.u_size_t),u_size:new t.Uniform1f(e,i.u_size),u_camera_to_center_distance:new t.Uniform1f(e,i.u_camera_to_center_distance),u_pitch:new t.Uniform1f(e,i.u_pitch),u_rotate_symbol:new t.Uniform1i(e,i.u_rotate_symbol),u_aspect_ratio:new t.Uniform1f(e,i.u_aspect_ratio),u_fade_change:new t.Uniform1f(e,i.u_fade_change),u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_label_plane_matrix:new t.UniformMatrix4f(e,i.u_label_plane_matrix),u_coord_matrix:new t.UniformMatrix4f(e,i.u_coord_matrix),u_is_text:new t.Uniform1i(e,i.u_is_text),u_pitch_with_map:new t.Uniform1i(e,i.u_pitch_with_map),u_texsize:new t.Uniform2f(e,i.u_texsize),u_texture:new t.Uniform1i(e,i.u_texture)}),symbolSDF:(e,i)=>({u_is_size_zoom_constant:new t.Uniform1i(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.Uniform1i(e,i.u_is_size_feature_constant),u_size_t:new t.Uniform1f(e,i.u_size_t),u_size:new t.Uniform1f(e,i.u_size),u_camera_to_center_distance:new t.Uniform1f(e,i.u_camera_to_center_distance),u_pitch:new t.Uniform1f(e,i.u_pitch),u_rotate_symbol:new t.Uniform1i(e,i.u_rotate_symbol),u_aspect_ratio:new t.Uniform1f(e,i.u_aspect_ratio),u_fade_change:new t.Uniform1f(e,i.u_fade_change),u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_label_plane_matrix:new t.UniformMatrix4f(e,i.u_label_plane_matrix),u_coord_matrix:new t.UniformMatrix4f(e,i.u_coord_matrix),u_is_text:new t.Uniform1i(e,i.u_is_text),u_pitch_with_map:new t.Uniform1i(e,i.u_pitch_with_map),u_texsize:new t.Uniform2f(e,i.u_texsize),u_texture:new t.Uniform1i(e,i.u_texture),u_gamma_scale:new t.Uniform1f(e,i.u_gamma_scale),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_is_halo:new t.Uniform1i(e,i.u_is_halo)}),symbolTextAndIcon:(e,i)=>({u_is_size_zoom_constant:new t.Uniform1i(e,i.u_is_size_zoom_constant),u_is_size_feature_constant:new t.Uniform1i(e,i.u_is_size_feature_constant),u_size_t:new t.Uniform1f(e,i.u_size_t),u_size:new t.Uniform1f(e,i.u_size),u_camera_to_center_distance:new t.Uniform1f(e,i.u_camera_to_center_distance),u_pitch:new t.Uniform1f(e,i.u_pitch),u_rotate_symbol:new t.Uniform1i(e,i.u_rotate_symbol),u_aspect_ratio:new t.Uniform1f(e,i.u_aspect_ratio),u_fade_change:new t.Uniform1f(e,i.u_fade_change),u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_label_plane_matrix:new t.UniformMatrix4f(e,i.u_label_plane_matrix),u_coord_matrix:new t.UniformMatrix4f(e,i.u_coord_matrix),u_is_text:new t.Uniform1i(e,i.u_is_text),u_pitch_with_map:new t.Uniform1i(e,i.u_pitch_with_map),u_texsize:new t.Uniform2f(e,i.u_texsize),u_texsize_icon:new t.Uniform2f(e,i.u_texsize_icon),u_texture:new t.Uniform1i(e,i.u_texture),u_texture_icon:new t.Uniform1i(e,i.u_texture_icon),u_gamma_scale:new t.Uniform1f(e,i.u_gamma_scale),u_device_pixel_ratio:new t.Uniform1f(e,i.u_device_pixel_ratio),u_is_halo:new t.Uniform1i(e,i.u_is_halo)}),background:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_opacity:new t.Uniform1f(e,i.u_opacity),u_color:new t.UniformColor(e,i.u_color)}),backgroundPattern:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_opacity:new t.Uniform1f(e,i.u_opacity),u_image:new t.Uniform1i(e,i.u_image),u_pattern_tl_a:new t.Uniform2f(e,i.u_pattern_tl_a),u_pattern_br_a:new t.Uniform2f(e,i.u_pattern_br_a),u_pattern_tl_b:new t.Uniform2f(e,i.u_pattern_tl_b),u_pattern_br_b:new t.Uniform2f(e,i.u_pattern_br_b),u_texsize:new t.Uniform2f(e,i.u_texsize),u_mix:new t.Uniform1f(e,i.u_mix),u_pattern_size_a:new t.Uniform2f(e,i.u_pattern_size_a),u_pattern_size_b:new t.Uniform2f(e,i.u_pattern_size_b),u_scale_a:new t.Uniform1f(e,i.u_scale_a),u_scale_b:new t.Uniform1f(e,i.u_scale_b),u_pixel_coord_upper:new t.Uniform2f(e,i.u_pixel_coord_upper),u_pixel_coord_lower:new t.Uniform2f(e,i.u_pixel_coord_lower),u_tile_units_to_pixels:new t.Uniform1f(e,i.u_tile_units_to_pixels)}),terrain:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_texture:new t.Uniform1i(e,i.u_texture)}),terrainDepth:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix)}),terrainCoords:(e,i)=>({u_matrix:new t.UniformMatrix4f(e,i.u_matrix),u_texture:new t.Uniform1i(e,i.u_texture),u_terrain_coords_id:new t.Uniform1f(e,i.u_terrain_coords_id)})};class Be{constructor(t,e,i){this.context=t;const r=t.gl;this.buffer=r.createBuffer(),this.dynamicDraw=Boolean(i),this.context.unbindVAO(),t.bindElementBuffer.set(this.buffer),r.bufferData(r.ELEMENT_ARRAY_BUFFER,e.arrayBuffer,this.dynamicDraw?r.DYNAMIC_DRAW:r.STATIC_DRAW),this.dynamicDraw||delete e.arrayBuffer}bind(){this.context.bindElementBuffer.set(this.buffer)}updateData(t){const e=this.context.gl;this.context.unbindVAO(),this.bind(),e.bufferSubData(e.ELEMENT_ARRAY_BUFFER,0,t.arrayBuffer)}destroy(){this.buffer&&(this.context.gl.deleteBuffer(this.buffer),delete this.buffer)}}const Re={Int8:"BYTE",Uint8:"UNSIGNED_BYTE",Int16:"SHORT",Uint16:"UNSIGNED_SHORT",Int32:"INT",Uint32:"UNSIGNED_INT",Float32:"FLOAT"};class Fe{constructor(t,e,i,r){this.length=e.length,this.attributes=i,this.itemSize=e.bytesPerElement,this.dynamicDraw=r,this.context=t;const n=t.gl;this.buffer=n.createBuffer(),t.bindVertexBuffer.set(this.buffer),n.bufferData(n.ARRAY_BUFFER,e.arrayBuffer,this.dynamicDraw?n.DYNAMIC_DRAW:n.STATIC_DRAW),this.dynamicDraw||delete e.arrayBuffer}bind(){this.context.bindVertexBuffer.set(this.buffer)}updateData(t){const e=this.context.gl;this.bind(),e.bufferSubData(e.ARRAY_BUFFER,0,t.arrayBuffer)}enableAttributes(t,e){for(let i=0;i0){const i=t.create(),r=y;t.mul(i,_.placementInvProjMatrix,e.transform.glCoordMatrix),t.mul(i,i,_.placementViewportMatrix),u.push({circleArray:v,circleOffset:d,transform:r,invTransform:i,coord:f}),p+=v.length/4,d=p}x&&h.draw(l,c.LINES,wi.disabled,Ei.disabled,e.colorModeForRenderPass(),Si.disabled,ge(y,e.transform,g),e.style.terrain&&e.style.terrain.getTerrainData(f),r.id,x.layoutVertexBuffer,x.indexBuffer,x.segments,null,e.transform.zoom,null,null,x.collisionVertexBuffer)}if(!a||!u.length)return;const m=e.useProgram("collisionCircle"),f=new t.CollisionCircleLayoutArray;f.resize(4*p),f._trim();let g=0;for(const t of u)for(let e=0;e=0&&(f[g.associatedIconIndex]={shiftedAnchor:S,angle:I})}else _t(g.numGlyphs,d)}if(h){m.clear();const i=e.icon.placedSymbolArray;for(let e=0;ee.style.terrain.getElevation(l,t,i):null,i="map"===r.layout.get("text-rotation-alignment");ht(c,l.posMatrix,e,s,R,F,_,h,i,t)}const V=e.translatePosMatrix(l.posMatrix,n,o,a),N=y||s&&T||U?zi:R,G=e.translatePosMatrix(F,n,o,a,!0),$=d&&0!==r.paint.get(s?"text-halo-width":"icon-halo-width").constantOr(1);let q;q=d?c.iconsInText?ke(w.kind,A,x,_,e,V,N,G,C,D):Me(w.kind,A,x,_,e,V,N,G,s,C,!0):Ce(w.kind,A,x,_,e,V,N,G,s,C);const j={program:I,buffers:u,uniformValues:q,atlasTexture:M,atlasTextureIcon:L,atlasInterpolation:k,atlasInterpolationIcon:P,isSDF:d,hasHalo:$};if(v&&c.canOverlap){b=!0;const e=u.segments.get();for(const i of e)E.push({segments:new t.SegmentVector([i]),sortKey:i.sortKey,state:j,terrainData:z})}else E.push({segments:u.segments,sortKey:0,state:j,terrainData:z})}b&&E.sort(((t,e)=>t.sortKey-e.sortKey));for(const t of E){const i=t.state;if(d.activeTexture.set(m.TEXTURE0),i.atlasTexture.bind(i.atlasInterpolation,m.CLAMP_TO_EDGE),i.atlasTextureIcon&&(d.activeTexture.set(m.TEXTURE1),i.atlasTextureIcon&&i.atlasTextureIcon.bind(i.atlasInterpolationIcon,m.CLAMP_TO_EDGE)),i.isSDF){const n=i.uniformValues;i.hasHalo&&(n.u_is_halo=1,Di(i.buffers,t.segments,r,e,i.program,w,u,p,n,t.terrainData)),n.u_is_halo=0}Di(i.buffers,t.segments,r,e,i.program,w,u,p,i.uniformValues,t.terrainData)}}function Di(t,e,i,r,n,s,o,a,l,c){const h=r.context;n.draw(h,h.gl.TRIANGLES,s,o,a,Si.disabled,l,c,i.id,t.layoutVertexBuffer,t.indexBuffer,e,i.paint,r.transform.zoom,t.programConfigurations.get(i.id),t.dynamicLayoutVertexBuffer,t.opacityVertexBuffer)}function Li(t,e,i,r,n,s,o){const a=t.context.gl,l=i.paint.get("fill-pattern"),c=l&&l.constantOr(1),h=i.getCrossfadeParameters();let u,p,d,m,f;o?(p=c&&!i.getPaintProperty("fill-outline-color")?"fillOutlinePattern":"fillOutline",u=a.LINES):(p=c?"fillPattern":"fill",u=a.TRIANGLES);for(const g of r){const r=e.getTile(g);if(c&&!r.patternsLoaded())continue;const _=r.getBucket(i);if(!_)continue;const y=_.programConfigurations.get(i.id),x=t.useProgram(p,y),v=t.style.terrain&&t.style.terrain.getTerrainData(g);c&&(t.context.activeTexture.set(a.TEXTURE0),r.imageAtlasTexture.bind(a.LINEAR,a.CLAMP_TO_EDGE),y.updatePaintBuffers(h));const b=l.constantOr(null);if(b&&r.imageAtlas){const t=r.imageAtlas,e=t.patternPositions[b.to.toString()],i=t.patternPositions[b.from.toString()];e&&i&&y.setConstantPatternPositions(e,i)}const w=v?g:null,T=t.translatePosMatrix(w?w.posMatrix:g.posMatrix,r,i.paint.get("fill-translate"),i.paint.get("fill-translate-anchor"));if(o){m=_.indexBuffer2,f=_.segments2;const e=[a.drawingBufferWidth,a.drawingBufferHeight];d="fillOutlinePattern"===p&&c?me(T,t,h,r,e):de(T,e)}else m=_.indexBuffer,f=_.segments,d=c?pe(T,t,h,r):ue(T);x.draw(t.context,u,n,t.stencilModeForClipping(g),s,Si.disabled,d,v,i.id,_.layoutVertexBuffer,m,f,i.paint,t.transform.zoom,y)}}function Bi(t,e,i,r,n,s,o){const a=t.context,l=a.gl,c=i.paint.get("fill-extrusion-pattern"),h=c.constantOr(1),u=i.getCrossfadeParameters(),p=i.paint.get("fill-extrusion-opacity");for(const d of r){const r=e.getTile(d),m=r.getBucket(i);if(!m)continue;const f=t.style.terrain&&t.style.terrain.getTerrainData(d),g=m.programConfigurations.get(i.id),_=t.useProgram(h?"fillExtrusionPattern":"fillExtrusion",g);h&&(t.context.activeTexture.set(l.TEXTURE0),r.imageAtlasTexture.bind(l.LINEAR,l.CLAMP_TO_EDGE),g.updatePaintBuffers(u));const y=c.constantOr(null);if(y&&r.imageAtlas){const t=r.imageAtlas,e=t.patternPositions[y.to.toString()],i=t.patternPositions[y.from.toString()];e&&i&&g.setConstantPatternPositions(e,i)}const x=t.translatePosMatrix(d.posMatrix,r,i.paint.get("fill-extrusion-translate"),i.paint.get("fill-extrusion-translate-anchor")),v=i.paint.get("fill-extrusion-vertical-gradient"),b=h?he(x,t,v,p,d,u,r):ce(x,t,v,p);_.draw(a,a.gl.TRIANGLES,n,s,o,Si.backCCW,b,f,i.id,m.layoutVertexBuffer,m.indexBuffer,m.segments,i.paint,t.transform.zoom,g,t.style.terrain&&m.centroidVertexBuffer)}}function Ri(t,e,i,r,n,s,o){const a=t.context,l=a.gl,c=i.fbo;if(!c)return;const h=t.useProgram("hillshade"),u=t.style.terrain&&t.style.terrain.getTerrainData(e);a.activeTexture.set(l.TEXTURE0),l.bindTexture(l.TEXTURE_2D,c.colorAttachment.get()),h.draw(a,l.TRIANGLES,n,s,o,Si.disabled,((t,e,i,r)=>{const n=i.paint.get("hillshade-shadow-color"),s=i.paint.get("hillshade-highlight-color"),o=i.paint.get("hillshade-accent-color");let a=i.paint.get("hillshade-illumination-direction")*(Math.PI/180);"viewport"===i.paint.get("hillshade-illumination-anchor")&&(a-=t.transform.angle);const l=!t.options.moving;return{u_matrix:r?r.posMatrix:t.transform.calculatePosMatrix(e.tileID.toUnwrapped(),l),u_image:0,u_latrange:ve(0,e.tileID),u_light:[i.paint.get("hillshade-exaggeration"),a],u_shadow:n,u_highlight:s,u_accent:o}})(t,i,r,u?e:null),u,r.id,t.rasterBoundsBuffer,t.quadTriangleIndexBuffer,t.rasterBoundsSegments)}function Fi(e,i,r,n,s,o){const a=e.context,c=a.gl,h=i.dem;if(h&&h.data){const u=h.dim,p=h.stride,d=h.getPixels();if(a.activeTexture.set(c.TEXTURE1),a.pixelStoreUnpackPremultiplyAlpha.set(!1),i.demTexture=i.demTexture||e.getTileTexture(p),i.demTexture){const t=i.demTexture;t.update(d,{premultiply:!1}),t.bind(c.NEAREST,c.CLAMP_TO_EDGE)}else i.demTexture=new l(a,d,c.RGBA,{premultiply:!1}),i.demTexture.bind(c.NEAREST,c.CLAMP_TO_EDGE);a.activeTexture.set(c.TEXTURE0);let m=i.fbo;if(!m){const t=new l(a,{width:u,height:u,data:null},c.RGBA);t.bind(c.LINEAR,c.CLAMP_TO_EDGE),m=i.fbo=a.createFramebuffer(u,u,!0),m.colorAttachment.set(t.texture)}a.bindFramebuffer.set(m.framebuffer),a.viewport.set([0,0,u,u]),e.useProgram("hillshadePrepare").draw(a,c.TRIANGLES,n,s,o,Si.disabled,((e,i)=>{const r=i.stride,n=t.create();return t.ortho(n,0,t.EXTENT,-t.EXTENT,0,0,1),t.translate(n,n,[0,-t.EXTENT,0]),{u_matrix:n,u_image:1,u_dimension:[r,r],u_zoom:e.overscaledZ,u_unpack:i.getUnpackVector()}})(i.tileID,h),null,r.id,e.rasterBoundsBuffer,e.quadTriangleIndexBuffer,e.rasterBoundsSegments),i.needsHillshadePrepare=!1}}function Oi(e,i,r,n,s,o){const a=n.paint.get("raster-fade-duration");if(!o&&a>0){const n=t.exported.now(),o=(n-e.timeAdded)/a,l=i?(n-i.timeAdded)/a:-1,c=r.getSource(),h=s.coveringZoomLevel({tileSize:c.tileSize,roundZoom:c.roundZoom}),u=!i||Math.abs(i.tileID.overscaledZ-h)>Math.abs(e.tileID.overscaledZ-h),p=u&&e.refreshedUponExpiration?1:t.clamp(u?o:1-l,0,1);return e.refreshedUponExpiration&&o>=1&&(e.refreshedUponExpiration=!1),i?{opacity:1,mix:1-p}:{opacity:p,mix:0}}return{opacity:1,mix:0}}const Ui=new t.Color(1,0,0,1),Vi=new t.Color(0,1,0,1),Ni=new t.Color(0,0,1,1),Gi=new t.Color(1,0,1,1),$i=new t.Color(0,1,1,1);function qi(t,e,i,r){Zi(t,0,e+i/2,t.transform.width,i,r)}function ji(t,e,i,r){Zi(t,e-i/2,0,i,t.transform.height,r)}function Zi(t,e,i,r,n,s){const o=t.context,a=o.gl;a.enable(a.SCISSOR_TEST),a.scissor(e*t.pixelRatio,i*t.pixelRatio,r*t.pixelRatio,n*t.pixelRatio),o.clear({color:s}),a.disable(a.SCISSOR_TEST)}function Xi(e,i,r){const n=e.context,s=n.gl,o=r.posMatrix,a=e.useProgram("debug"),l=wi.disabled,c=Ei.disabled,h=e.colorModeForRenderPass(),u="$debug",p=e.style.terrain&&e.style.terrain.getTerrainData(r);n.activeTexture.set(s.TEXTURE0),e.emptyTexture.bind(s.LINEAR,s.CLAMP_TO_EDGE);const d=i.getTileByID(r.key).latestRawTileData,m=Math.floor((d&&d.byteLength||0)/1024),f=i.getTile(r).tileSize,g=512/Math.min(f,512)*(r.overscaledZ/e.transform.zoom)*.5;let _=r.canonical.toString();r.overscaledZ!==r.canonical.z&&(_+=` => ${r.overscaledZ}`),function(t,e){t.initDebugOverlayCanvas();const i=t.debugOverlayCanvas,r=t.context.gl,n=t.debugOverlayCanvas.getContext("2d");n.clearRect(0,0,i.width,i.height),n.shadowColor="white",n.shadowBlur=2,n.lineWidth=1.5,n.strokeStyle="white",n.textBaseline="top",n.font="bold 36px Open Sans, sans-serif",n.fillText(e,5,5),n.strokeText(e,5,5),t.debugOverlayTexture.update(i),t.debugOverlayTexture.bind(r.LINEAR,r.CLAMP_TO_EDGE)}(e,`${_} ${m}kB`),a.draw(n,s.TRIANGLES,l,c,vi.alphaBlended,Si.disabled,_e(o,t.Color.transparent,g),null,u,e.debugBuffer,e.quadTriangleIndexBuffer,e.debugSegments),a.draw(n,s.LINE_STRIP,l,c,h,Si.disabled,_e(o,t.Color.red),p,u,e.debugBuffer,e.tileBorderIndexBuffer,e.debugSegments)}function Wi(t,e,i){const r=t.context,n=r.gl,s=t.colorModeForRenderPass(),o=new wi(n.LEQUAL,wi.ReadWrite,t.depthRangeFor3D),a=t.useProgram("terrain"),l=e.getTerrainMesh(),c=e.getTerrainData(i.tileID);r.bindFramebuffer.set(null),r.viewport.set([0,0,t.width,t.height]),r.activeTexture.set(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,e.getRTTFramebuffer().colorAttachment.get());const h=t.transform.calculatePosMatrix(i.tileID.toUnwrapped());a.draw(r,n.TRIANGLES,o,Ei.disabled,s,Si.backCCW,{u_matrix:h,u_texture:0},c,"terrain",l.vertexBuffer,l.indexBuffer,l.segments)}function Hi(t,e,i,r){const n=t.context,s=i.tileSize*e.qualityFactor;i.textures[r]||(i.textures[r]=t.getTileTexture(s)||new l(n,{width:s,height:s,data:null},n.gl.RGBA),i.textures[r].bind(n.gl.LINEAR,n.gl.CLAMP_TO_EDGE),0===r&&e.sourceCache.renderHistory.unshift(i.tileID.key));const o=e.getRTTFramebuffer();o.colorAttachment.set(i.textures[r].texture),n.bindFramebuffer.set(o.framebuffer),n.viewport.set([0,0,s,s])}class Ki{constructor(t){this._coordsDescendingInv={},this._coordsDescendingInvStr={},this.painter=t,this._renderToTexture={background:!0,fill:!0,line:!0,raster:!0},this._coordsDescendingInv={},this._coordsDescendingInvStr={},this._stacks=[],this._prevType=null,this._rerender={},this._renderableTiles=t.style.terrain.sourceCache.getRenderableTiles(),this._init()}_init(){const t=this.painter.style,e=t.terrain;for(const i in t.sourceCaches){this._coordsDescendingInv[i]={};const r=t.sourceCaches[i].getVisibleCoordinates();for(const t of r){const r=e.sourceCache.getTerrainCoords(t);for(const t in r)this._coordsDescendingInv[i][t]||(this._coordsDescendingInv[i][t]=[]),this._coordsDescendingInv[i][t].push(r[t])}}for(const e of t._order){const i=t._layers[e],r=i.source;if(this._renderToTexture[i.type]&&!this._coordsDescendingInvStr[r]){this._coordsDescendingInvStr[r]={};for(const t in this._coordsDescendingInv[r])this._coordsDescendingInvStr[r][t]=this._coordsDescendingInv[r][t].map((t=>t.key)).sort().join()}}return this._renderableTiles.forEach((t=>{for(const i in this._coordsDescendingInvStr){const r=this._coordsDescendingInvStr[i][t.tileID.key];r&&r!==t.textureCoords[i]&&t.clearTextures(this.painter),e.needsRerender(i,t.tileID)&&t.clearTextures(this.painter)}this._rerender[t.tileID.key]=!t.textures.length})),e.clearRerenderCache(),e.sourceCache.removeOutdated(this.painter),this}renderLayer(e){const i=e.type,r=this.painter,n=r.style._order,s=r.currentLayer,o=s+1===n.length;if(this._renderToTexture[i]&&(this._prevType&&this._renderToTexture[this._prevType]||this._stacks.push([]),this._prevType=i,this._stacks[this._stacks.length-1].push(n[s]),!o))return!0;if(this._renderToTexture[this._prevType]||"hillshade"===i||this._renderToTexture[i]&&o){this._prevType=i;const o=this._stacks.length-1,a=this._stacks[o]||[];for(const e of this._renderableTiles){if(Hi(r,r.style.terrain,e,o),this._rerender[e.tileID.key]){r.context.clear({color:t.Color.transparent});for(let t=0;ti.style.terrain.getElevation(s,t,e):null)}}}(n,e,r,i,r.layout.get("text-rotation-alignment"),r.layout.get("text-pitch-alignment"),s),0!==r.paint.get("icon-opacity").constantOr(1)&&Pi(e,i,r,n,!1,r.paint.get("icon-translate"),r.paint.get("icon-translate-anchor"),r.layout.get("icon-rotation-alignment"),r.layout.get("icon-pitch-alignment"),r.layout.get("icon-keep-upright"),o,a),0!==r.paint.get("text-opacity").constantOr(1)&&Pi(e,i,r,n,!0,r.paint.get("text-translate"),r.paint.get("text-translate-anchor"),r.layout.get("text-rotation-alignment"),r.layout.get("text-pitch-alignment"),r.layout.get("text-keep-upright"),o,a),i.map.showCollisionBoxes&&(Ai(e,i,r,n,r.paint.get("text-translate"),r.paint.get("text-translate-anchor"),!0),Ai(e,i,r,n,r.paint.get("icon-translate"),r.paint.get("icon-translate-anchor"),!1))},circle:function(e,i,r,n){if("translucent"!==e.renderPass)return;const s=r.paint.get("circle-opacity"),o=r.paint.get("circle-stroke-width"),a=r.paint.get("circle-stroke-opacity"),l=!r.layout.get("circle-sort-key").isConstant();if(0===s.constantOr(1)&&(0===o.constantOr(1)||0===a.constantOr(1)))return;const c=e.context,h=c.gl,u=e.depthModeForSublayer(0,wi.ReadOnly),p=Ei.disabled,d=e.colorModeForRenderPass(),m=[];for(let s=0;st.sortKey-e.sortKey));for(const t of m){const{programConfiguration:i,program:n,layoutVertexBuffer:s,indexBuffer:o,uniformValues:a,terrainData:l}=t.state;n.draw(c,h.TRIANGLES,u,p,d,Si.disabled,a,l,r.id,s,o,t.segments,r.paint,e.transform.zoom,i)}},heatmap:function(e,i,r,n){if(0!==r.paint.get("heatmap-opacity"))if("offscreen"===e.renderPass){const s=e.context,o=s.gl,a=Ei.disabled,l=new vi([o.ONE,o.ONE],t.Color.transparent,[!0,!0,!0,!0]);!function(t,e,i){const r=t.gl;t.activeTexture.set(r.TEXTURE1),t.viewport.set([0,0,e.width/4,e.height/4]);let n=i.heatmapFbo;if(n)r.bindTexture(r.TEXTURE_2D,n.colorAttachment.get()),t.bindFramebuffer.set(n.framebuffer);else{const s=r.createTexture();r.bindTexture(r.TEXTURE_2D,s),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.LINEAR),n=i.heatmapFbo=t.createFramebuffer(e.width/4,e.height/4,!1),function(t,e,i,r){const n=t.gl;n.texImage2D(n.TEXTURE_2D,0,n.RGBA,e.width/4,e.height/4,0,n.RGBA,t.extRenderToTextureHalfFloat?t.extTextureHalfFloat.HALF_FLOAT_OES:n.UNSIGNED_BYTE,null),r.colorAttachment.set(i)}(t,e,s,n)}}(s,e,r),s.clear({color:t.Color.transparent});for(let t=0;t{const s=t.create();t.ortho(s,0,e.width,e.height,0,0,1);const o=e.context.gl;return{u_matrix:s,u_world:[o.drawingBufferWidth,o.drawingBufferHeight],u_image:0,u_color_ramp:1,u_opacity:i.paint.get("heatmap-opacity")}})(e,i),null,i.id,e.viewportBuffer,e.quadTriangleIndexBuffer,e.viewportSegments,i.paint,e.transform.zoom)}(e,r))},line:function(e,i,r,n){if("translucent"!==e.renderPass)return;const s=r.paint.get("line-opacity"),o=r.paint.get("line-width");if(0===s.constantOr(1)||0===o.constantOr(1))return;const a=e.depthModeForSublayer(0,wi.ReadOnly),c=e.colorModeForRenderPass(),h=r.paint.get("line-dasharray"),u=r.paint.get("line-pattern"),p=u.constantOr(1),d=r.paint.get("line-gradient"),m=r.getCrossfadeParameters(),f=p?"linePattern":h?"lineSDF":d?"lineGradient":"line",g=e.context,_=g.gl;let y=!0;for(const s of n){const n=i.getTile(s);if(p&&!n.patternsLoaded())continue;const o=n.getBucket(r);if(!o)continue;const x=o.programConfigurations.get(r.id),v=e.context.program.get(),b=e.useProgram(f,x),w=y||b.program!==v,T=e.style.terrain&&e.style.terrain.getTerrainData(s),E=u.constantOr(null);if(E&&n.imageAtlas){const t=n.imageAtlas,e=t.patternPositions[E.to.toString()],i=t.patternPositions[E.from.toString()];e&&i&&x.setConstantPatternPositions(e,i)}const S=T?s:null,I=p?Te(e,n,r,m,S):h?Ee(e,n,r,h,m,S):d?we(e,n,r,o.lineClipsArray.length,S):be(e,n,r,S);if(p)g.activeTexture.set(_.TEXTURE0),n.imageAtlasTexture.bind(_.LINEAR,_.CLAMP_TO_EDGE),x.updatePaintBuffers(m);else if(h&&(w||e.lineAtlas.dirty))g.activeTexture.set(_.TEXTURE0),e.lineAtlas.bind(g);else if(d){const n=o.gradients[r.id];let a=n.texture;if(r.gradientVersion!==n.version){let c=256;if(r.stepInterpolant){const r=i.getSource().maxzoom,n=s.canonical.z===r?Math.ceil(1<256&&this.clearStencil(),i.setColorMode(vi.disabled),i.setDepthMode(wi.disabled);const n=this.useProgram("clippingMask");this._tileClippingMaskIDs={};for(const t of e){const e=this._tileClippingMaskIDs[t.key]=this.nextStencilID++,s=this.style.terrain&&this.style.terrain.getTerrainData(t);n.draw(i,r.TRIANGLES,wi.disabled,new Ei({func:r.ALWAYS,mask:0},e,255,r.KEEP,r.KEEP,r.REPLACE),vi.disabled,Si.disabled,ye(t.posMatrix),s,"$clipping",this.tileExtentBuffer,this.quadTriangleIndexBuffer,this.tileExtentSegments)}}stencilModeFor3D(){this.currentStencilSource=void 0,this.nextStencilID+1>256&&this.clearStencil();const t=this.nextStencilID++,e=this.context.gl;return new Ei({func:e.NOTEQUAL,mask:255},t,255,e.KEEP,e.KEEP,e.REPLACE)}stencilModeForClipping(t){const e=this.context.gl;return new Ei({func:e.EQUAL,mask:255},this._tileClippingMaskIDs[t.key],0,e.KEEP,e.KEEP,e.REPLACE)}stencilConfigForOverlap(t){const e=this.context.gl,i=t.sort(((t,e)=>e.overscaledZ-t.overscaledZ)),r=i[i.length-1].overscaledZ,n=i[0].overscaledZ-r+1;if(n>1){this.currentStencilSource=void 0,this.nextStencilID+n>256&&this.clearStencil();const t={};for(let i=0;i=0;this.currentLayer--){const t=this.style._layers[r[this.currentLayer]],e=n[t.source],i=o[t.source];this._renderTileClippingMasks(t,i),this.renderLayer(this,e,t,i)}for(this.renderPass="translucent",this.currentLayer=0;this.currentLayer{i.source&&!i.isHidden(this.transform.zoom)&&(i.source!==(e&&e.id)&&(e=this.style.sourceCaches[i.source]),(!t||t.getSource().maxzoom0?e.pop():null}isPatternMissing(t){if(!t)return!1;if(!t.from||!t.to)return!0;const e=this.imageManager.getPattern(t.from.toString()),i=this.imageManager.getPattern(t.to.toString());return!e||!i}useProgram(t,e){this.cache=this.cache||{};const i=t+(e?e.cacheKey:"")+(this._showOverdrawInspector?"/overdraw":"")+(this.style.terrain?"/terrain":"");return this.cache[i]||(this.cache[i]=new ae(this.context,t,re[t],e,Le[t],this._showOverdrawInspector,this.style.terrain)),this.cache[i]}setCustomLayerDefaults(){this.context.unbindVAO(),this.context.cullFace.setDefault(),this.context.activeTexture.setDefault(),this.context.pixelStoreUnpack.setDefault(),this.context.pixelStoreUnpackPremultiplyAlpha.setDefault(),this.context.pixelStoreUnpackFlipY.setDefault()}setBaseState(){const t=this.context.gl;this.context.cullFace.set(!1),this.context.viewport.set([0,0,this.width,this.height]),this.context.blendEquation.set(t.FUNC_ADD)}initDebugOverlayCanvas(){null==this.debugOverlayCanvas&&(this.debugOverlayCanvas=document.createElement("canvas"),this.debugOverlayCanvas.width=512,this.debugOverlayCanvas.height=512,this.debugOverlayTexture=new l(this.context,this.debugOverlayCanvas,this.context.gl.RGBA))}destroy(){this.emptyTexture.destroy(),this.debugOverlayTexture&&this.debugOverlayTexture.destroy()}}class Qi{constructor(t,e){this.points=t,this.planes=e}static fromInvProjectionMatrix(e,i,r){const n=Math.pow(2,r),s=[[-1,1,-1,1],[1,1,-1,1],[1,-1,-1,1],[-1,-1,-1,1],[-1,1,1,1],[1,1,1,1],[1,-1,1,1],[-1,-1,1,1]].map((r=>{const s=1/(r=t.transformMat4([],r,e))[3]/i*n;return t.mul$1(r,r,[s,s,1/r[3],s])})),o=[[0,1,2],[6,5,4],[0,3,7],[2,1,5],[3,2,6],[0,4,5]].map((e=>{const i=t.sub([],s[e[0]],s[e[1]]),r=t.sub([],s[e[2]],s[e[1]]),n=t.normalize([],t.cross([],i,r)),o=-t.dot(n,s[e[1]]);return n.concat(o)}));return new Qi(s,o)}}class tr{constructor(e,i){this.min=e,this.max=i,this.center=t.scale$1([],t.add([],this.min,this.max),.5)}quadrant(e){const i=[e%2==0,e<2],r=t.clone$2(this.min),n=t.clone$2(this.max);for(let t=0;t=0&&o++;if(0===o)return 0;o!==i.length&&(r=!1)}if(r)return 2;for(let t=0;t<3;t++){let i=Number.MAX_VALUE,r=-Number.MAX_VALUE;for(let n=0;nthis.max[t]-this.min[t])return 0}return 1}}class er{constructor(t=0,e=0,i=0,r=0){if(isNaN(t)||t<0||isNaN(e)||e<0||isNaN(i)||i<0||isNaN(r)||r<0)throw new Error("Invalid value for edge-insets, top, bottom, left and right must all be numbers");this.top=t,this.bottom=e,this.left=i,this.right=r}interpolate(e,i,r){return null!=i.top&&null!=e.top&&(this.top=t.number(e.top,i.top,r)),null!=i.bottom&&null!=e.bottom&&(this.bottom=t.number(e.bottom,i.bottom,r)),null!=i.left&&null!=e.left&&(this.left=t.number(e.left,i.left,r)),null!=i.right&&null!=e.right&&(this.right=t.number(e.right,i.right,r)),this}getCenter(e,i){const r=t.clamp((this.left+e-this.right)/2,0,e),n=t.clamp((this.top+i-this.bottom)/2,0,i);return new t.pointGeometry(r,n)}equals(t){return this.top===t.top&&this.bottom===t.bottom&&this.left===t.left&&this.right===t.right}clone(){return new er(this.top,this.bottom,this.left,this.right)}toJSON(){return{top:this.top,bottom:this.bottom,left:this.left,right:this.right}}}class ir{constructor(e,i,r,n,s){this.tileSize=512,this.maxValidLatitude=85.051129,this.freezeElevation=!1,this._renderWorldCopies=void 0===s||!!s,this._minZoom=e||0,this._maxZoom=i||22,this._minPitch=null==r?0:r,this._maxPitch=null==n?60:n,this.setMaxBounds(),this.width=0,this.height=0,this._center=new t.LngLat(0,0),this._elevation=0,this.zoom=0,this.angle=0,this._fov=.6435011087932844,this._pitch=0,this._unmodified=!0,this._edgeInsets=new er,this._posMatrixCache={},this._alignedPosMatrixCache={}}clone(){const t=new ir(this._minZoom,this._maxZoom,this._minPitch,this.maxPitch,this._renderWorldCopies);return t.tileSize=this.tileSize,t.latRange=this.latRange,t.width=this.width,t.height=this.height,t._center=this._center,t._elevation=this._elevation,t.zoom=this.zoom,t.angle=this.angle,t._fov=this._fov,t._pitch=this._pitch,t._unmodified=this._unmodified,t._edgeInsets=this._edgeInsets.clone(),t._calcMatrices(),t}get minZoom(){return this._minZoom}set minZoom(t){this._minZoom!==t&&(this._minZoom=t,this.zoom=Math.max(this.zoom,t))}get maxZoom(){return this._maxZoom}set maxZoom(t){this._maxZoom!==t&&(this._maxZoom=t,this.zoom=Math.min(this.zoom,t))}get minPitch(){return this._minPitch}set minPitch(t){this._minPitch!==t&&(this._minPitch=t,this.pitch=Math.max(this.pitch,t))}get maxPitch(){return this._maxPitch}set maxPitch(t){this._maxPitch!==t&&(this._maxPitch=t,this.pitch=Math.min(this.pitch,t))}get renderWorldCopies(){return this._renderWorldCopies}set renderWorldCopies(t){void 0===t?t=!0:null===t&&(t=!1),this._renderWorldCopies=t}get worldSize(){return this.tileSize*this.scale}get centerOffset(){return this.centerPoint._sub(this.size._div(2))}get size(){return new t.pointGeometry(this.width,this.height)}get bearing(){return-this.angle/Math.PI*180}set bearing(e){const i=-t.wrap(e,-180,180)*Math.PI/180;var r;this.angle!==i&&(this._unmodified=!1,this.angle=i,this._calcMatrices(),this.rotationMatrix=(r=new t.ARRAY_TYPE(4),t.ARRAY_TYPE!=Float32Array&&(r[1]=0,r[2]=0),r[0]=1,r[3]=1,r),function(t,e,i){var r=e[0],n=e[1],s=e[2],o=e[3],a=Math.sin(i),l=Math.cos(i);t[0]=r*l+s*a,t[1]=n*l+o*a,t[2]=r*-a+s*l,t[3]=n*-a+o*l}(this.rotationMatrix,this.rotationMatrix,this.angle))}get pitch(){return this._pitch/Math.PI*180}set pitch(e){const i=t.clamp(e,this.minPitch,this.maxPitch)/180*Math.PI;this._pitch!==i&&(this._unmodified=!1,this._pitch=i,this._calcMatrices())}get fov(){return this._fov/Math.PI*180}set fov(t){t=Math.max(.01,Math.min(60,t)),this._fov!==t&&(this._unmodified=!1,this._fov=t/180*Math.PI,this._calcMatrices())}get zoom(){return this._zoom}set zoom(t){const e=Math.min(Math.max(t,this.minZoom),this.maxZoom);this._zoom!==e&&(this._unmodified=!1,this._zoom=e,this.scale=this.zoomScale(e),this.tileZoom=Math.floor(e),this.zoomFraction=e-this.tileZoom,this._constrain(),this._calcMatrices())}get center(){return this._center}set center(t){t.lat===this._center.lat&&t.lng===this._center.lng||(this._unmodified=!1,this._center=t,this._constrain(),this._calcMatrices())}get elevation(){return this._elevation}set elevation(t){t!==this._elevation&&(this._elevation=t,this._constrain(),this._calcMatrices())}get padding(){return this._edgeInsets.toJSON()}set padding(t){this._edgeInsets.equals(t)||(this._unmodified=!1,this._edgeInsets.interpolate(this._edgeInsets,t,1),this._calcMatrices())}get centerPoint(){return this._edgeInsets.getCenter(this.width,this.height)}isPaddingEqual(t){return this._edgeInsets.equals(t)}interpolatePadding(t,e,i){this._unmodified=!1,this._edgeInsets.interpolate(t,e,i),this._constrain(),this._calcMatrices()}coveringZoomLevel(t){const e=(t.roundZoom?Math.round:Math.floor)(this.zoom+this.scaleZoom(this.tileSize/t.tileSize));return Math.max(0,e)}getVisibleUnwrappedCoordinates(e){const i=[new t.UnwrappedTileID(0,e)];if(this._renderWorldCopies){const r=this.pointCoordinate(new t.pointGeometry(0,0)),n=this.pointCoordinate(new t.pointGeometry(this.width,0)),s=this.pointCoordinate(new t.pointGeometry(this.width,this.height)),o=this.pointCoordinate(new t.pointGeometry(0,this.height)),a=Math.floor(Math.min(r.x,n.x,s.x,o.x)),l=Math.floor(Math.max(r.x,n.x,s.x,o.x)),c=1;for(let r=a-c;r<=l+c;r++)0!==r&&i.push(new t.UnwrappedTileID(r,e))}return i}coveringTiles(e){var i,r;let n=this.coveringZoomLevel(e);const s=n;if(void 0!==e.minzoom&&ne.maxzoom&&(n=e.maxzoom);const o=this.pointCoordinate(this.getCameraPoint()),a=t.MercatorCoordinate.fromLngLat(this.center),l=Math.pow(2,n),c=[l*o.x,l*o.y,0],h=[l*a.x,l*a.y,0],u=Qi.fromInvProjectionMatrix(this.invProjMatrix,this.worldSize,n);let p=e.minzoom||0;!e.terrain&&this.pitch<=60&&this._edgeInsets.top<.1&&(p=n);const d=e.terrain?2/Math.min(this.tileSize,e.tileSize)*this.tileSize:3,m=t=>({aabb:new tr([t*l,0,0],[(t+1)*l,l,0]),zoom:0,x:0,y:0,wrap:t,fullyVisible:!1}),f=[],g=[],_=n,y=e.reparseOverscaled?s:n;if(this._renderWorldCopies)for(let t=1;t<=3;t++)f.push(m(-t)),f.push(m(t));for(f.push(m(0));f.length>0;){const n=f.pop(),s=n.x,o=n.y;let a=n.fullyVisible;if(!a){const t=n.aabb.intersects(u);if(0===t)continue;a=2===t}const l=e.terrain?c:h,m=n.aabb.distanceX(l),x=n.aabb.distanceY(l),v=Math.max(Math.abs(m),Math.abs(x)),b=d+(1<<_-n.zoom)-2;if(n.zoom===_||v>b&&n.zoom>=p){const e=_-n.zoom,i=c[0]-.5-(s<>1),u=n.zoom+1;let p=n.aabb.quadrant(l);if(e.terrain){const s=new t.OverscaledTileID(u,n.wrap,u,c,h),o=e.terrain.getMinMaxElevation(s),a=null!==(i=o.minElevation)&&void 0!==i?i:this.elevation,l=null!==(r=o.maxElevation)&&void 0!==r?r:this.elevation;p=new tr([p.min[0],p.min[1],a],[p.max[0],p.max[1],l])}f.push({aabb:p,zoom:u,x:c,y:h,wrap:n.wrap,fullyVisible:a})}}return g.sort(((t,e)=>t.distanceSq-e.distanceSq)).map((t=>t.tileID))}resize(t,e){this.width=t,this.height=e,this.pixelsToGLUnits=[2/t,-2/e],this._constrain(),this._calcMatrices()}get unmodified(){return this._unmodified}zoomScale(t){return Math.pow(2,t)}scaleZoom(t){return Math.log(t)/Math.LN2}project(e){const i=t.clamp(e.lat,-this.maxValidLatitude,this.maxValidLatitude);return new t.pointGeometry(t.mercatorXfromLng(e.lng)*this.worldSize,t.mercatorYfromLat(i)*this.worldSize)}unproject(e){return new t.MercatorCoordinate(e.x/this.worldSize,e.y/this.worldSize).toLngLat()}get point(){return this.project(this.center)}updateElevation(t){this.freezeElevation||(this.elevation=t?this.getElevation(this._center,t):0)}getElevation(e,i){const r=t.MercatorCoordinate.fromLngLat(e),n=(1<o&&(n=o-e)}if(this.lngRange){const e=(a+l)/2,i=t.wrap(u.x,e-this.worldSize/2,e+this.worldSize/2),n=c.x/2;i-nl&&(r=l-n)}void 0===r&&void 0===n||(this.center=this.unproject(new t.pointGeometry(void 0!==r?r:u.x,void 0!==n?n:u.y)).wrap()),this._unmodified=h,this._constraining=!1}_calcMatrices(){if(!this.height)return;const e=this.centerOffset,i=this.point.x,r=this.point.y;this.cameraToCenterDistance=.5/Math.tan(this._fov/2)*this.height,this._pixelPerMeter=t.mercatorZfromAltitude(1,this.center.lat)*this.worldSize;let n=t.identity(new Float64Array(16));t.scale(n,n,[this.width/2,-this.height/2,1]),t.translate(n,n,[1,-1,0]),this.labelPlaneMatrix=n,n=t.identity(new Float64Array(16)),t.scale(n,n,[1,-1,1]),t.translate(n,n,[-1,-1,0]),t.scale(n,n,[2/this.width,2/this.height,1]),this.glCoordMatrix=n,this.cameraToSeaLevelDistance=this.cameraToCenterDistance+this._elevation*this._pixelPerMeter/Math.cos(this._pitch);const s=Math.PI/2+this._pitch,o=this._fov*(.5+e.y/this.height),a=Math.sin(o)*this.cameraToSeaLevelDistance/Math.sin(t.clamp(Math.PI-s-o,.01,Math.PI-.01)),l=this.getHorizon(),c=2*Math.atan(l/this.cameraToCenterDistance)*(.5+e.y/(2*l)),h=Math.sin(c)*this.cameraToSeaLevelDistance/Math.sin(t.clamp(Math.PI-s-c,.01,Math.PI-.01)),u=Math.cos(Math.PI/2-this._pitch)*a+this.cameraToSeaLevelDistance,p=Math.cos(Math.PI/2-this._pitch)*h+this.cameraToSeaLevelDistance,d=1.01*Math.min(u,p),m=this.height/50;n=new Float64Array(16),t.perspective(n,this._fov,this.width/this.height,m,d),n[8]=2*-e.x/this.width,n[9]=2*e.y/this.height,t.scale(n,n,[1,-1,1]),t.translate(n,n,[0,0,-this.cameraToCenterDistance]),t.rotateX(n,n,this._pitch),t.rotateZ(n,n,this.angle),t.translate(n,n,[-i,-r,0]),this.mercatorMatrix=t.scale([],n,[this.worldSize,this.worldSize,this.worldSize]),t.scale(n,n,[1,1,this._pixelPerMeter]),this.pixelMatrix=t.multiply(new Float64Array(16),this.labelPlaneMatrix,n),t.translate(n,n,[0,0,-this.elevation]),this.projMatrix=n,this.invProjMatrix=t.invert([],n),this.pixelMatrix3D=t.multiply(new Float64Array(16),this.labelPlaneMatrix,n);const f=this.width%2/2,g=this.height%2/2,_=Math.cos(this.angle),y=Math.sin(this.angle),x=i-Math.round(i)+_*f+y*g,v=r-Math.round(r)+_*g+y*f,b=new Float64Array(n);if(t.translate(b,b,[x>.5?x-1:x,v>.5?v-1:v,0]),this.alignedProjMatrix=b,n=t.invert(new Float64Array(16),this.pixelMatrix),!n)throw new Error("failed to invert matrix");this.pixelMatrixInverse=n,this._posMatrixCache={},this._alignedPosMatrixCache={}}maxPitchScaleFactor(){if(!this.pixelMatrixInverse)return 1;const e=this.pointCoordinate(new t.pointGeometry(0,0)),i=[e.x*this.worldSize,e.y*this.worldSize,0,1];return t.transformMat4(i,i,this.pixelMatrix)[3]/this.cameraToCenterDistance}getCameraPoint(){const e=Math.tan(this._pitch)*(this.cameraToCenterDistance||1);return this.centerPoint.add(new t.pointGeometry(0,e))}getCameraQueryGeometry(e){const i=this.getCameraPoint();if(1===e.length)return[e[0],i];{let r=i.x,n=i.y,s=i.x,o=i.y;for(const t of e)r=Math.min(r,t.x),n=Math.min(n,t.y),s=Math.max(s,t.x),o=Math.max(o,t.y);return[new t.pointGeometry(r,n),new t.pointGeometry(s,n),new t.pointGeometry(s,o),new t.pointGeometry(r,o),new t.pointGeometry(r,n)]}}}class rr{constructor(e){this._hashName=e&&encodeURIComponent(e),t.bindAll(["_getCurrentHash","_onHashChange","_updateHash"],this),this._updateHash=function(t,e){let i=!1,r=null;const n=()=>{r=null,i&&(t(),r=setTimeout(n,300),i=!1)};return()=>(i=!0,r||n(),r)}(this._updateHashUnthrottled.bind(this))}addTo(t){return this._map=t,addEventListener("hashchange",this._onHashChange,!1),this._map.on("moveend",this._updateHash),this}remove(){return removeEventListener("hashchange",this._onHashChange,!1),this._map.off("moveend",this._updateHash),clearTimeout(this._updateHash()),delete this._map,this}getHashString(t){const e=this._map.getCenter(),i=Math.round(100*this._map.getZoom())/100,r=Math.ceil((i*Math.LN2+Math.log(512/360/.5))/Math.LN10),n=Math.pow(10,r),s=Math.round(e.lng*n)/n,o=Math.round(e.lat*n)/n,a=this._map.getBearing(),l=this._map.getPitch();let c="";if(c+=t?`/${s}/${o}/${i}`:`${i}/${o}/${s}`,(a||l)&&(c+="/"+Math.round(10*a)/10),l&&(c+=`/${Math.round(l)}`),this._hashName){const t=this._hashName;let e=!1;const i=window.location.hash.slice(1).split("&").map((i=>{const r=i.split("=")[0];return r===t?(e=!0,`${r}=${c}`):i})).filter((t=>t));return e||i.push(`${t}=${c}`),`#${i.join("&")}`}return`#${c}`}_getCurrentHash(){const t=window.location.hash.replace("#","");if(this._hashName){let e;return t.split("&").map((t=>t.split("="))).forEach((t=>{t[0]===this._hashName&&(e=t)})),(e&&e[1]||"").split("/")}return t.split("/")}_onHashChange(){const t=this._getCurrentHash();if(t.length>=3&&!t.some((t=>isNaN(t)))){const e=this._map.dragRotate.isEnabled()&&this._map.touchZoomRotate.isEnabled()?+(t[3]||0):this._map.getBearing();return this._map.jumpTo({center:[+t[2],+t[1]],zoom:+t[0],bearing:e,pitch:+(t[4]||0)}),!0}return!1}_updateHashUnthrottled(){const t=window.location.href.replace(/(#.+)?$/,this.getHashString());try{window.history.replaceState(window.history.state,null,t)}catch(t){}}}const nr={linearity:.3,easing:t.bezier(0,0,.3,1)},sr=t.extend({deceleration:2500,maxSpeed:1400},nr),or=t.extend({deceleration:20,maxSpeed:1400},nr),ar=t.extend({deceleration:1e3,maxSpeed:360},nr),lr=t.extend({deceleration:1e3,maxSpeed:90},nr);class cr{constructor(t){this._map=t,this.clear()}clear(){this._inertiaBuffer=[]}record(e){this._drainInertiaBuffer(),this._inertiaBuffer.push({time:t.exported.now(),settings:e})}_drainInertiaBuffer(){const e=this._inertiaBuffer,i=t.exported.now();for(;e.length>0&&i-e[0].time>160;)e.shift()}_onMoveEnd(e){if(this._drainInertiaBuffer(),this._inertiaBuffer.length<2)return;const i={zoom:0,bearing:0,pitch:0,pan:new t.pointGeometry(0,0),pinchAround:void 0,around:void 0};for(const{settings:t}of this._inertiaBuffer)i.zoom+=t.zoomDelta||0,i.bearing+=t.bearingDelta||0,i.pitch+=t.pitchDelta||0,t.panDelta&&i.pan._add(t.panDelta),t.around&&(i.around=t.around),t.pinchAround&&(i.pinchAround=t.pinchAround);const r=this._inertiaBuffer[this._inertiaBuffer.length-1].time-this._inertiaBuffer[0].time,n={};if(i.pan.mag()){const s=ur(i.pan.mag(),r,t.extend({},sr,e||{}));n.offset=i.pan.mult(s.amount/i.pan.mag()),n.center=this._map.transform.center,hr(n,s)}if(i.zoom){const t=ur(i.zoom,r,or);n.zoom=this._map.transform.zoom+t.amount,hr(n,t)}if(i.bearing){const e=ur(i.bearing,r,ar);n.bearing=this._map.transform.bearing+t.clamp(e.amount,-179,179),hr(n,e)}if(i.pitch){const t=ur(i.pitch,r,lr);n.pitch=this._map.transform.pitch+t.amount,hr(n,t)}if(n.zoom||n.bearing){const t=void 0===i.pinchAround?i.around:i.pinchAround;n.around=t?this._map.unproject(t):this._map.getCenter()}return this.clear(),t.extend(n,{noMoveStart:!0})}}function hr(t,e){(!t.duration||t.durationi.unproject(t))),l=o.reduce(((t,e,i,r)=>t.add(e.div(r.length))),new t.pointGeometry(0,0));super(e,{points:o,point:l,lngLats:a,lngLat:i.unproject(l),originalEvent:r}),this._defaultPrevented=!1}preventDefault(){this._defaultPrevented=!0}get defaultPrevented(){return this._defaultPrevented}}class mr extends t.Event{constructor(t,e,i){super(t,{originalEvent:i}),this._defaultPrevented=!1}preventDefault(){this._defaultPrevented=!0}get defaultPrevented(){return this._defaultPrevented}}class fr{constructor(t,e){this._map=t,this._clickTolerance=e.clickTolerance}reset(){delete this._mousedownPos}wheel(t){return this._firePreventable(new mr(t.type,this._map,t))}mousedown(t,e){return this._mousedownPos=e,this._firePreventable(new pr(t.type,this._map,t))}mouseup(t){this._map.fire(new pr(t.type,this._map,t))}click(t,e){this._mousedownPos&&this._mousedownPos.dist(e)>=this._clickTolerance||this._map.fire(new pr(t.type,this._map,t))}dblclick(t){return this._firePreventable(new pr(t.type,this._map,t))}mouseover(t){this._map.fire(new pr(t.type,this._map,t))}mouseout(t){this._map.fire(new pr(t.type,this._map,t))}touchstart(t){return this._firePreventable(new dr(t.type,this._map,t))}touchmove(t){this._map.fire(new dr(t.type,this._map,t))}touchend(t){this._map.fire(new dr(t.type,this._map,t))}touchcancel(t){this._map.fire(new dr(t.type,this._map,t))}_firePreventable(t){if(this._map.fire(t),t.defaultPrevented)return{}}isEnabled(){return!0}isActive(){return!1}enable(){}disable(){}}class gr{constructor(t){this._map=t}reset(){this._delayContextMenu=!1,delete this._contextMenuEvent}mousemove(t){this._map.fire(new pr(t.type,this._map,t))}mousedown(){this._delayContextMenu=!0}mouseup(){this._delayContextMenu=!1,this._contextMenuEvent&&(this._map.fire(new pr("contextmenu",this._map,this._contextMenuEvent)),delete this._contextMenuEvent)}contextmenu(t){this._delayContextMenu?this._contextMenuEvent=t:this._map.fire(new pr(t.type,this._map,t)),this._map.listens("contextmenu")&&t.preventDefault()}isEnabled(){return!0}isActive(){return!1}enable(){}disable(){}}class _r{constructor(t,e){this._map=t,this._el=t.getCanvasContainer(),this._container=t.getContainer(),this._clickTolerance=e.clickTolerance||1}isEnabled(){return!!this._enabled}isActive(){return!!this._active}enable(){this.isEnabled()||(this._enabled=!0)}disable(){this.isEnabled()&&(this._enabled=!1)}mousedown(t,e){this.isEnabled()&&t.shiftKey&&0===t.button&&(s.disableDrag(),this._startPos=this._lastPos=e,this._active=!0)}mousemoveWindow(t,e){if(!this._active)return;const i=e;if(this._lastPos.equals(i)||!this._box&&i.dist(this._startPos)t.fitScreenCoordinates(r,n,this._map.getBearing(),{linear:!0})};this._fireEvent("boxzoomcancel",e)}keydown(t){this._active&&27===t.keyCode&&(this.reset(),this._fireEvent("boxzoomcancel",t))}reset(){this._active=!1,this._container.classList.remove("maplibregl-crosshair","mapboxgl-crosshair"),this._box&&(s.remove(this._box),this._box=null),s.enableDrag(),delete this._startPos,delete this._lastPos}_fireEvent(e,i){return this._map.fire(new t.Event(e,{originalEvent:i}))}}function yr(t,e){const i={};for(let r=0;rthis.numTouches)&&(this.aborted=!0),this.aborted||(void 0===this.startTime&&(this.startTime=e.timeStamp),r.length===this.numTouches&&(this.centroid=function(e){const i=new t.pointGeometry(0,0);for(const t of e)i._add(t);return i.div(e.length)}(i),this.touches=yr(r,i)))}touchmove(t,e,i){if(this.aborted||!this.centroid)return;const r=yr(i,e);for(const t in this.touches){const e=this.touches[t],i=r[t];(!i||i.dist(e)>30)&&(this.aborted=!0)}}touchend(t,e,i){if((!this.centroid||t.timeStamp-this.startTime>500)&&(this.aborted=!0),0===i.length){const t=!this.aborted&&this.centroid;if(this.reset(),t)return t}}}class vr{constructor(t){this.singleTap=new xr(t),this.numTaps=t.numTaps,this.reset()}reset(){this.lastTime=1/0,delete this.lastTap,this.count=0,this.singleTap.reset()}touchstart(t,e,i){this.singleTap.touchstart(t,e,i)}touchmove(t,e,i){this.singleTap.touchmove(t,e,i)}touchend(t,e,i){const r=this.singleTap.touchend(t,e,i);if(r){const e=t.timeStamp-this.lastTime<500,i=!this.lastTap||this.lastTap.dist(r)<30;if(e&&i||this.reset(),this.count++,this.lastTime=t.timeStamp,this.lastTap=r,this.count===this.numTaps)return this.reset(),r}}}class br{constructor(){this._zoomIn=new vr({numTouches:1,numTaps:2}),this._zoomOut=new vr({numTouches:2,numTaps:1}),this.reset()}reset(){this._active=!1,this._zoomIn.reset(),this._zoomOut.reset()}touchstart(t,e,i){this._zoomIn.touchstart(t,e,i),this._zoomOut.touchstart(t,e,i)}touchmove(t,e,i){this._zoomIn.touchmove(t,e,i),this._zoomOut.touchmove(t,e,i)}touchend(t,e,i){const r=this._zoomIn.touchend(t,e,i),n=this._zoomOut.touchend(t,e,i);return r?(this._active=!0,t.preventDefault(),setTimeout((()=>this.reset()),0),{cameraAnimation:e=>e.easeTo({duration:300,zoom:e.getZoom()+1,around:e.unproject(r)},{originalEvent:t})}):n?(this._active=!0,t.preventDefault(),setTimeout((()=>this.reset()),0),{cameraAnimation:e=>e.easeTo({duration:300,zoom:e.getZoom()-1,around:e.unproject(n)},{originalEvent:t})}):void 0}touchcancel(){this.reset()}enable(){this._enabled=!0}disable(){this._enabled=!1,this.reset()}isEnabled(){return this._enabled}isActive(){return this._active}}const wr={0:1,2:2};class Tr{constructor(t){this.reset(),this._clickTolerance=t.clickTolerance||1}reset(){this._active=!1,this._moved=!1,delete this._lastPoint,delete this._eventButton}_correctButton(t,e){return!1}_move(t,e){return{}}mousedown(t,e){if(this._lastPoint)return;const i=s.mouseButton(t);this._correctButton(t,i)&&(this._lastPoint=e,this._eventButton=i)}mousemoveWindow(t,e){const i=this._lastPoint;if(i)if(t.preventDefault(),function(t,e){const i=wr[e];return void 0===t.buttons||(t.buttons&i)!==i}(t,this._eventButton))this.reset();else if(this._moved||!(e.dist(i){this._cancelCooperativeMessage=!1}),200)}touchstart(t,e,i){return this._calculateTransform(t,e,i)}touchmove(t,e,i){if(this._map._cooperativeGestures&&(2===this._minTouches&&i.length<2&&!this._cancelCooperativeMessage?this._map._onCooperativeGesture(t,!1,i.length):this._cancelCooperativeMessage||(this._cancelCooperativeMessage=!0)),this._active&&!(i.length0&&(this._active=!0);const n=yr(r,i),s=new t.pointGeometry(0,0),o=new t.pointGeometry(0,0);let a=0;for(const t in n){const e=n[t],i=this._touches[t];i&&(s._add(e),o._add(e.sub(i)),a++,n[t]=e)}if(this._touches=n,aMath.abs(t.x)}class Br extends zr{constructor(t){super(),this._map=t}reset(){super.reset(),this._valid=void 0,delete this._firstMove,delete this._lastPoints}touchstart(t,e,i){super.touchstart(t,e,i),this._currentTouchCount=i.length}_start(t){this._lastPoints=t,Lr(t[0].sub(t[1]))&&(this._valid=!1)}_move(t,e,i){if(this._map._cooperativeGestures&&this._currentTouchCount<3)return;const r=t[0].sub(this._lastPoints[0]),n=t[1].sub(this._lastPoints[1]);return this._valid=this.gestureBeginsVertically(r,n,i.timeStamp),this._valid?(this._lastPoints=t,this._active=!0,{pitchDelta:(r.y+n.y)/2*-.5}):void 0}gestureBeginsVertically(t,e,i){if(void 0!==this._valid)return this._valid;const r=t.mag()>=2,n=e.mag()>=2;if(!r&&!n)return;if(!r||!n)return void 0===this._firstMove&&(this._firstMove=i),i-this._firstMove<100&&void 0;const s=t.y>0==e.y>0;return Lr(t)&&Lr(e)&&s}}const Rr={panStep:100,bearingStep:15,pitchStep:10};class Fr{constructor(){const t=Rr;this._panStep=t.panStep,this._bearingStep=t.bearingStep,this._pitchStep=t.pitchStep,this._rotationDisabled=!1}reset(){this._active=!1}keydown(t){if(t.altKey||t.ctrlKey||t.metaKey)return;let e=0,i=0,r=0,n=0,s=0;switch(t.keyCode){case 61:case 107:case 171:case 187:e=1;break;case 189:case 109:case 173:e=-1;break;case 37:t.shiftKey?i=-1:(t.preventDefault(),n=-1);break;case 39:t.shiftKey?i=1:(t.preventDefault(),n=1);break;case 38:t.shiftKey?r=1:(t.preventDefault(),s=-1);break;case 40:t.shiftKey?r=-1:(t.preventDefault(),s=1);break;default:return}return this._rotationDisabled&&(i=0,r=0),{cameraAnimation:o=>{const a=o.getZoom();o.easeTo({duration:300,easeId:"keyboardHandler",easing:Or,zoom:e?Math.round(a)+e*(t.shiftKey?2:1):a,bearing:o.getBearing()+i*this._bearingStep,pitch:o.getPitch()+r*this._pitchStep,offset:[-n*this._panStep,-s*this._panStep],center:o.getCenter()},{originalEvent:t})}}}enable(){this._enabled=!0}disable(){this._enabled=!1,this.reset()}isEnabled(){return this._enabled}isActive(){return this._active}disableRotation(){this._rotationDisabled=!0}enableRotation(){this._rotationDisabled=!1}}function Or(t){return t*(2-t)}const Ur=4.000244140625;class Vr{constructor(e,i){this._map=e,this._el=e.getCanvasContainer(),this._handler=i,this._delta=0,this._defaultZoomRate=.01,this._wheelZoomRate=.0022222222222222222,t.bindAll(["_onTimeout"],this)}setZoomRate(t){this._defaultZoomRate=t}setWheelZoomRate(t){this._wheelZoomRate=t}isEnabled(){return!!this._enabled}isActive(){return!!this._active||void 0!==this._finishTimeout}isZooming(){return!!this._zooming}enable(t){this.isEnabled()||(this._enabled=!0,this._aroundCenter=t&&"center"===t.around)}disable(){this.isEnabled()&&(this._enabled=!1)}wheel(e){if(!this.isEnabled())return;if(this._map._cooperativeGestures){if(!this._map._metaPress)return;e.preventDefault()}let i=e.deltaMode===WheelEvent.DOM_DELTA_LINE?40*e.deltaY:e.deltaY;const r=t.exported.now(),n=r-(this._lastWheelEventTime||0);this._lastWheelEventTime=r,0!==i&&i%Ur==0?this._type="wheel":0!==i&&Math.abs(i)<4?this._type="trackpad":n>400?(this._type=null,this._lastValue=i,this._timeout=setTimeout(this._onTimeout,40,e)):this._type||(this._type=Math.abs(n*i)<200?"trackpad":"wheel",this._timeout&&(clearTimeout(this._timeout),this._timeout=null,i+=this._lastValue)),e.shiftKey&&i&&(i/=4),this._type&&(this._lastWheelEvent=e,this._delta-=i,this._active||this._start(e)),e.preventDefault()}_onTimeout(t){this._type="wheel",this._delta-=this._lastValue,this._active||this._start(t)}_start(e){if(!this._delta)return;this._frameId&&(this._frameId=null),this._active=!0,this.isZooming()||(this._zooming=!0),this._finishTimeout&&(clearTimeout(this._finishTimeout),delete this._finishTimeout);const i=s.mousePos(this._el,e);this._around=t.LngLat.convert(this._aroundCenter?this._map.getCenter():this._map.unproject(i)),this._aroundPoint=this._map.transform.locationPoint(this._around),this._frameId||(this._frameId=!0,this._handler._triggerRenderFrame())}renderFrame(){if(!this._frameId)return;if(this._frameId=null,!this.isActive())return;const e=this._map.transform;if(0!==this._delta){const t="wheel"===this._type&&Math.abs(this._delta)>Ur?this._wheelZoomRate:this._defaultZoomRate;let i=2/(1+Math.exp(-Math.abs(this._delta*t)));this._delta<0&&0!==i&&(i=1/i);const r="number"==typeof this._targetZoom?e.zoomScale(this._targetZoom):e.scale;this._targetZoom=Math.min(e.maxZoom,Math.max(e.minZoom,e.scaleZoom(r*i))),"wheel"===this._type&&(this._startZoom=e.zoom,this._easing=this._smoothOutEasing(200)),this._delta=0}const i="number"==typeof this._targetZoom?this._targetZoom:e.zoom,r=this._startZoom,n=this._easing;let s,o=!1;if("wheel"===this._type&&r&&n){const e=Math.min((t.exported.now()-this._lastWheelEventTime)/200,1),a=n(e);s=t.number(r,i,a),e<1?this._frameId||(this._frameId=!0):o=!0}else s=i,o=!0;return this._active=!0,o&&(this._active=!1,this._finishTimeout=setTimeout((()=>{this._zooming=!1,this._handler._triggerRenderFrame(),delete this._targetZoom,delete this._finishTimeout}),200)),{noInertia:!0,needsRenderFrame:!o,zoomDelta:s-e.zoom,around:this._aroundPoint,originalEvent:this._lastWheelEvent}}_smoothOutEasing(e){let i=t.ease;if(this._prevEase){const e=this._prevEase,r=(t.exported.now()-e.start)/e.duration,n=e.easing(r+.01)-e.easing(r),s=.27/Math.sqrt(n*n+1e-4)*.01,o=Math.sqrt(.0729-s*s);i=t.bezier(s,o,.25,1)}return this._prevEase={start:t.exported.now(),duration:e,easing:i},i}reset(){this._active=!1}}class Nr{constructor(t,e){this._clickZoom=t,this._tapZoom=e}enable(){this._clickZoom.enable(),this._tapZoom.enable()}disable(){this._clickZoom.disable(),this._tapZoom.disable()}isEnabled(){return this._clickZoom.isEnabled()&&this._tapZoom.isEnabled()}isActive(){return this._clickZoom.isActive()||this._tapZoom.isActive()}}class Gr{constructor(){this.reset()}reset(){this._active=!1}dblclick(t,e){return t.preventDefault(),{cameraAnimation:i=>{i.easeTo({duration:300,zoom:i.getZoom()+(t.shiftKey?-1:1),around:i.unproject(e)},{originalEvent:t})}}}enable(){this._enabled=!0}disable(){this._enabled=!1,this.reset()}isEnabled(){return this._enabled}isActive(){return this._active}}class $r{constructor(){this._tap=new vr({numTouches:1,numTaps:1}),this.reset()}reset(){this._active=!1,delete this._swipePoint,delete this._swipeTouch,delete this._tapTime,this._tap.reset()}touchstart(t,e,i){this._swipePoint||(this._tapTime&&t.timeStamp-this._tapTime>500&&this.reset(),this._tapTime?i.length>0&&(this._swipePoint=e[0],this._swipeTouch=i[0].identifier):this._tap.touchstart(t,e,i))}touchmove(t,e,i){if(this._tapTime){if(this._swipePoint){if(i[0].identifier!==this._swipeTouch)return;const r=e[0],n=r.y-this._swipePoint.y;return this._swipePoint=r,t.preventDefault(),this._active=!0,{zoomDelta:n/128}}}else this._tap.touchmove(t,e,i)}touchend(t,e,i){this._tapTime?this._swipePoint&&0===i.length&&this.reset():this._tap.touchend(t,e,i)&&(this._tapTime=t.timeStamp)}touchcancel(){this.reset()}enable(){this._enabled=!0}disable(){this._enabled=!1,this.reset()}isEnabled(){return this._enabled}isActive(){return this._active}}class qr{constructor(t,e,i){this._el=t,this._mousePan=e,this._touchPan=i}enable(t){this._inertiaOptions=t||{},this._mousePan.enable(),this._touchPan.enable(),this._el.classList.add("maplibregl-touch-drag-pan","mapboxgl-touch-drag-pan")}disable(){this._mousePan.disable(),this._touchPan.disable(),this._el.classList.remove("maplibregl-touch-drag-pan","mapboxgl-touch-drag-pan")}isEnabled(){return this._mousePan.isEnabled()&&this._touchPan.isEnabled()}isActive(){return this._mousePan.isActive()||this._touchPan.isActive()}}class jr{constructor(t,e,i){this._pitchWithRotate=t.pitchWithRotate,this._mouseRotate=e,this._mousePitch=i}enable(){this._mouseRotate.enable(),this._pitchWithRotate&&this._mousePitch.enable()}disable(){this._mouseRotate.disable(),this._mousePitch.disable()}isEnabled(){return this._mouseRotate.isEnabled()&&(!this._pitchWithRotate||this._mousePitch.isEnabled())}isActive(){return this._mouseRotate.isActive()||this._mousePitch.isActive()}}class Zr{constructor(t,e,i,r){this._el=t,this._touchZoom=e,this._touchRotate=i,this._tapDragZoom=r,this._rotationDisabled=!1,this._enabled=!0}enable(t){this._touchZoom.enable(t),this._rotationDisabled||this._touchRotate.enable(t),this._tapDragZoom.enable(),this._el.classList.add("maplibregl-touch-zoom-rotate","mapboxgl-touch-zoom-rotate")}disable(){this._touchZoom.disable(),this._touchRotate.disable(),this._tapDragZoom.disable(),this._el.classList.remove("maplibregl-touch-zoom-rotate","mapboxgl-touch-zoom-rotate")}isEnabled(){return this._touchZoom.isEnabled()&&(this._rotationDisabled||this._touchRotate.isEnabled())&&this._tapDragZoom.isEnabled()}isActive(){return this._touchZoom.isActive()||this._touchRotate.isActive()||this._tapDragZoom.isActive()}disableRotation(){this._rotationDisabled=!0,this._touchRotate.disable()}enableRotation(){this._rotationDisabled=!1,this._touchZoom.isEnabled()&&this._touchRotate.enable()}}const Xr=t=>t.zoom||t.drag||t.pitch||t.rotate;class Wr extends t.Event{}function Hr(t){return t.panDelta&&t.panDelta.mag()||t.zoomDelta||t.bearingDelta||t.pitchDelta}class Kr{constructor(e,i){this._map=e,this._el=this._map.getCanvasContainer(),this._handlers=[],this._handlersById={},this._changes=[],this._inertia=new cr(e),this._bearingSnap=i.bearingSnap,this._previousActiveHandlers={},this._eventsInProgress={},this._addDefaultHandlers(i),t.bindAll(["handleEvent","handleWindowEvent"],this);const r=this._el;this._listeners=[[r,"touchstart",{passive:!0}],[r,"touchmove",{passive:!1}],[r,"touchend",void 0],[r,"touchcancel",void 0],[r,"mousedown",void 0],[r,"mousemove",void 0],[r,"mouseup",void 0],[document,"mousemove",{capture:!0}],[document,"mouseup",void 0],[r,"mouseover",void 0],[r,"mouseout",void 0],[r,"dblclick",void 0],[r,"click",void 0],[r,"keydown",{capture:!1}],[r,"keyup",void 0],[r,"wheel",{passive:!1}],[r,"contextmenu",void 0],[window,"blur",void 0]];for(const[t,e,i]of this._listeners)s.addEventListener(t,e,t===document?this.handleWindowEvent:this.handleEvent,i)}destroy(){for(const[t,e,i]of this._listeners)s.removeEventListener(t,e,t===document?this.handleWindowEvent:this.handleEvent,i)}_addDefaultHandlers(t){const e=this._map,i=e.getCanvasContainer();this._add("mapEvent",new fr(e,t));const r=e.boxZoom=new _r(e,t);this._add("boxZoom",r);const n=new br,s=new Gr;e.doubleClickZoom=new Nr(s,n),this._add("tapZoom",n),this._add("clickZoom",s);const o=new $r;this._add("tapDragZoom",o);const a=e.touchPitch=new Br(e);this._add("touchPitch",a);const l=new Sr(t),c=new Ir(t);e.dragRotate=new jr(t,l,c),this._add("mouseRotate",l,["mousePitch"]),this._add("mousePitch",c,["mouseRotate"]);const h=new Er(t),u=new Ar(t,e);e.dragPan=new qr(i,h,u),this._add("mousePan",h),this._add("touchPan",u,["touchZoom","touchRotate"]);const p=new Dr,d=new kr;e.touchZoomRotate=new Zr(i,d,p,o),this._add("touchRotate",p,["touchPan","touchZoom"]),this._add("touchZoom",d,["touchPan","touchRotate"]);const m=e.scrollZoom=new Vr(e,this);this._add("scrollZoom",m,["mousePan"]);const f=e.keyboard=new Fr;this._add("keyboard",f),this._add("blockableMapEvent",new gr(e));for(const i of["boxZoom","doubleClickZoom","tapDragZoom","touchPitch","dragRotate","dragPan","touchZoomRotate","scrollZoom","keyboard"])t.interactive&&t[i]&&e[i].enable(t[i])}_add(t,e,i){this._handlers.push({handlerName:t,handler:e,allowed:i}),this._handlersById[t]=e}stop(t){if(!this._updatingCamera){for(const{handler:t}of this._handlers)t.reset();this._inertia.clear(),this._fireEvents({},{},t),this._changes=[]}}isActive(){for(const{handler:t}of this._handlers)if(t.isActive())return!0;return!1}isZooming(){return!!this._eventsInProgress.zoom||this._map.scrollZoom.isZooming()}isRotating(){return!!this._eventsInProgress.rotate}isMoving(){return Boolean(Xr(this._eventsInProgress))||this.isZooming()}_blockedByActive(t,e,i){for(const r in t)if(r!==i&&(!e||e.indexOf(r)<0))return!0;return!1}handleWindowEvent(t){this.handleEvent(t,`${t.type}Window`)}_getMapTouches(t){const e=[];for(const i of t)this._el.contains(i.target)&&e.push(i);return e}handleEvent(t,e){if("blur"===t.type)return void this.stop(!0);this._updatingCamera=!0;const i="renderFrame"===t.type?void 0:t,r={needsRenderFrame:!1},n={},o={},a=t.touches,l=a?this._getMapTouches(a):void 0,c=l?s.touchPos(this._el,l):s.mousePos(this._el,t);for(const{handlerName:s,handler:a,allowed:h}of this._handlers){if(!a.isEnabled())continue;let u;this._blockedByActive(o,h,s)?a.reset():a[e||t.type]&&(u=a[e||t.type](t,c,l),this.mergeHandlerResult(r,n,u,s,i),u&&u.needsRenderFrame&&this._triggerRenderFrame()),(u||a.isActive())&&(o[s]=a)}const h={};for(const t in this._previousActiveHandlers)o[t]||(h[t]=i);this._previousActiveHandlers=o,(Object.keys(h).length||Hr(r))&&(this._changes.push([r,n,h]),this._triggerRenderFrame()),(Object.keys(o).length||Hr(r))&&this._map._stop(!0),this._updatingCamera=!1;const{cameraAnimation:u}=r;u&&(this._inertia.clear(),this._fireEvents({},{},!0),this._changes=[],u(this._map))}mergeHandlerResult(e,i,r,n,s){if(!r)return;t.extend(e,r);const o={handlerName:n,originalEvent:r.originalEvent||s};void 0!==r.zoomDelta&&(i.zoom=o),void 0!==r.panDelta&&(i.drag=o),void 0!==r.pitchDelta&&(i.pitch=o),void 0!==r.bearingDelta&&(i.rotate=o)}_applyChanges(){const e={},i={},r={};for(const[n,s,o]of this._changes)n.panDelta&&(e.panDelta=(e.panDelta||new t.pointGeometry(0,0))._add(n.panDelta)),n.zoomDelta&&(e.zoomDelta=(e.zoomDelta||0)+n.zoomDelta),n.bearingDelta&&(e.bearingDelta=(e.bearingDelta||0)+n.bearingDelta),n.pitchDelta&&(e.pitchDelta=(e.pitchDelta||0)+n.pitchDelta),void 0!==n.around&&(e.around=n.around),void 0!==n.pinchAround&&(e.pinchAround=n.pinchAround),n.noInertia&&(e.noInertia=n.noInertia),t.extend(i,s),t.extend(r,o);this._updateMapTransform(e,i,r),this._changes=[]}_updateMapTransform(e,i,r){const n=this._map,s=n.transform,o=n.style&&n.style.terrain;if(!(Hr(e)||o&&this._drag))return this._fireEvents(i,r,!0);let{panDelta:a,zoomDelta:l,bearingDelta:c,pitchDelta:h,around:u,pinchAround:p}=e;void 0!==p&&(u=p),n._stop(!0),u=u||n.transform.centerPoint;const d=s.pointLocation(a?u.sub(a):u);c&&(s.bearing+=c),h&&(s.pitch+=h),l&&(s.zoom+=l),o?i.drag&&!this._drag?(this._drag={center:s.centerPoint,lngLat:s.pointLocation(u),point:u,handlerName:i.drag.handlerName},n.fire(new t.Event("freezeElevation",{freeze:!0}))):this._drag&&r[this._drag.handlerName]?(n.fire(new t.Event("freezeElevation",{freeze:!1})),this._drag=null):i.drag&&this._drag&&(s.center=s.pointLocation(s.centerPoint.sub(a))):s.setLocationAtPoint(d,u),this._map._update(),e.noInertia||this._inertia.record(e),this._fireEvents(i,r,!0)}_fireEvents(e,i,r){const n=Xr(this._eventsInProgress),s=Xr(e),o={};for(const t in e){const{originalEvent:i}=e[t];this._eventsInProgress[t]||(o[`${t}start`]=i),this._eventsInProgress[t]=e[t]}!n&&s&&this._fireEvent("movestart",s.originalEvent);for(const t in o)this._fireEvent(t,o[t]);s&&this._fireEvent("move",s.originalEvent);for(const t in e){const{originalEvent:i}=e[t];this._fireEvent(t,i)}const a={};let l;for(const t in this._eventsInProgress){const{handlerName:e,originalEvent:r}=this._eventsInProgress[t];this._handlersById[e].isActive()||(delete this._eventsInProgress[t],l=i[e]||r,a[`${t}end`]=l)}for(const t in a)this._fireEvent(t,a[t]);const c=Xr(this._eventsInProgress);if(r&&(n||s)&&!c){this._updatingCamera=!0;const e=this._inertia._onMoveEnd(this._map.dragPan._inertiaOptions),i=t=>0!==t&&-this._bearingSnap{delete this._frameId,this.handleEvent(new Wr("renderFrame",{timeStamp:t})),this._applyChanges()}))}_triggerRenderFrame(){void 0===this._frameId&&(this._frameId=this._requestFrame())}}const Jr={extend:(e,...i)=>t.extend(e,...i),run(t){t()},logToElement(t,e=!1,i="log"){const r=window.document.getElementById(i);r&&(e&&(r.innerHTML=""),r.innerHTML+=`
${t}`)}};class Yr extends t.Evented{constructor(e,i){super(),this._moving=!1,this._zooming=!1,this.transform=e,this._bearingSnap=i.bearingSnap,t.bindAll(["_renderFrameCallback"],this)}getCenter(){return new t.LngLat(this.transform.center.lng,this.transform.center.lat)}setCenter(t,e){return this.jumpTo({center:t},e)}panBy(e,i,r){return e=t.pointGeometry.convert(e).mult(-1),this.panTo(this.transform.center,t.extend({offset:e},i),r)}panTo(e,i,r){return this.easeTo(t.extend({center:e},i),r)}getZoom(){return this.transform.zoom}setZoom(t,e){return this.jumpTo({zoom:t},e),this}zoomTo(e,i,r){return this.easeTo(t.extend({zoom:e},i),r)}zoomIn(t,e){return this.zoomTo(this.getZoom()+1,t,e),this}zoomOut(t,e){return this.zoomTo(this.getZoom()-1,t,e),this}getBearing(){return this.transform.bearing}setBearing(t,e){return this.jumpTo({bearing:t},e),this}getPadding(){return this.transform.padding}setPadding(t,e){return this.jumpTo({padding:t},e),this}rotateTo(e,i,r){return this.easeTo(t.extend({bearing:e},i),r)}resetNorth(e,i){return this.rotateTo(0,t.extend({duration:1e3},e),i),this}resetNorthPitch(e,i){return this.easeTo(t.extend({bearing:0,pitch:0,duration:1e3},e),i),this}snapToNorth(t,e){return Math.abs(this.getBearing()){if(this._zooming&&(r.zoom=t.number(n,l,e)),this._rotating&&(r.bearing=t.number(s,c,e)),this._pitching&&(r.pitch=t.number(o,h,e)),this._padding&&(r.interpolatePadding(a,u,e),d=r.centerPoint.add(p)),x)r.setLocationAtPoint(x,v);else{const t=r.zoomScale(r.zoom-n),i=l>n?Math.min(2,y):Math.max(.5,y),s=Math.pow(i,1-e),o=r.unproject(g.add(_.mult(e*s)).mult(t));r.setLocationAtPoint(r.renderWorldCopies?o.wrap():o,d)}this._fireMoveEvents(i)}),(t=>{this._afterEase(i,t)}),e),this}_prepareEase(e,i,r={}){this._moving=!0,this.fire(new t.Event("freezeElevation",{freeze:!0})),i||r.moving||this.fire(new t.Event("movestart",e)),this._zooming&&!r.zooming&&this.fire(new t.Event("zoomstart",e)),this._rotating&&!r.rotating&&this.fire(new t.Event("rotatestart",e)),this._pitching&&!r.pitching&&this.fire(new t.Event("pitchstart",e))}_fireMoveEvents(e){this.fire(new t.Event("move",e)),this._zooming&&this.fire(new t.Event("zoom",e)),this._rotating&&this.fire(new t.Event("rotate",e)),this._pitching&&this.fire(new t.Event("pitch",e))}_afterEase(e,i){if(this._easeId&&i&&this._easeId===i)return;delete this._easeId,this.fire(new t.Event("freezeElevation",{freeze:!1}));const r=this._zooming,n=this._rotating,s=this._pitching;this._moving=!1,this._zooming=!1,this._rotating=!1,this._pitching=!1,this._padding=!1,r&&this.fire(new t.Event("zoomend",e)),n&&this.fire(new t.Event("rotateend",e)),s&&this.fire(new t.Event("pitchend",e)),this.fire(new t.Event("moveend",e))}flyTo(e,i){if(!e.essential&&t.exported.prefersReducedMotion){const r=t.pick(e,["center","zoom","bearing","pitch","around"]);return this.jumpTo(r,i)}this.stop(),e=t.extend({offset:[0,0],speed:1.2,curve:1.42,easing:t.ease},e);const r=this.transform,n=this.getZoom(),s=this.getBearing(),o=this.getPitch(),a=this.getPadding(),l="zoom"in e?t.clamp(+e.zoom,r.minZoom,r.maxZoom):n,c="bearing"in e?this._normalizeBearing(e.bearing,s):s,h="pitch"in e?+e.pitch:o,u="padding"in e?e.padding:r.padding,p=r.zoomScale(l-n),d=t.pointGeometry.convert(e.offset);let m=r.centerPoint.add(d);const f=r.pointLocation(m),g=t.LngLat.convert(e.center||f);this._normalizeCenter(g);const _=r.project(f),y=r.project(g).sub(_);let x=e.curve;const v=Math.max(r.width,r.height),b=v/p,w=y.mag();if("minZoom"in e){const i=t.clamp(Math.min(e.minZoom,n,l),r.minZoom,r.maxZoom),s=v/r.zoomScale(i-n);x=Math.sqrt(s/w*2)}const T=x*x;function E(t){const e=(b*b-v*v+(t?-1:1)*T*T*w*w)/(2*(t?b:v)*T*w);return Math.log(Math.sqrt(e*e+1)-e)}function S(t){return(Math.exp(t)-Math.exp(-t))/2}function I(t){return(Math.exp(t)+Math.exp(-t))/2}const A=E(0);let z=function(t){return I(A)/I(A+x*t)},C=function(t){return v*((I(A)*(S(e=A+x*t)/I(e))-S(A))/T)/w;var e},M=(E(1)-A)/x;if(Math.abs(w)<1e-6||!isFinite(M)){if(Math.abs(v-b)<1e-6)return this.easeTo(e,i);const t=be.maxDuration&&(e.duration=0),this._zooming=!0,this._rotating=s!==c,this._pitching=h!==o,this._padding=!r.isPaddingEqual(u),this._prepareEase(i,!1),this._ease((e=>{const p=e*M,f=1/z(p);r.zoom=1===e?l:n+r.scaleZoom(f),this._rotating&&(r.bearing=t.number(s,c,e)),this._pitching&&(r.pitch=t.number(o,h,e)),this._padding&&(r.interpolatePadding(a,u,e),m=r.centerPoint.add(d));const x=1===e?g:r.unproject(_.add(y.mult(C(p))).mult(f));r.setLocationAtPoint(r.renderWorldCopies?x.wrap():x,m),this._fireMoveEvents(i)}),(()=>this._afterEase(i)),e),this}isEasing(){return!!this._easeFrameId}stop(){return this._stop()}_stop(t,e){if(this._easeFrameId&&(this._cancelRenderFrame(this._easeFrameId),delete this._easeFrameId,delete this._onEaseFrame),this._onEaseEnd){const t=this._onEaseEnd;delete this._onEaseEnd,t.call(this,e)}if(!t){const t=this.handlers;t&&t.stop(!1)}return this}_ease(e,i,r){!1===r.animate||0===r.duration?(e(1),i()):(this._easeStart=t.exported.now(),this._easeOptions=r,this._onEaseFrame=e,this._onEaseEnd=i,this._easeFrameId=this._requestRenderFrame(this._renderFrameCallback))}_renderFrameCallback(){const e=Math.min((t.exported.now()-this._easeStart)/this._easeOptions.duration,1);this._onEaseFrame(this._easeOptions.easing(e)),e<1?this._easeFrameId=this._requestRenderFrame(this._renderFrameCallback):this.stop()}_normalizeBearing(e,i){e=t.wrap(e,-180,180);const r=Math.abs(e-i);return Math.abs(e-360-i)180?-360:i<-180?360:0}}class Qr{constructor(e={}){this.options=e,t.bindAll(["_toggleAttribution","_updateData","_updateCompact","_updateCompactMinimize"],this)}getDefaultPosition(){return"bottom-right"}onAdd(t){return this._map=t,this._compact=this.options&&this.options.compact,this._container=s.create("details","maplibregl-ctrl maplibregl-ctrl-attrib mapboxgl-ctrl mapboxgl-ctrl-attrib"),this._compactButton=s.create("summary","maplibregl-ctrl-attrib-button mapboxgl-ctrl-attrib-button",this._container),this._compactButton.addEventListener("click",this._toggleAttribution),this._setElementTitle(this._compactButton,"ToggleAttribution"),this._innerContainer=s.create("div","maplibregl-ctrl-attrib-inner mapboxgl-ctrl-attrib-inner",this._container),this._updateAttributions(),this._updateCompact(),this._map.on("styledata",this._updateData),this._map.on("sourcedata",this._updateData),this._map.on("resize",this._updateCompact),this._map.on("drag",this._updateCompactMinimize),this._container}onRemove(){s.remove(this._container),this._map.off("styledata",this._updateData),this._map.off("sourcedata",this._updateData),this._map.off("resize",this._updateCompact),this._map.off("drag",this._updateCompactMinimize),this._map=void 0,this._compact=void 0,this._attribHTML=void 0}_setElementTitle(t,e){const i=this._map._getUIString(`AttributionControl.${e}`);t.title=i,t.setAttribute("aria-label",i)}_toggleAttribution(){this._container.classList.contains("maplibregl-compact")&&(this._container.classList.contains("maplibregl-compact-show")?(this._container.setAttribute("open",""),this._container.classList.remove("maplibregl-compact-show","mapboxgl-compact-show")):(this._container.classList.add("maplibregl-compact-show","mapboxgl-compact-show"),this._container.removeAttribute("open")))}_updateData(t){!t||"metadata"!==t.sourceDataType&&"visibility"!==t.sourceDataType&&"style"!==t.dataType||this._updateAttributions()}_updateAttributions(){if(!this._map.style)return;let t=[];if(this.options.customAttribution&&(Array.isArray(this.options.customAttribution)?t=t.concat(this.options.customAttribution.map((t=>"string"!=typeof t?"":t))):"string"==typeof this.options.customAttribution&&t.push(this.options.customAttribution)),this._map.style.stylesheet){const t=this._map.style.stylesheet;this.styleOwner=t.owner,this.styleId=t.id}const e=this._map.style.sourceCaches;for(const i in e){const r=e[i];if(r.used){const e=r.getSource();e.attribution&&t.indexOf(e.attribution)<0&&t.push(e.attribution)}}t=t.filter((t=>String(t).trim())),t.sort(((t,e)=>t.length-e.length)),t=t.filter(((e,i)=>{for(let r=i+1;r=0)return!1;return!0}));const i=t.join(" | ");i!==this._attribHTML&&(this._attribHTML=i,t.length?(this._innerContainer.innerHTML=i,this._container.classList.remove("maplibregl-attrib-empty","mapboxgl-attrib-empty")):this._container.classList.add("maplibregl-attrib-empty","mapboxgl-attrib-empty"),this._updateCompact(),this._editLink=null)}_updateCompact(){this._map.getCanvasContainer().offsetWidth<=640||this._compact?!1===this._compact?this._container.setAttribute("open",""):this._container.classList.contains("maplibregl-compact")||this._container.classList.contains("maplibregl-attrib-empty")||(this._container.setAttribute("open",""),this._container.classList.add("maplibregl-compact","mapboxgl-compact","maplibregl-compact-show","mapboxgl-compact-show")):(this._container.setAttribute("open",""),this._container.classList.contains("maplibregl-compact")&&this._container.classList.remove("maplibregl-compact","maplibregl-compact-show","mapboxgl-compact","mapboxgl-compact-show"))}_updateCompactMinimize(){this._container.classList.contains("maplibregl-compact")&&this._container.classList.contains("maplibregl-compact-show")&&this._container.classList.remove("maplibregl-compact-show","mapboxgl-compact-show")}}class tn{constructor(e={}){this.options=e,t.bindAll(["_updateCompact"],this)}getDefaultPosition(){return"bottom-left"}onAdd(t){this._map=t,this._compact=this.options&&this.options.compact,this._container=s.create("div","maplibregl-ctrl mapboxgl-ctrl");const e=s.create("a","maplibregl-ctrl-logo mapboxgl-ctrl-logo");return e.target="_blank",e.rel="noopener nofollow",e.href="https://maplibre.org/",e.setAttribute("aria-label",this._map._getUIString("LogoControl.Title")),e.setAttribute("rel","noopener nofollow"),this._container.appendChild(e),this._container.style.display="block",this._map.on("resize",this._updateCompact),this._updateCompact(),this._container}onRemove(){s.remove(this._container),this._map.off("resize",this._updateCompact),this._map=void 0,this._compact=void 0}_updateCompact(){const t=this._container.children;if(t.length){const e=t[0];this._map.getCanvasContainer().offsetWidth<=640||this._compact?!1!==this._compact&&e.classList.add("maplibregl-compact","mapboxgl-compact"):e.classList.remove("maplibregl-compact","mapboxgl-compact")}}}class en{constructor(){this._queue=[],this._id=0,this._cleared=!1,this._currentlyRunning=!1}add(t){const e=++this._id;return this._queue.push({callback:t,id:e,cancelled:!1}),e}remove(t){const e=this._currentlyRunning,i=e?this._queue.concat(e):this._queue;for(const e of i)if(e.id===t)return void(e.cancelled=!0)}run(t=0){const e=this._currentlyRunning=this._queue;this._queue=[];for(const i of e)if(!i.cancelled&&(i.callback(t),this._cleared))break;this._cleared=!1,this._currentlyRunning=!1}clear(){this._currentlyRunning&&(this._cleared=!0),this._queue=[]}}const rn={"AttributionControl.ToggleAttribution":"Toggle attribution","AttributionControl.MapFeedback":"Map feedback","FullscreenControl.Enter":"Enter fullscreen","FullscreenControl.Exit":"Exit fullscreen","GeolocateControl.FindMyLocation":"Find my location","GeolocateControl.LocationNotAvailable":"Location not available","LogoControl.Title":"Mapbox logo","NavigationControl.ResetBearing":"Reset bearing to north","NavigationControl.ZoomIn":"Zoom in","NavigationControl.ZoomOut":"Zoom out","ScaleControl.Feet":"ft","ScaleControl.Meters":"m","ScaleControl.Kilometers":"km","ScaleControl.Miles":"mi","ScaleControl.NauticalMiles":"nm","TerrainControl.enableTerrain":"Enable terrain","TerrainControl.disableTerrain":"Disable terrain"},nn={center:[0,0],zoom:0,bearing:0,pitch:0,minZoom:-2,maxZoom:22,minPitch:0,maxPitch:60,interactive:!0,scrollZoom:!0,boxZoom:!0,dragRotate:!0,dragPan:!0,keyboard:!0,doubleClickZoom:!0,touchZoomRotate:!0,touchPitch:!0,cooperativeGestures:void 0,bearingSnap:7,clickTolerance:3,pitchWithRotate:!0,hash:!1,attributionControl:!0,maplibreLogo:!1,failIfMajorPerformanceCaveat:!1,preserveDrawingBuffer:!1,trackResize:!0,renderWorldCopies:!0,refreshExpiredTiles:!0,maxTileCacheSize:null,localIdeographFontFamily:"sans-serif",transformRequest:null,fadeDuration:300,crossSourceCollisions:!0},sn={showCompass:!0,showZoom:!0,visualizePitch:!1};class on{constructor(e,i,r=!1){this._clickTolerance=10,this.element=i,this.mouseRotate=new Sr({clickTolerance:e.dragRotate._mouseRotate._clickTolerance}),this.map=e,r&&(this.mousePitch=new Ir({clickTolerance:e.dragRotate._mousePitch._clickTolerance})),t.bindAll(["mousedown","mousemove","mouseup","touchstart","touchmove","touchend","reset"],this),s.addEventListener(i,"mousedown",this.mousedown),s.addEventListener(i,"touchstart",this.touchstart,{passive:!1}),s.addEventListener(i,"touchmove",this.touchmove),s.addEventListener(i,"touchend",this.touchend),s.addEventListener(i,"touchcancel",this.reset)}down(t,e){this.mouseRotate.mousedown(t,e),this.mousePitch&&this.mousePitch.mousedown(t,e),s.disableDrag()}move(t,e){const i=this.map,r=this.mouseRotate.mousemoveWindow(t,e);if(r&&r.bearingDelta&&i.setBearing(i.getBearing()+r.bearingDelta),this.mousePitch){const r=this.mousePitch.mousemoveWindow(t,e);r&&r.pitchDelta&&i.setPitch(i.getPitch()+r.pitchDelta)}}off(){const t=this.element;s.removeEventListener(t,"mousedown",this.mousedown),s.removeEventListener(t,"touchstart",this.touchstart,{passive:!1}),s.removeEventListener(t,"touchmove",this.touchmove),s.removeEventListener(t,"touchend",this.touchend),s.removeEventListener(t,"touchcancel",this.reset),this.offTemp()}offTemp(){s.enableDrag(),s.removeEventListener(window,"mousemove",this.mousemove),s.removeEventListener(window,"mouseup",this.mouseup)}mousedown(e){this.down(t.extend({},e,{ctrlKey:!0,preventDefault:()=>e.preventDefault()}),s.mousePos(this.element,e)),s.addEventListener(window,"mousemove",this.mousemove),s.addEventListener(window,"mouseup",this.mouseup)}mousemove(t){this.move(t,s.mousePos(this.element,t))}mouseup(t){this.mouseRotate.mouseupWindow(t),this.mousePitch&&this.mousePitch.mouseupWindow(t),this.offTemp()}touchstart(t){1!==t.targetTouches.length?this.reset():(this._startPos=this._lastPos=s.touchPos(this.element,t.targetTouches)[0],this.down({type:"mousedown",button:0,ctrlKey:!0,preventDefault:()=>t.preventDefault()},this._startPos))}touchmove(t){1!==t.targetTouches.length?this.reset():(this._lastPos=s.touchPos(this.element,t.targetTouches)[0],this.move({preventDefault:()=>t.preventDefault()},this._lastPos))}touchend(t){0===t.targetTouches.length&&this._startPos&&this._lastPos&&this._startPos.dist(this._lastPos)180;){const t=r.locationPoint(e);if(t.x>=0&&t.y>=0&&t.x<=r.width&&t.y<=r.height)break;e.lng>r.center.lng?e.lng-=360:e.lng+=360}return e}const ln={center:"translate(-50%,-50%)",top:"translate(-50%,0)","top-left":"translate(0,0)","top-right":"translate(-100%,0)",bottom:"translate(-50%,-100%)","bottom-left":"translate(0,-100%)","bottom-right":"translate(-100%,-100%)",left:"translate(0,-50%)",right:"translate(-100%,-50%)"};function cn(t,e,i){const r=t.classList;for(const t in ln)r.remove(`maplibregl-${i}-anchor-${t}`,`mapboxgl-${i}-anchor-${t}`);r.add(`maplibregl-${i}-anchor-${e}`,`mapboxgl-${i}-anchor-${e}`)}class hn extends t.Evented{constructor(e,i){if(super(),(e instanceof HTMLElement||i)&&(e=t.extend({element:e},i)),t.bindAll(["_update","_onMove","_onUp","_addDragHandler","_onMapClick","_onKeyPress"],this),this._anchor=e&&e.anchor||"center",this._color=e&&e.color||"#3FB1CE",this._scale=e&&e.scale||1,this._draggable=e&&e.draggable||!1,this._clickTolerance=e&&e.clickTolerance||0,this._isDragging=!1,this._state="inactive",this._rotation=e&&e.rotation||0,this._rotationAlignment=e&&e.rotationAlignment||"auto",this._pitchAlignment=e&&e.pitchAlignment&&"auto"!==e.pitchAlignment?e.pitchAlignment:this._rotationAlignment,e&&e.element)this._element=e.element,this._offset=t.pointGeometry.convert(e&&e.offset||[0,0]);else{this._defaultMarker=!0,this._element=s.create("div"),this._element.setAttribute("aria-label","Map marker");const i=s.createNS("http://www.w3.org/2000/svg","svg"),r=41,n=27;i.setAttributeNS(null,"display","block"),i.setAttributeNS(null,"height",`${r}px`),i.setAttributeNS(null,"width",`${n}px`),i.setAttributeNS(null,"viewBox",`0 0 ${n} ${r}`);const o=s.createNS("http://www.w3.org/2000/svg","g");o.setAttributeNS(null,"stroke","none"),o.setAttributeNS(null,"stroke-width","1"),o.setAttributeNS(null,"fill","none"),o.setAttributeNS(null,"fill-rule","evenodd");const a=s.createNS("http://www.w3.org/2000/svg","g");a.setAttributeNS(null,"fill-rule","nonzero");const l=s.createNS("http://www.w3.org/2000/svg","g");l.setAttributeNS(null,"transform","translate(3.0, 29.0)"),l.setAttributeNS(null,"fill","#000000");const c=[{rx:"10.5",ry:"5.25002273"},{rx:"10.5",ry:"5.25002273"},{rx:"9.5",ry:"4.77275007"},{rx:"8.5",ry:"4.29549936"},{rx:"7.5",ry:"3.81822308"},{rx:"6.5",ry:"3.34094679"},{rx:"5.5",ry:"2.86367051"},{rx:"4.5",ry:"2.38636864"}];for(const t of c){const e=s.createNS("http://www.w3.org/2000/svg","ellipse");e.setAttributeNS(null,"opacity","0.04"),e.setAttributeNS(null,"cx","10.5"),e.setAttributeNS(null,"cy","5.80029008"),e.setAttributeNS(null,"rx",t.rx),e.setAttributeNS(null,"ry",t.ry),l.appendChild(e)}const h=s.createNS("http://www.w3.org/2000/svg","g");h.setAttributeNS(null,"fill",this._color);const u=s.createNS("http://www.w3.org/2000/svg","path");u.setAttributeNS(null,"d","M27,13.5 C27,19.074644 20.250001,27.000002 14.75,34.500002 C14.016665,35.500004 12.983335,35.500004 12.25,34.500002 C6.7499993,27.000002 0,19.222562 0,13.5 C0,6.0441559 6.0441559,0 13.5,0 C20.955844,0 27,6.0441559 27,13.5 Z"),h.appendChild(u);const p=s.createNS("http://www.w3.org/2000/svg","g");p.setAttributeNS(null,"opacity","0.25"),p.setAttributeNS(null,"fill","#000000");const d=s.createNS("http://www.w3.org/2000/svg","path");d.setAttributeNS(null,"d","M13.5,0 C6.0441559,0 0,6.0441559 0,13.5 C0,19.222562 6.7499993,27 12.25,34.5 C13,35.522727 14.016664,35.500004 14.75,34.5 C20.250001,27 27,19.074644 27,13.5 C27,6.0441559 20.955844,0 13.5,0 Z M13.5,1 C20.415404,1 26,6.584596 26,13.5 C26,15.898657 24.495584,19.181431 22.220703,22.738281 C19.945823,26.295132 16.705119,30.142167 13.943359,33.908203 C13.743445,34.180814 13.612715,34.322738 13.5,34.441406 C13.387285,34.322738 13.256555,34.180814 13.056641,33.908203 C10.284481,30.127985 7.4148684,26.314159 5.015625,22.773438 C2.6163816,19.232715 1,15.953538 1,13.5 C1,6.584596 6.584596,1 13.5,1 Z"),p.appendChild(d);const m=s.createNS("http://www.w3.org/2000/svg","g");m.setAttributeNS(null,"transform","translate(6.0, 7.0)"),m.setAttributeNS(null,"fill","#FFFFFF");const f=s.createNS("http://www.w3.org/2000/svg","g");f.setAttributeNS(null,"transform","translate(8.0, 8.0)");const g=s.createNS("http://www.w3.org/2000/svg","circle");g.setAttributeNS(null,"fill","#000000"),g.setAttributeNS(null,"opacity","0.25"),g.setAttributeNS(null,"cx","5.5"),g.setAttributeNS(null,"cy","5.5"),g.setAttributeNS(null,"r","5.4999962");const _=s.createNS("http://www.w3.org/2000/svg","circle");_.setAttributeNS(null,"fill","#FFFFFF"),_.setAttributeNS(null,"cx","5.5"),_.setAttributeNS(null,"cy","5.5"),_.setAttributeNS(null,"r","5.4999962"),f.appendChild(g),f.appendChild(_),a.appendChild(l),a.appendChild(h),a.appendChild(p),a.appendChild(m),a.appendChild(f),i.appendChild(a),i.setAttributeNS(null,"height",r*this._scale+"px"),i.setAttributeNS(null,"width",n*this._scale+"px"),this._element.appendChild(i),this._offset=t.pointGeometry.convert(e&&e.offset||[0,-14])}this._element.classList.add("maplibregl-marker","mapboxgl-marker"),this._element.addEventListener("dragstart",(t=>{t.preventDefault()})),this._element.addEventListener("mousedown",(t=>{t.preventDefault()})),cn(this._element,this._anchor,"marker"),this._popup=null}addTo(t){return this.remove(),this._map=t,t.getCanvasContainer().appendChild(this._element),t.on("move",this._update),t.on("moveend",this._update),this.setDraggable(this._draggable),this._update(),this._map.on("click",this._onMapClick),this}remove(){return this._opacityTimeout&&(clearTimeout(this._opacityTimeout),delete this._opacityTimeout),this._map&&(this._map.off("click",this._onMapClick),this._map.off("move",this._update),this._map.off("moveend",this._update),this._map.off("mousedown",this._addDragHandler),this._map.off("touchstart",this._addDragHandler),this._map.off("mouseup",this._onUp),this._map.off("touchend",this._onUp),this._map.off("mousemove",this._onMove),this._map.off("touchmove",this._onMove),delete this._map),s.remove(this._element),this._popup&&this._popup.remove(),this}getLngLat(){return this._lngLat}setLngLat(e){return this._lngLat=t.LngLat.convert(e),this._pos=null,this._popup&&this._popup.setLngLat(this._lngLat),this._update(),this}getElement(){return this._element}setPopup(t){if(this._popup&&(this._popup.remove(),this._popup=null,this._element.removeEventListener("keypress",this._onKeyPress),this._originalTabIndex||this._element.removeAttribute("tabindex")),t){if(!("offset"in t.options)){const e=38.1,i=13.5,r=Math.sqrt(Math.pow(i,2)/2);t.options.offset=this._defaultMarker?{top:[0,0],"top-left":[0,0],"top-right":[0,0],bottom:[0,-e],"bottom-left":[r,-1*(e-i+r)],"bottom-right":[-r,-1*(e-i+r)],left:[i,-1*(e-i)],right:[-i,-1*(e-i)]}:this._offset}this._popup=t,this._lngLat&&this._popup.setLngLat(this._lngLat),this._originalTabIndex=this._element.getAttribute("tabindex"),this._originalTabIndex||this._element.setAttribute("tabindex","0"),this._element.addEventListener("keypress",this._onKeyPress)}return this}_onKeyPress(t){const e=t.code,i=t.charCode||t.keyCode;"Space"!==e&&"Enter"!==e&&32!==i&&13!==i||this.togglePopup()}_onMapClick(t){const e=t.originalEvent.target,i=this._element;this._popup&&(e===i||i.contains(e))&&this.togglePopup()}getPopup(){return this._popup}togglePopup(){const t=this._popup;return t?(t.isOpen()?t.remove():t.addTo(this._map),this):this}_update(t){if(!this._map)return;this._map.transform.renderWorldCopies&&(this._lngLat=an(this._lngLat,this._pos,this._map.transform)),this._pos=this._map.project(this._lngLat)._add(this._offset);let e="";"viewport"===this._rotationAlignment||"auto"===this._rotationAlignment?e=`rotateZ(${this._rotation}deg)`:"map"===this._rotationAlignment&&(e=`rotateZ(${this._rotation-this._map.getBearing()}deg)`);let i="";"viewport"===this._pitchAlignment||"auto"===this._pitchAlignment?i="rotateX(0deg)":"map"===this._pitchAlignment&&(i=`rotateX(${this._map.getPitch()}deg)`),t&&"moveend"!==t.type||(this._pos=this._pos.round()),s.setTransform(this._element,`${ln[this._anchor]} translate(${this._pos.x}px, ${this._pos.y}px) ${i} ${e}`),this._map.style&&this._map.style.terrain&&!this._opacityTimeout&&(this._opacityTimeout=setTimeout((()=>{const t=this._map.unproject(this._pos),e=40075016.686*Math.abs(Math.cos(this._lngLat.lat*Math.PI/180))/Math.pow(2,this._map.transform.tileZoom+8);this._element.style.opacity=t.distanceTo(this._lngLat)>20*e?"0.2":"1.0",this._opacityTimeout=null}),100))}getOffset(){return this._offset}setOffset(e){return this._offset=t.pointGeometry.convert(e),this._update(),this}_onMove(e){if(!this._isDragging){const t=this._clickTolerance||this._map._clickTolerance;this._isDragging=e.point.dist(this._pointerdownPos)>=t}this._isDragging&&(this._pos=e.point.sub(this._positionDelta),this._lngLat=this._map.unproject(this._pos),this.setLngLat(this._lngLat),this._element.style.pointerEvents="none","pending"===this._state&&(this._state="active",this.fire(new t.Event("dragstart"))),this.fire(new t.Event("drag")))}_onUp(){this._element.style.pointerEvents="auto",this._positionDelta=null,this._pointerdownPos=null,this._isDragging=!1,this._map.off("mousemove",this._onMove),this._map.off("touchmove",this._onMove),"active"===this._state&&this.fire(new t.Event("dragend")),this._state="inactive"}_addDragHandler(t){this._element.contains(t.originalEvent.target)&&(t.preventDefault(),this._positionDelta=t.point.sub(this._pos).add(this._offset),this._pointerdownPos=t.point,this._state="pending",this._map.on("mousemove",this._onMove),this._map.on("touchmove",this._onMove),this._map.once("mouseup",this._onUp),this._map.once("touchend",this._onUp))}setDraggable(t){return this._draggable=!!t,this._map&&(t?(this._map.on("mousedown",this._addDragHandler),this._map.on("touchstart",this._addDragHandler)):(this._map.off("mousedown",this._addDragHandler),this._map.off("touchstart",this._addDragHandler))),this}isDraggable(){return this._draggable}setRotation(t){return this._rotation=t||0,this._update(),this}getRotation(){return this._rotation}setRotationAlignment(t){return this._rotationAlignment=t||"auto",this._update(),this}getRotationAlignment(){return this._rotationAlignment}setPitchAlignment(t){return this._pitchAlignment=t&&"auto"!==t?t:this._rotationAlignment,this._update(),this}getPitchAlignment(){return this._pitchAlignment}}const un={positionOptions:{enableHighAccuracy:!1,maximumAge:0,timeout:6e3},fitBoundsOptions:{maxZoom:15},trackUserLocation:!1,showAccuracyCircle:!0,showUserLocation:!0};let pn,dn=0,mn=!1;const fn={maxWidth:100,unit:"metric"};function gn(t,e,i){const r=i&&i.maxWidth||100,n=t._container.clientHeight/2,s=t.unproject([0,n]),o=t.unproject([r,n]),a=s.distanceTo(o);if(i&&"imperial"===i.unit){const i=3.2808*a;i>5280?_n(e,r,i/5280,t._getUIString("ScaleControl.Miles")):_n(e,r,i,t._getUIString("ScaleControl.Feet"))}else i&&"nautical"===i.unit?_n(e,r,a/1852,t._getUIString("ScaleControl.NauticalMiles")):a>=1e3?_n(e,r,a/1e3,t._getUIString("ScaleControl.Kilometers")):_n(e,r,a,t._getUIString("ScaleControl.Meters"))}function _n(t,e,i,r){const n=function(t){const e=Math.pow(10,`${Math.floor(t)}`.length-1);let i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:i>=1?1:function(t){const e=Math.pow(10,Math.ceil(-Math.log(t)/Math.LN10));return Math.round(t*e)/e}(i),e*i}(i);t.style.width=e*(n/i)+"px",t.innerHTML=`${n} ${r}`}const yn={closeButton:!0,closeOnClick:!0,focusAfterOpen:!0,className:"",maxWidth:"240px"},xn=["a[href]","[tabindex]:not([tabindex='-1'])","[contenteditable]:not([contenteditable='false'])","button:not([disabled])","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].join(", ");function vn(e){if(e){if("number"==typeof e){const i=Math.round(Math.sqrt(.5*Math.pow(e,2)));return{center:new t.pointGeometry(0,0),top:new t.pointGeometry(0,e),"top-left":new t.pointGeometry(i,i),"top-right":new t.pointGeometry(-i,i),bottom:new t.pointGeometry(0,-e),"bottom-left":new t.pointGeometry(i,-i),"bottom-right":new t.pointGeometry(-i,-i),left:new t.pointGeometry(e,0),right:new t.pointGeometry(-e,0)}}if(e instanceof t.pointGeometry||Array.isArray(e)){const i=t.pointGeometry.convert(e);return{center:i,top:i,"top-left":i,"top-right":i,bottom:i,"bottom-left":i,"bottom-right":i,left:i,right:i}}return{center:t.pointGeometry.convert(e.center||[0,0]),top:t.pointGeometry.convert(e.top||[0,0]),"top-left":t.pointGeometry.convert(e["top-left"]||[0,0]),"top-right":t.pointGeometry.convert(e["top-right"]||[0,0]),bottom:t.pointGeometry.convert(e.bottom||[0,0]),"bottom-left":t.pointGeometry.convert(e["bottom-left"]||[0,0]),"bottom-right":t.pointGeometry.convert(e["bottom-right"]||[0,0]),left:t.pointGeometry.convert(e.left||[0,0]),right:t.pointGeometry.convert(e.right||[0,0])}}return vn(new t.pointGeometry(0,0))}const bn={supported:e,setRTLTextPlugin:t.setRTLTextPlugin,getRTLTextPluginStatus:t.getRTLTextPluginStatus,Map:class extends Yr{constructor(e){var i;if(t.PerformanceUtils.mark(t.PerformanceMarkers.create),null!=(e=t.extend({},nn,e)).minZoom&&null!=e.maxZoom&&e.minZoom>e.maxZoom)throw new Error("maxZoom must be greater than or equal to minZoom");if(null!=e.minPitch&&null!=e.maxPitch&&e.minPitch>e.maxPitch)throw new Error("maxPitch must be greater than or equal to minPitch");if(null!=e.minPitch&&e.minPitch<0)throw new Error("minPitch must be greater than or equal to 0");if(null!=e.maxPitch&&e.maxPitch>85)throw new Error("maxPitch must be less than or equal to 85");if(super(new ir(e.minZoom,e.maxZoom,e.minPitch,e.maxPitch,e.renderWorldCopies),{bearingSnap:e.bearingSnap}),this._interactive=e.interactive,this._cooperativeGestures=e.cooperativeGestures,this._maxTileCacheSize=e.maxTileCacheSize,this._failIfMajorPerformanceCaveat=e.failIfMajorPerformanceCaveat,this._preserveDrawingBuffer=e.preserveDrawingBuffer,this._antialias=e.antialias,this._trackResize=e.trackResize,this._bearingSnap=e.bearingSnap,this._refreshExpiredTiles=e.refreshExpiredTiles,this._fadeDuration=e.fadeDuration,this._crossSourceCollisions=e.crossSourceCollisions,this._crossFadingFactor=1,this._collectResourceTiming=e.collectResourceTiming,this._renderTaskQueue=new en,this._controls=[],this._mapId=t.uniqueId(),this._locale=t.extend({},rn,e.locale),this._clickTolerance=e.clickTolerance,this._pixelRatio=null!==(i=e.pixelRatio)&&void 0!==i?i:devicePixelRatio,this._requestManager=new o(e.transformRequest),"string"==typeof e.container){if(this._container=document.getElementById(e.container),!this._container)throw new Error(`Container '${e.container}' not found.`)}else{if(!(e.container instanceof HTMLElement))throw new Error("Invalid type: 'container' must be a String or HTMLElement.");this._container=e.container}if(e.maxBounds&&this.setMaxBounds(e.maxBounds),t.bindAll(["_onWindowOnline","_onWindowResize","_onMapScroll","_contextLost","_contextRestored"],this),this._setupContainer(),this._setupPainter(),void 0===this.painter)throw new Error("Failed to initialize WebGL.");this.on("move",(()=>this._update(!1))),this.on("moveend",(()=>this._update(!1))),this.on("zoom",(()=>this._update(!0))),this.on("terrain",(()=>{this.painter.terrainFacilitator.dirty=!0,this._update(!0)})),"undefined"!=typeof window&&(addEventListener("online",this._onWindowOnline,!1),addEventListener("resize",this._onWindowResize,!1),addEventListener("orientationchange",this._onWindowResize,!1)),this.handlers=new Kr(this,e),this._cooperativeGestures&&this._setupCooperativeGestures(),this._hash=e.hash&&new rr("string"==typeof e.hash&&e.hash||void 0).addTo(this),this._hash&&this._hash._onHashChange()||(this.jumpTo({center:e.center,zoom:e.zoom,bearing:e.bearing,pitch:e.pitch}),e.bounds&&(this.resize(),this.fitBounds(e.bounds,t.extend({},e.fitBoundsOptions,{duration:0})))),this.resize(),this._localIdeographFontFamily=e.localIdeographFontFamily,e.style&&this.setStyle(e.style,{localIdeographFontFamily:e.localIdeographFontFamily}),e.attributionControl&&this.addControl(new Qr({customAttribution:e.customAttribution})),e.maplibreLogo&&this.addControl(new tn,e.logoPosition),this.on("style.load",(()=>{this.transform.unmodified&&this.jumpTo(this.style.stylesheet)})),this.on("data",(e=>{this._update("style"===e.dataType),this.fire(new t.Event(`${e.dataType}data`,e))})),this.on("dataloading",(e=>{this.fire(new t.Event(`${e.dataType}dataloading`,e))})),this.on("dataabort",(e=>{this.fire(new t.Event("sourcedataabort",e))}))}_getMapId(){return this._mapId}addControl(e,i){if(void 0===i&&(i=e.getDefaultPosition?e.getDefaultPosition():"top-right"),!e||!e.onAdd)return this.fire(new t.ErrorEvent(new Error("Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.")));const r=e.onAdd(this);this._controls.push(e);const n=this._controlPositions[i];return-1!==i.indexOf("bottom")?n.insertBefore(r,n.firstChild):n.appendChild(r),this}removeControl(e){if(!e||!e.onRemove)return this.fire(new t.ErrorEvent(new Error("Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.")));const i=this._controls.indexOf(e);return i>-1&&this._controls.splice(i,1),e.onRemove(this),this}hasControl(t){return this._controls.indexOf(t)>-1}resize(e){const i=this._containerDimensions(),r=i[0],n=i[1];this._resizeCanvas(r,n,this.getPixelRatio()),this.transform.resize(r,n),this.painter.resize(r,n,this.getPixelRatio());const s=!this._moving;return s&&(this.stop(),this.fire(new t.Event("movestart",e)).fire(new t.Event("move",e))),this.fire(new t.Event("resize",e)),s&&this.fire(new t.Event("moveend",e)),this}getPixelRatio(){return this._pixelRatio}setPixelRatio(t){const[e,i]=this._containerDimensions();this._pixelRatio=t,this._resizeCanvas(e,i,t),this.painter.resize(e,i,t)}getBounds(){return this.transform.getBounds()}getMaxBounds(){return this.transform.getMaxBounds()}setMaxBounds(e){return this.transform.setMaxBounds(t.LngLatBounds.convert(e)),this._update()}setMinZoom(t){if((t=null==t?-2:t)>=-2&&t<=this.transform.maxZoom)return this.transform.minZoom=t,this._update(),this.getZoom()=this.transform.minZoom)return this.transform.maxZoom=t,this._update(),this.getZoom()>t&&this.setZoom(t),this;throw new Error("maxZoom must be greater than the current minZoom")}getMaxZoom(){return this.transform.maxZoom}setMinPitch(t){if((t=null==t?0:t)<0)throw new Error("minPitch must be greater than or equal to 0");if(t>=0&&t<=this.transform.maxPitch)return this.transform.minPitch=t,this._update(),this.getPitch()85)throw new Error("maxPitch must be less than or equal to 85");if(t>=this.transform.minPitch)return this.transform.maxPitch=t,this._update(),this.getPitch()>t&&this.setPitch(t),this;throw new Error("maxPitch must be greater than the current minPitch")}getMaxPitch(){return this.transform.maxPitch}getRenderWorldCopies(){return this.transform.renderWorldCopies}setRenderWorldCopies(t){return this.transform.renderWorldCopies=t,this._update()}project(e){return this.transform.locationPoint(t.LngLat.convert(e),this.style&&this.style.terrain)}unproject(e){return this.transform.pointLocation(t.pointGeometry.convert(e),this.style&&this.style.terrain)}isMoving(){return this._moving||this.handlers.isMoving()}isZooming(){return this._zooming||this.handlers.isZooming()}isRotating(){return this._rotating||this.handlers.isRotating()}_createDelegatedListener(t,e,i){if("mouseenter"===t||"mouseover"===t){let r=!1;const n=n=>{const s=this.getLayer(e)?this.queryRenderedFeatures(n.point,{layers:[e]}):[];s.length?r||(r=!0,i.call(this,new pr(t,this,n.originalEvent,{features:s}))):r=!1};return{layer:e,listener:i,delegates:{mousemove:n,mouseout:()=>{r=!1}}}}if("mouseleave"===t||"mouseout"===t){let r=!1;const n=n=>{(this.getLayer(e)?this.queryRenderedFeatures(n.point,{layers:[e]}):[]).length?r=!0:r&&(r=!1,i.call(this,new pr(t,this,n.originalEvent)))},s=e=>{r&&(r=!1,i.call(this,new pr(t,this,e.originalEvent)))};return{layer:e,listener:i,delegates:{mousemove:n,mouseout:s}}}{const r=t=>{const r=this.getLayer(e)?this.queryRenderedFeatures(t.point,{layers:[e]}):[];r.length&&(t.features=r,i.call(this,t),delete t.features)};return{layer:e,listener:i,delegates:{[t]:r}}}}on(t,e,i){if(void 0===i)return super.on(t,e);const r=this._createDelegatedListener(t,e,i);this._delegatedListeners=this._delegatedListeners||{},this._delegatedListeners[t]=this._delegatedListeners[t]||[],this._delegatedListeners[t].push(r);for(const t in r.delegates)this.on(t,r.delegates[t]);return this}once(t,e,i){if(void 0===i)return super.once(t,e);const r=this._createDelegatedListener(t,e,i);for(const t in r.delegates)this.once(t,r.delegates[t]);return this}off(t,e,i){return void 0===i?super.off(t,e):(this._delegatedListeners&&this._delegatedListeners[t]&&(r=>{const n=this._delegatedListeners[t];for(let t=0;t{e?this.fire(new t.ErrorEvent(e)):r&&this._updateDiff(r,i)}))}else"object"==typeof e&&this._updateDiff(e,i)}_updateDiff(e,i){try{this.style.setState(e)&&this._update(!0)}catch(r){t.warnOnce(`Unable to perform style diff: ${r.message||r.error||r}. Rebuilding the style from scratch.`),this._updateStyle(e,i)}}getStyle(){if(this.style)return this.style.serialize()}isStyleLoaded(){return this.style?this.style.loaded():t.warnOnce("There is no style added to the map.")}addSource(t,e){return this._lazyInitEmptyStyle(),this.style.addSource(t,e),this._update(!0)}isSourceLoaded(e){const i=this.style&&this.style.sourceCaches[e];if(void 0!==i)return i.loaded();this.fire(new t.ErrorEvent(new Error(`There is no source with ID '${e}'`)))}setTerrain(t){return this.style.setTerrain(t),this}getTerrain(){return this.style.terrain&&this.style.terrain.options}areTilesLoaded(){const t=this.style&&this.style.sourceCaches;for(const e in t){const i=t[e]._tiles;for(const t in i){const e=i[t];if("loaded"!==e.state&&"errored"!==e.state)return!1}}return!0}addSourceType(t,e,i){return this._lazyInitEmptyStyle(),this.style.addSourceType(t,e,i)}removeSource(t){return this.style.removeSource(t),this._update(!0)}getSource(t){return this.style.getSource(t)}addImage(e,i,{pixelRatio:r=1,sdf:n=!1,stretchX:s,stretchY:o,content:a}={}){if(this._lazyInitEmptyStyle(),i instanceof HTMLImageElement||t.isImageBitmap(i)){const{width:l,height:c,data:h}=t.exported.getImageData(i);this.style.addImage(e,{data:new t.RGBAImage({width:l,height:c},h),pixelRatio:r,stretchX:s,stretchY:o,content:a,sdf:n,version:0})}else{if(void 0===i.width||void 0===i.height)return this.fire(new t.ErrorEvent(new Error("Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, or object with `width`, `height`, and `data` properties with the same format as `ImageData`")));{const{width:l,height:c,data:h}=i,u=i;this.style.addImage(e,{data:new t.RGBAImage({width:l,height:c},new Uint8Array(h)),pixelRatio:r,stretchX:s,stretchY:o,content:a,sdf:n,version:0,userImage:u}),u.onAdd&&u.onAdd(this,e)}}}updateImage(e,i){const r=this.style.getImage(e);if(!r)return this.fire(new t.ErrorEvent(new Error("The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.")));const n=i instanceof HTMLImageElement||t.isImageBitmap(i)?t.exported.getImageData(i):i,{width:s,height:o,data:a}=n;if(void 0===s||void 0===o)return this.fire(new t.ErrorEvent(new Error("Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, or object with `width`, `height`, and `data` properties with the same format as `ImageData`")));if(s!==r.data.width||o!==r.data.height)return this.fire(new t.ErrorEvent(new Error("The width and height of the updated image must be that same as the previous version of the image")));const l=!(i instanceof HTMLImageElement||t.isImageBitmap(i));r.data.replace(a,l),this.style.updateImage(e,r)}hasImage(e){return e?!!this.style.getImage(e):(this.fire(new t.ErrorEvent(new Error("Missing required image id"))),!1)}removeImage(t){this.style.removeImage(t)}loadImage(e,i){t.getImage(this._requestManager.transformRequest(e,t.ResourceType.Image),i)}listImages(){return this.style.listImages()}addLayer(t,e){return this._lazyInitEmptyStyle(),this.style.addLayer(t,e),this._update(!0)}moveLayer(t,e){return this.style.moveLayer(t,e),this._update(!0)}removeLayer(t){return this.style.removeLayer(t),this._update(!0)}getLayer(t){return this.style.getLayer(t)}setLayerZoomRange(t,e,i){return this.style.setLayerZoomRange(t,e,i),this._update(!0)}setFilter(t,e,i={}){return this.style.setFilter(t,e,i),this._update(!0)}getFilter(t){return this.style.getFilter(t)}setPaintProperty(t,e,i,r={}){return this.style.setPaintProperty(t,e,i,r),this._update(!0)}getPaintProperty(t,e){return this.style.getPaintProperty(t,e)}setLayoutProperty(t,e,i,r={}){return this.style.setLayoutProperty(t,e,i,r),this._update(!0)}getLayoutProperty(t,e){return this.style.getLayoutProperty(t,e)}setLight(t,e={}){return this._lazyInitEmptyStyle(),this.style.setLight(t,e),this._update(!0)}getLight(){return this.style.getLight()}setFeatureState(t,e){return this.style.setFeatureState(t,e),this._update()}removeFeatureState(t,e){return this.style.removeFeatureState(t,e),this._update()}getFeatureState(t){return this.style.getFeatureState(t)}getContainer(){return this._container}getCanvasContainer(){return this._canvasContainer}getCanvas(){return this._canvas}_containerDimensions(){let t=0,e=0;return this._container&&(t=this._container.clientWidth||400,e=this._container.clientHeight||300),[t,e]}_setupContainer(){const t=this._container;t.classList.add("maplibregl-map","mapboxgl-map");const e=this._canvasContainer=s.create("div","maplibregl-canvas-container mapboxgl-canvas-container",t);this._interactive&&e.classList.add("maplibregl-interactive","mapboxgl-interactive"),this._canvas=s.create("canvas","maplibregl-canvas mapboxgl-canvas",e),this._canvas.addEventListener("webglcontextlost",this._contextLost,!1),this._canvas.addEventListener("webglcontextrestored",this._contextRestored,!1),this._canvas.setAttribute("tabindex","0"),this._canvas.setAttribute("aria-label","Map"),this._canvas.setAttribute("role","region");const i=this._containerDimensions();this._resizeCanvas(i[0],i[1],this.getPixelRatio());const r=this._controlContainer=s.create("div","maplibregl-control-container mapboxgl-control-container",t),n=this._controlPositions={};["top-left","top-right","bottom-left","bottom-right"].forEach((t=>{n[t]=s.create("div",`maplibregl-ctrl-${t} mapboxgl-ctrl-${t}`,r)})),this._container.addEventListener("scroll",this._onMapScroll,!1)}_setupCooperativeGestures(){const t=this._container;this._metaPress=!1,this._cooperativeGesturesScreen=s.create("div","maplibregl-cooperative-gesture-screen",t);let e="Control",i="boolean"!=typeof this._cooperativeGestures&&this._cooperativeGestures.windowsHelpText?this._cooperativeGestures.windowsHelpText:"Use Ctrl + scroll to zoom the map";0===navigator.platform.indexOf("Mac")&&(i="boolean"!=typeof this._cooperativeGestures&&this._cooperativeGestures.macHelpText?this._cooperativeGestures.macHelpText:"Use ⌘ + scroll to zoom the map",e="Meta"),this._cooperativeGesturesScreen.innerHTML=`\n
${i}
\n
${"boolean"!=typeof this._cooperativeGestures&&this._cooperativeGestures.mobileHelpText?this._cooperativeGestures.mobileHelpText:"Use two fingers to move the map"}
\n `,document.addEventListener("keydown",(t=>{t.key===e&&(this._metaPress=!0)})),document.addEventListener("keyup",(t=>{t.key===e&&(this._metaPress=!1)})),this._canvasContainer.addEventListener("wheel",(t=>{this._onCooperativeGesture(t,this._metaPress,1)}),!1),this._canvasContainer.classList.remove("mapboxgl-touch-drag-pan","maplibregl-touch-drag-pan")}_resizeCanvas(t,e,i){this._canvas.width=i*t,this._canvas.height=i*e,this._canvas.style.width=`${t}px`,this._canvas.style.height=`${e}px`}_setupPainter(){const i=t.extend({},e.webGLContextAttributes,{failIfMajorPerformanceCaveat:this._failIfMajorPerformanceCaveat,preserveDrawingBuffer:this._preserveDrawingBuffer,antialias:this._antialias||!1}),r=this._canvas.getContext("webgl",i)||this._canvas.getContext("experimental-webgl",i);r?(this.painter=new Yi(r,this.transform),t.exported$1.testSupport(r)):this.fire(new t.ErrorEvent(new Error("Failed to initialize WebGL")))}_contextLost(e){e.preventDefault(),this._frame&&(this._frame.cancel(),this._frame=null),this.fire(new t.Event("webglcontextlost",{originalEvent:e}))}_contextRestored(e){this._setupPainter(),this.resize(),this._update(),this.fire(new t.Event("webglcontextrestored",{originalEvent:e}))}_onMapScroll(t){if(t.target===this._container)return this._container.scrollTop=0,this._container.scrollLeft=0,!1}_onCooperativeGesture(t,e,i){return!e&&i<2&&(this._cooperativeGesturesScreen.classList.add("maplibregl-show"),setTimeout((()=>{this._cooperativeGesturesScreen.classList.remove("maplibregl-show")}),100)),!1}loaded(){return!this._styleDirty&&!this._sourcesDirty&&!!this.style&&this.style.loaded()}_update(t){return this.style?(this._styleDirty=this._styleDirty||t,this._sourcesDirty=!0,this.triggerRepaint(),this):this}_requestRenderFrame(t){return this._update(),this._renderTaskQueue.add(t)}_cancelRenderFrame(t){this._renderTaskQueue.remove(t)}_render(e){let i,r=0;const n=this.painter.context.extTimerQuery;if(this.listens("gpu-timing-frame")&&(i=n.createQueryEXT(),n.beginQueryEXT(n.TIME_ELAPSED_EXT,i),r=t.exported.now()),this.painter.context.setDirty(),this.painter.setBaseState(),this._renderTaskQueue.run(e),this._removed)return;let s=!1;if(this.style&&this._styleDirty){this._styleDirty=!1;const e=this.transform.zoom,i=t.exported.now();this.style.zoomHistory.update(e,i);const r=new t.EvaluationParameters(e,{now:i,fadeDuration:this._fadeDuration,zoomHistory:this.style.zoomHistory,transition:this.style.getTransition()}),n=r.crossFadingFactor();1===n&&n===this._crossFadingFactor||(s=!0,this._crossFadingFactor=n),this.style.update(r)}if(this.style&&this._sourcesDirty&&(this._sourcesDirty=!1,this.style._updateSources(this.transform)),this.style.terrain&&this.style.terrain.sourceCache.update(this.transform,this.style.terrain),this.transform.updateElevation(this.style.terrain),this._placementDirty=this.style&&this.style._updatePlacement(this.painter.transform,this.showCollisionBoxes,this._fadeDuration,this._crossSourceCollisions),this.painter.render(this.style,{showTileBoundaries:this.showTileBoundaries,showOverdrawInspector:this._showOverdrawInspector,rotating:this.isRotating(),zooming:this.isZooming(),moving:this.isMoving(),fadeDuration:this._fadeDuration,showPadding:this.showPadding,gpuTiming:!!this.listens("gpu-timing-layer")}),this.fire(new t.Event("render")),this.loaded()&&!this._loaded&&(this._loaded=!0,t.PerformanceUtils.mark(t.PerformanceMarkers.load),this.fire(new t.Event("load"))),this.style&&(this.style.hasTransitions()||s)&&(this._styleDirty=!0),this.style&&!this._placementDirty&&this.style._releaseSymbolFadeTiles(),this.listens("gpu-timing-frame")){const e=t.exported.now()-r;n.endQueryEXT(n.TIME_ELAPSED_EXT,i),setTimeout((()=>{const r=n.getQueryObjectEXT(i,n.QUERY_RESULT_EXT)/1e6;n.deleteQueryEXT(i),this.fire(new t.Event("gpu-timing-frame",{cpuTime:e,gpuTime:r}))}),50)}if(this.listens("gpu-timing-layer")){const e=this.painter.collectGpuTimers();setTimeout((()=>{const i=this.painter.queryGpuTimers(e);this.fire(new t.Event("gpu-timing-layer",{layerTimes:i}))}),50)}const o=this._sourcesDirty||this._styleDirty||this._placementDirty;return o||this._repaint?this.triggerRepaint():!this.isMoving()&&this.loaded()&&this.fire(new t.Event("idle")),!this._loaded||this._fullyLoaded||o||(this._fullyLoaded=!0,t.PerformanceUtils.mark(t.PerformanceMarkers.fullLoad)),this}redraw(){return this.style&&(this._frame&&(this._frame.cancel(),this._frame=null),this._render(0)),this}remove(){this._hash&&this._hash.remove();for(const t of this._controls)t.onRemove(this);this._controls=[],this._frame&&(this._frame.cancel(),this._frame=null),this._renderTaskQueue.clear(),this.painter.destroy(),this.handlers.destroy(),delete this.handlers,this.setStyle(null),"undefined"!=typeof window&&(removeEventListener("resize",this._onWindowResize,!1),removeEventListener("orientationchange",this._onWindowResize,!1),removeEventListener("online",this._onWindowOnline,!1));const e=this.painter.context.gl.getExtension("WEBGL_lose_context");e&&e.loseContext(),this._canvas.removeEventListener("webglcontextrestored",this._contextRestored,!1),this._canvas.removeEventListener("webglcontextlost",this._contextLost,!1),s.remove(this._canvasContainer),s.remove(this._controlContainer),this._cooperativeGestures&&s.remove(this._cooperativeGesturesScreen),this._container.classList.remove("maplibregl-map","mapboxgl-map"),t.PerformanceUtils.clearMetrics(),this._removed=!0,this.fire(new t.Event("remove"))}triggerRepaint(){this.style&&!this._frame&&(this._frame=t.exported.frame((e=>{t.PerformanceUtils.frame(e),this._frame=null,this._render(e)})))}_onWindowOnline(){this._update()}_onWindowResize(t){this._trackResize&&this.resize({originalEvent:t})._update()}get showTileBoundaries(){return!!this._showTileBoundaries}set showTileBoundaries(t){this._showTileBoundaries!==t&&(this._showTileBoundaries=t,this._update())}get showPadding(){return!!this._showPadding}set showPadding(t){this._showPadding!==t&&(this._showPadding=t,this._update())}get showCollisionBoxes(){return!!this._showCollisionBoxes}set showCollisionBoxes(t){this._showCollisionBoxes!==t&&(this._showCollisionBoxes=t,t?this.style._generateCollisionBoxes():this._update())}get showOverdrawInspector(){return!!this._showOverdrawInspector}set showOverdrawInspector(t){this._showOverdrawInspector!==t&&(this._showOverdrawInspector=t,this._update())}get repaint(){return!!this._repaint}set repaint(t){this._repaint!==t&&(this._repaint=t,this.triggerRepaint())}get vertices(){return!!this._vertices}set vertices(t){this._vertices=t,this._update()}_setCacheLimits(e,i){t.setCacheLimits(e,i)}get version(){return"2.3.0"}},NavigationControl:class{constructor(e){this.options=t.extend({},sn,e),this._container=s.create("div","maplibregl-ctrl maplibregl-ctrl-group mapboxgl-ctrl mapboxgl-ctrl-group"),this._container.addEventListener("contextmenu",(t=>t.preventDefault())),this.options.showZoom&&(t.bindAll(["_setButtonTitle","_updateZoomButtons"],this),this._zoomInButton=this._createButton("maplibregl-ctrl-zoom-in mapboxgl-ctrl-zoom-in",(t=>this._map.zoomIn({},{originalEvent:t}))),s.create("span","maplibregl-ctrl-icon mapboxgl-ctrl-icon",this._zoomInButton).setAttribute("aria-hidden","true"),this._zoomOutButton=this._createButton("maplibregl-ctrl-zoom-out mapboxgl-ctrl-zoom-out",(t=>this._map.zoomOut({},{originalEvent:t}))),s.create("span","maplibregl-ctrl-icon mapboxgl-ctrl-icon",this._zoomOutButton).setAttribute("aria-hidden","true")),this.options.showCompass&&(t.bindAll(["_rotateCompassArrow"],this),this._compass=this._createButton("maplibregl-ctrl-compass mapboxgl-ctrl-compass",(t=>{this.options.visualizePitch?this._map.resetNorthPitch({},{originalEvent:t}):this._map.resetNorth({},{originalEvent:t})})),this._compassIcon=s.create("span","maplibregl-ctrl-icon mapboxgl-ctrl-icon",this._compass),this._compassIcon.setAttribute("aria-hidden","true"))}_updateZoomButtons(){const t=this._map.getZoom(),e=t===this._map.getMaxZoom(),i=t===this._map.getMinZoom();this._zoomInButton.disabled=e,this._zoomOutButton.disabled=i,this._zoomInButton.setAttribute("aria-disabled",e.toString()),this._zoomOutButton.setAttribute("aria-disabled",i.toString())}_rotateCompassArrow(){const t=this.options.visualizePitch?`scale(${1/Math.pow(Math.cos(this._map.transform.pitch*(Math.PI/180)),.5)}) rotateX(${this._map.transform.pitch}deg) rotateZ(${this._map.transform.angle*(180/Math.PI)}deg)`:`rotate(${this._map.transform.angle*(180/Math.PI)}deg)`;this._compassIcon.style.transform=t}onAdd(t){return this._map=t,this.options.showZoom&&(this._setButtonTitle(this._zoomInButton,"ZoomIn"),this._setButtonTitle(this._zoomOutButton,"ZoomOut"),this._map.on("zoom",this._updateZoomButtons),this._updateZoomButtons()),this.options.showCompass&&(this._setButtonTitle(this._compass,"ResetBearing"),this.options.visualizePitch&&this._map.on("pitch",this._rotateCompassArrow),this._map.on("rotate",this._rotateCompassArrow),this._rotateCompassArrow(),this._handler=new on(this._map,this._compass,this.options.visualizePitch)),this._container}onRemove(){s.remove(this._container),this.options.showZoom&&this._map.off("zoom",this._updateZoomButtons),this.options.showCompass&&(this.options.visualizePitch&&this._map.off("pitch",this._rotateCompassArrow),this._map.off("rotate",this._rotateCompassArrow),this._handler.off(),delete this._handler),delete this._map}_createButton(t,e){const i=s.create("button",t,this._container);return i.type="button",i.addEventListener("click",e),i}_setButtonTitle(t,e){const i=this._map._getUIString(`NavigationControl.${e}`);t.title=i,t.setAttribute("aria-label",i)}},GeolocateControl:class extends t.Evented{constructor(e){super(),this.options=t.extend({},un,e),t.bindAll(["_onSuccess","_onError","_onZoom","_finish","_setupUI","_updateCamera","_updateMarker"],this)}onAdd(t){var e;return this._map=t,this._container=s.create("div","maplibregl-ctrl maplibregl-ctrl-group mapboxgl-ctrl mapboxgl-ctrl-group"),e=this._setupUI,void 0!==pn?e(pn):void 0!==window.navigator.permissions?window.navigator.permissions.query({name:"geolocation"}).then((t=>{pn="denied"!==t.state,e(pn)})):(pn=!!window.navigator.geolocation,e(pn)),this._container}onRemove(){void 0!==this._geolocationWatchID&&(window.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0),this.options.showUserLocation&&this._userLocationDotMarker&&this._userLocationDotMarker.remove(),this.options.showAccuracyCircle&&this._accuracyCircleMarker&&this._accuracyCircleMarker.remove(),s.remove(this._container),this._map.off("zoom",this._onZoom),this._map=void 0,dn=0,mn=!1}_isOutOfMapMaxBounds(t){const e=this._map.getMaxBounds(),i=t.coords;return e&&(i.longitudee.getEast()||i.latitudee.getNorth())}_setErrorState(){switch(this._watchState){case"WAITING_ACTIVE":this._watchState="ACTIVE_ERROR",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error","mapboxgl-ctrl-geolocate-active-error");break;case"ACTIVE_LOCK":this._watchState="ACTIVE_ERROR",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error","mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting");break;case"BACKGROUND":this._watchState="BACKGROUND_ERROR",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background","mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background-error","mapboxgl-ctrl-geolocate-background-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting")}}_onSuccess(e){if(this._map){if(this._isOutOfMapMaxBounds(e))return this._setErrorState(),this.fire(new t.Event("outofmaxbounds",e)),this._updateMarker(),void this._finish();if(this.options.trackUserLocation)switch(this._lastKnownPosition=e,this._watchState){case"WAITING_ACTIVE":case"ACTIVE_LOCK":case"ACTIVE_ERROR":this._watchState="ACTIVE_LOCK",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error","mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active");break;case"BACKGROUND":case"BACKGROUND_ERROR":this._watchState="BACKGROUND",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error","mapboxgl-ctrl-geolocate-background-error"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background","mapboxgl-ctrl-geolocate-background")}this.options.showUserLocation&&"OFF"!==this._watchState&&this._updateMarker(e),this.options.trackUserLocation&&"ACTIVE_LOCK"!==this._watchState||this._updateCamera(e),this.options.showUserLocation&&this._dotElement.classList.remove("maplibregl-user-location-dot-stale","mapboxgl-user-location-dot-stale"),this.fire(new t.Event("geolocate",e)),this._finish()}}_updateCamera(e){const i=new t.LngLat(e.coords.longitude,e.coords.latitude),r=e.coords.accuracy,n=this._map.getBearing(),s=t.extend({bearing:n},this.options.fitBoundsOptions);this._map.fitBounds(i.toBounds(r),s,{geolocateSource:!0})}_updateMarker(e){if(e){const i=new t.LngLat(e.coords.longitude,e.coords.latitude);this._accuracyCircleMarker.setLngLat(i).addTo(this._map),this._userLocationDotMarker.setLngLat(i).addTo(this._map),this._accuracy=e.coords.accuracy,this.options.showUserLocation&&this.options.showAccuracyCircle&&this._updateCircleRadius()}else this._userLocationDotMarker.remove(),this._accuracyCircleMarker.remove()}_updateCircleRadius(){const t=this._map._container.clientHeight/2,e=this._map.unproject([0,t]),i=this._map.unproject([1,t]),r=e.distanceTo(i),n=Math.ceil(2*this._accuracy/r);this._circleElement.style.width=`${n}px`,this._circleElement.style.height=`${n}px`}_onZoom(){this.options.showUserLocation&&this.options.showAccuracyCircle&&this._updateCircleRadius()}_onError(e){if(this._map){if(this.options.trackUserLocation)if(1===e.code){this._watchState="OFF",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error","mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background","mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error","mapboxgl-ctrl-geolocate-background-error"),this._geolocateButton.disabled=!0;const t=this._map._getUIString("GeolocateControl.LocationNotAvailable");this._geolocateButton.title=t,this._geolocateButton.setAttribute("aria-label",t),void 0!==this._geolocationWatchID&&this._clearWatch()}else{if(3===e.code&&mn)return;this._setErrorState()}"OFF"!==this._watchState&&this.options.showUserLocation&&this._dotElement.classList.add("maplibregl-user-location-dot-stale","mapboxgl-user-location-dot-stale"),this.fire(new t.Event("error",e)),this._finish()}}_finish(){this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=void 0}_setupUI(e){if(this._container.addEventListener("contextmenu",(t=>t.preventDefault())),this._geolocateButton=s.create("button","maplibregl-ctrl-geolocate mapboxgl-ctrl-geolocate",this._container),s.create("span","maplibregl-ctrl-icon mapboxgl-ctrl-icon",this._geolocateButton).setAttribute("aria-hidden","true"),this._geolocateButton.type="button",!1===e){t.warnOnce("Geolocation support is not available so the GeolocateControl will be disabled.");const e=this._map._getUIString("GeolocateControl.LocationNotAvailable");this._geolocateButton.disabled=!0,this._geolocateButton.title=e,this._geolocateButton.setAttribute("aria-label",e)}else{const t=this._map._getUIString("GeolocateControl.FindMyLocation");this._geolocateButton.title=t,this._geolocateButton.setAttribute("aria-label",t)}this.options.trackUserLocation&&(this._geolocateButton.setAttribute("aria-pressed","false"),this._watchState="OFF"),this.options.showUserLocation&&(this._dotElement=s.create("div","maplibregl-user-location-dot mapboxgl-user-location-dot"),this._userLocationDotMarker=new hn(this._dotElement),this._circleElement=s.create("div","maplibregl-user-location-accuracy-circle mapboxgl-user-location-accuracy-circle"),this._accuracyCircleMarker=new hn({element:this._circleElement,pitchAlignment:"map"}),this.options.trackUserLocation&&(this._watchState="OFF"),this._map.on("zoom",this._onZoom)),this._geolocateButton.addEventListener("click",this.trigger.bind(this)),this._setup=!0,this.options.trackUserLocation&&this._map.on("movestart",(e=>{e.geolocateSource||"ACTIVE_LOCK"!==this._watchState||e.originalEvent&&"resize"===e.originalEvent.type||(this._watchState="BACKGROUND",this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background","mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active"),this.fire(new t.Event("trackuserlocationend")))}))}trigger(){if(!this._setup)return t.warnOnce("Geolocate control triggered before added to a map"),!1;if(this.options.trackUserLocation){switch(this._watchState){case"OFF":this._watchState="WAITING_ACTIVE",this.fire(new t.Event("trackuserlocationstart"));break;case"WAITING_ACTIVE":case"ACTIVE_LOCK":case"ACTIVE_ERROR":case"BACKGROUND_ERROR":dn--,mn=!1,this._watchState="OFF",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error","mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background","mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error","mapboxgl-ctrl-geolocate-background-error"),this.fire(new t.Event("trackuserlocationend"));break;case"BACKGROUND":this._watchState="ACTIVE_LOCK",this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background","mapboxgl-ctrl-geolocate-background"),this._lastKnownPosition&&this._updateCamera(this._lastKnownPosition),this.fire(new t.Event("trackuserlocationstart"))}switch(this._watchState){case"WAITING_ACTIVE":this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active");break;case"ACTIVE_LOCK":this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active","mapboxgl-ctrl-geolocate-active")}if("OFF"===this._watchState&&void 0!==this._geolocationWatchID)this._clearWatch();else if(void 0===this._geolocationWatchID){let t;this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.setAttribute("aria-pressed","true"),dn++,dn>1?(t={maximumAge:6e5,timeout:0},mn=!0):(t=this.options.positionOptions,mn=!1),this._geolocationWatchID=window.navigator.geolocation.watchPosition(this._onSuccess,this._onError,t)}}else window.navigator.geolocation.getCurrentPosition(this._onSuccess,this._onError,this.options.positionOptions),this._timeoutId=setTimeout(this._finish,1e4);return!0}_clearWatch(){window.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0,this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting","mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.setAttribute("aria-pressed","false"),this.options.showUserLocation&&this._updateMarker(null)}},AttributionControl:Qr,LogoControl:tn,ScaleControl:class{constructor(e){this.options=t.extend({},fn,e),t.bindAll(["_onMove","setUnit"],this)}getDefaultPosition(){return"bottom-left"}_onMove(){gn(this._map,this._container,this.options)}onAdd(t){return this._map=t,this._container=s.create("div","maplibregl-ctrl maplibregl-ctrl-scale mapboxgl-ctrl mapboxgl-ctrl-scale",t.getContainer()),this._map.on("move",this._onMove),this._onMove(),this._container}onRemove(){s.remove(this._container),this._map.off("move",this._onMove),this._map=void 0}setUnit(t){this.options.unit=t,gn(this._map,this._container,this.options)}},FullscreenControl:class{constructor(e){this._fullscreen=!1,e&&e.container&&(e.container instanceof HTMLElement?this._container=e.container:t.warnOnce("Full screen control 'container' must be a DOM element.")),t.bindAll(["_onClickFullscreen","_changeIcon"],this),"onfullscreenchange"in document?this._fullscreenchange="fullscreenchange":"onmozfullscreenchange"in document?this._fullscreenchange="mozfullscreenchange":"onwebkitfullscreenchange"in document?this._fullscreenchange="webkitfullscreenchange":"onmsfullscreenchange"in document&&(this._fullscreenchange="MSFullscreenChange")}onAdd(e){return this._map=e,this._container||(this._container=this._map.getContainer()),this._controlContainer=s.create("div","maplibregl-ctrl maplibregl-ctrl-group mapboxgl-ctrl mapboxgl-ctrl-group"),this._checkFullscreenSupport()?this._setupUI():(this._controlContainer.style.display="none",t.warnOnce("This device does not support fullscreen mode.")),this._controlContainer}onRemove(){s.remove(this._controlContainer),this._map=null,window.document.removeEventListener(this._fullscreenchange,this._changeIcon)}_checkFullscreenSupport(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.msFullscreenEnabled||document.webkitFullscreenEnabled)}_setupUI(){const t=this._fullscreenButton=s.create("button","maplibregl-ctrl-fullscreen mapboxgl-ctrl-fullscreen",this._controlContainer);s.create("span","maplibregl-ctrl-icon mapboxgl-ctrl-icon",t).setAttribute("aria-hidden","true"),t.type="button",this._updateTitle(),this._fullscreenButton.addEventListener("click",this._onClickFullscreen),window.document.addEventListener(this._fullscreenchange,this._changeIcon)}_updateTitle(){const t=this._getTitle();this._fullscreenButton.setAttribute("aria-label",t),this._fullscreenButton.title=t}_getTitle(){return this._map._getUIString(this._isFullscreen()?"FullscreenControl.Exit":"FullscreenControl.Enter")}_isFullscreen(){return this._fullscreen}_changeIcon(){(window.document.fullscreenElement||window.document.mozFullScreenElement||window.document.webkitFullscreenElement||window.document.msFullscreenElement)===this._container!==this._fullscreen&&(this._fullscreen=!this._fullscreen,this._fullscreenButton.classList.toggle("maplibregl-ctrl-shrink"),this._fullscreenButton.classList.toggle("mapboxgl-ctrl-shrink"),this._fullscreenButton.classList.toggle("maplibregl-ctrl-fullscreen"),this._fullscreenButton.classList.toggle("mapboxgl-ctrl-fullscreen"),this._updateTitle())}_onClickFullscreen(){this._isFullscreen()?window.document.exitFullscreen?window.document.exitFullscreen():window.document.mozCancelFullScreen?window.document.mozCancelFullScreen():window.document.msExitFullscreen?window.document.msExitFullscreen():window.document.webkitCancelFullScreen&&window.document.webkitCancelFullScreen():this._container.requestFullscreen?this._container.requestFullscreen():this._container.mozRequestFullScreen?this._container.mozRequestFullScreen():this._container.msRequestFullscreen?this._container.msRequestFullscreen():this._container.webkitRequestFullscreen&&this._container.webkitRequestFullscreen()}},TerrainControl:class{constructor(e){this.options=e,t.bindAll(["_toggleTerrain","_updateTerrainIcon"],this)}onAdd(t){return this._map=t,this._container=s.create("div","maplibregl-ctrl maplibregl-ctrl-group mapboxgl-ctrl mapboxgl-ctrl-group"),this._terrainButton=s.create("button","maplibregl-ctrl-terrain mapboxgl-ctrl-terrain",this._container),s.create("span","maplibregl-ctrl-icon mapboxgl-ctrl-icon",this._terrainButton).setAttribute("aria-hidden","true"),this._terrainButton.type="button",this._terrainButton.addEventListener("click",this._toggleTerrain),this._updateTerrainIcon(),this._map.on("terrain",this._updateTerrainIcon),this._container}onRemove(){s.remove(this._container),this._map.off("terrain",this._updateTerrainIcon),this._map=void 0}_toggleTerrain(){this._map.getTerrain()?this._map.setTerrain(null):this._map.setTerrain(this.options),this._updateTerrainIcon()}_updateTerrainIcon(){this._terrainButton.classList.remove("maplibregl-ctrl-terrain","mapboxgl-ctrl-terrain"),this._terrainButton.classList.remove("maplibregl-ctrl-terrain-enabled","mapboxgl-ctrl-terrain-enabled"),this._map.style.terrain?(this._terrainButton.classList.add("maplibregl-ctrl-terrain-enabled","mapboxgl-ctrl-terrain-enabled"),this._terrainButton.title=this._map._getUIString("TerrainControl.disableTerrain")):(this._terrainButton.classList.add("maplibregl-ctrl-terrain","mapboxgl-ctrl-terrain"),this._terrainButton.title=this._map._getUIString("TerrainControl.enableTerrain"))}},Popup:class extends t.Evented{constructor(e){super(),this.options=t.extend(Object.create(yn),e),t.bindAll(["_update","_onClose","remove","_onMouseMove","_onMouseUp","_onDrag"],this)}addTo(e){return this._map&&this.remove(),this._map=e,this.options.closeOnClick&&this._map.on("click",this._onClose),this.options.closeOnMove&&this._map.on("move",this._onClose),this._map.on("remove",this.remove),this._update(),this._focusFirstElement(),this._trackPointer?(this._map.on("mousemove",this._onMouseMove),this._map.on("mouseup",this._onMouseUp),this._container&&this._container.classList.add("maplibregl-popup-track-pointer","mapboxgl-popup-track-pointer"),this._map._canvasContainer.classList.add("maplibregl-track-pointer","mapboxgl-track-pointer")):this._map.on("move",this._update),this.fire(new t.Event("open")),this}isOpen(){return!!this._map}remove(){return this._content&&s.remove(this._content),this._container&&(s.remove(this._container),delete this._container),this._map&&(this._map.off("move",this._update),this._map.off("move",this._onClose),this._map.off("click",this._onClose),this._map.off("remove",this.remove),this._map.off("mousemove",this._onMouseMove),this._map.off("mouseup",this._onMouseUp),this._map.off("drag",this._onDrag),delete this._map),this.fire(new t.Event("close")),this}getLngLat(){return this._lngLat}setLngLat(e){return this._lngLat=t.LngLat.convert(e),this._pos=null,this._trackPointer=!1,this._update(),this._map&&(this._map.on("move",this._update),this._map.off("mousemove",this._onMouseMove),this._container&&this._container.classList.remove("maplibregl-popup-track-pointer","mapboxgl-popup-track-pointer"),this._map._canvasContainer.classList.remove("maplibregl-track-pointer","mapboxgl-track-pointer")),this}trackPointer(){return this._trackPointer=!0,this._pos=null,this._update(),this._map&&(this._map.off("move",this._update),this._map.on("mousemove",this._onMouseMove),this._map.on("drag",this._onDrag),this._container&&this._container.classList.add("maplibregl-popup-track-pointer","mapboxgl-popup-track-pointer"),this._map._canvasContainer.classList.add("maplibregl-track-pointer","mapboxgl-track-pointer")),this}getElement(){return this._container}setText(t){return this.setDOMContent(document.createTextNode(t))}setHTML(t){const e=document.createDocumentFragment(),i=document.createElement("body");let r;for(i.innerHTML=t;r=i.firstChild,r;)e.appendChild(r);return this.setDOMContent(e)}getMaxWidth(){return this._container&&this._container.style.maxWidth}setMaxWidth(t){return this.options.maxWidth=t,this._update(),this}setDOMContent(t){if(this._content)for(;this._content.hasChildNodes();)this._content.firstChild&&this._content.removeChild(this._content.firstChild);else this._content=s.create("div","maplibregl-popup-content mapboxgl-popup-content",this._container);return this._content.appendChild(t),this._createCloseButton(),this._update(),this._focusFirstElement(),this}addClassName(t){this._container&&this._container.classList.add(t)}removeClassName(t){this._container&&this._container.classList.remove(t)}setOffset(t){return this.options.offset=t,this._update(),this}toggleClassName(t){if(this._container)return this._container.classList.toggle(t)}_createCloseButton(){this.options.closeButton&&(this._closeButton=s.create("button","maplibregl-popup-close-button mapboxgl-popup-close-button",this._content),this._closeButton.type="button",this._closeButton.setAttribute("aria-label","Close popup"),this._closeButton.innerHTML="×",this._closeButton.addEventListener("click",this._onClose))}_onMouseUp(t){this._update(t.point)}_onMouseMove(t){this._update(t.point)}_onDrag(t){this._update(t.point)}_update(t){if(!this._map||!this._lngLat&&!this._trackPointer||!this._content)return;if(this._container||(this._container=s.create("div","maplibregl-popup mapboxgl-popup",this._map.getContainer()),this._tip=s.create("div","maplibregl-popup-tip mapboxgl-popup-tip",this._container),this._container.appendChild(this._content),this.options.className&&this.options.className.split(" ").forEach((t=>this._container.classList.add(t))),this._trackPointer&&this._container.classList.add("maplibregl-popup-track-pointer","mapboxgl-popup-track-pointer")),this.options.maxWidth&&this._container.style.maxWidth!==this.options.maxWidth&&(this._container.style.maxWidth=this.options.maxWidth),this._map.transform.renderWorldCopies&&!this._trackPointer&&(this._lngLat=an(this._lngLat,this._pos,this._map.transform)),this._trackPointer&&!t)return;const e=this._pos=this._trackPointer&&t?t:this._map.project(this._lngLat);let i=this.options.anchor;const r=vn(this.options.offset);if(!i){const t=this._container.offsetWidth,n=this._container.offsetHeight;let s;s=e.y+r.bottom.ythis._map.transform.height-n?["bottom"]:[],e.xthis._map.transform.width-t/2&&s.push("right"),i=0===s.length?"bottom":s.join("-")}const n=e.add(r[i]).round();s.setTransform(this._container,`${ln[i]} translate(${n.x}px,${n.y}px)`),cn(this._container,i,"popup")}_focusFirstElement(){if(!this.options.focusAfterOpen||!this._container)return;const t=this._container.querySelector(xn);t&&t.focus()}_onClose(){this.remove()}},Marker:hn,Style:ee,LngLat:t.LngLat,LngLatBounds:t.LngLatBounds,Point:t.pointGeometry,MercatorCoordinate:t.MercatorCoordinate,Evented:t.Evented,AJAXError:t.AJAXError,config:t.config,CanvasSource:M,GeoJSONSource:I,ImageSource:z,RasterDEMTileSource:S,RasterTileSource:T,VectorTileSource:w,VideoSource:C,prewarm:function(){j().acquire(N)},clearPrewarmedResources:function(){const t=q;t&&(t.isPreloaded()&&1===t.numActive()?(t.release(N),q=null):console.warn("Could not clear WebWorkers since there are active Map instances that still reference it. The pre-warmed WebWorker pool can only be cleared when all map instances have been removed with map.remove()"))},get version(){return"2.3.0"},get workerCount(){return G.workerCount},set workerCount(t){G.workerCount=t},get maxParallelImageRequests(){return t.config.MAX_PARALLEL_IMAGE_REQUESTS},set maxParallelImageRequests(e){t.config.MAX_PARALLEL_IMAGE_REQUESTS=e},clearStorage(e){t.clearTileCache(e)},workerUrl:"",addProtocol(e,i){t.config.REGISTERED_PROTOCOLS[e]=i},removeProtocol(e){delete t.config.REGISTERED_PROTOCOLS[e]}};return Jr.extend(bn,{isSafari:t.isSafari,getPerformanceMetrics:t.PerformanceUtils.getPerformanceMetrics}),bn}));var n=i;return n}()}({get exports(){return h},set exports(t){h=t}});var u=h,p=e.Layer.extend({options:{updateInterval:32,padding:.1,interactive:!1,pane:"tilePane"},initialize:function(t){e.setOptions(this,t),this._throttledUpdate=e.Util.throttle(this._update,this.options.updateInterval,this)},onAdd:function(t){this._container||this._initContainer();const i=this.getPaneName();t.getPane(i).appendChild(this._container),this._initGL(),this._offset=this._map.containerPointToLayerPoint([0,0]),t.options.zoomAnimation&&e.DomEvent.on(t._proxy,e.DomUtil.TRANSITION_END,this._transitionEnd,this)},onRemove:function(t){this._map._proxy&&this._map.options.zoomAnimation&&e.DomEvent.off(this._map._proxy,e.DomUtil.TRANSITION_END,this._transitionEnd,this);const i=this.getPaneName();t.getPane(i).removeChild(this._container),this._container=null,this._glMap.remove(),this._glMap=null},getEvents:function(){return{move:this._throttledUpdate,zoomanim:this._animateZoom,zoom:this._pinchZoom,zoomstart:this._zoomStart,zoomend:this._zoomEnd,resize:this._resize}},getMaplibreMap:function(){return this._glMap},getCanvas:function(){return this._glMap.getCanvas()},getSize:function(){return this._map.getSize().multiplyBy(1+2*this.options.padding)},getOpacity:function(){return this.options.opacity},setOpacity:function(t){this.options.opacity=t,this._container.style.opacity=t},getBounds:function(){const t=this.getSize().multiplyBy(.5),i=this._map.latLngToContainerPoint(this._map.getCenter());return e.latLngBounds(this._map.containerPointToLatLng(i.subtract(t)),this._map.containerPointToLatLng(i.add(t)))},getContainer:function(){return this._container},getPaneName:function(){return this._map.getPane(this.options.pane)?this.options.pane:"tilePane"},_initContainer:function(){if(this._container)return;this._container=e.DomUtil.create("div","leaflet-gl-layer");const t=this.getSize(),i=this._map.getSize().multiplyBy(this.options.padding);this._container.style.width=t.x+"px",this._container.style.height=t.y+"px";const r=this._map.containerPointToLayerPoint([0,0]).subtract(i);e.DomUtil.setPosition(this._container,r)},_initGL:function(){if(this._glMap)return;const t=this._map.getCenter(),i=e.extend({},this.options,{container:this._container,center:[t.lng,t.lat],zoom:this._map.getZoom()-1,attributionControl:!1});this._glMap=new u.Map(i),this._glMap.once("styledata",function(t){this.fire("styleLoaded")}.bind(this)),this._glMap.transform.latRange=null,this._glMap.transform.maxValidLatitude=1/0,this._transformGL(this._glMap),this._glMap._canvas.canvas?this._glMap._actualCanvas=this._glMap._canvas.canvas:this._glMap._actualCanvas=this._glMap._canvas;const r=this._glMap._actualCanvas;e.DomUtil.addClass(r,"leaflet-image-layer"),e.DomUtil.addClass(r,"leaflet-zoom-animated"),this.options.interactive&&e.DomUtil.addClass(r,"leaflet-interactive"),this.options.className&&e.DomUtil.addClass(r,this.options.className)},_update:function(t){if(this._offset=this._map.containerPointToLayerPoint([0,0]),this._zooming)return;const i=this.getSize(),r=this._container,n=this._glMap,s=this._map.getSize().multiplyBy(this.options.padding),o=this._map.containerPointToLayerPoint([0,0]).subtract(s);e.DomUtil.setPosition(r,o),this._transformGL(n),n.transform.width!==i.x||n.transform.height!==i.y?(r.style.width=i.x+"px",r.style.height=i.y+"px",null!==n._resize&&void 0!==n._resize?n._resize():n.resize()):null!==n._update&&void 0!==n._update?n._update():n.update()},_transformGL:function(t){const e=this._map.getCenter(),i=t.transform;i.center=u.LngLat.convert([e.lng,e.lat]),i.zoom=this._map.getZoom()-1},_pinchZoom:function(t){this._glMap.jumpTo({zoom:this._map.getZoom()-1,center:this._map.getCenter()})},_animateZoom:function(t){const i=this._map.getZoomScale(t.zoom),r=this._map.getSize().multiplyBy(this.options.padding*i),n=this.getSize()._divideBy(2),s=this._map.project(t.center,t.zoom)._subtract(n)._add(this._map._getMapPanePos().add(r))._round(),o=this._map.project(this._map.getBounds().getNorthWest(),t.zoom)._subtract(s);e.DomUtil.setTransform(this._glMap._actualCanvas,o.subtract(this._offset),i)},_zoomStart:function(t){this._zooming=!0},_zoomEnd:function(){const t=this._map.getZoomScale(this._map.getZoom());e.DomUtil.setTransform(this._glMap._actualCanvas,null,t),this._zooming=!1,this._update()},_transitionEnd:function(t){e.Util.requestAnimFrame((function(){const t=this._map.getZoom(),i=this._map.getCenter(),r=this._map.latLngToContainerPoint(this._map.getBounds().getNorthWest());e.DomUtil.setTransform(this._glMap._actualCanvas,r,1),this._glMap.once("moveend",e.Util.bind((function(){this._zoomEnd()}),this)),this._glMap.jumpTo({center:i,zoom:t-1})}),this)}});function d(t){return new p(t)}var m=e.Layer.extend({options:{key:"ArcGIS:Streets"},initialize:function(t,i){if(i&&e.setOptions(this,i),this.options.apiKey&&(this.options.apikey=this.options.apiKey),this.options.token&&(this.options.apikey=this.options.token),!this.options.apikey&&!this.options.token)throw new Error("API Key or token is required for vectorBasemapLayer.");t&&(this.options.key=t),this._createLayer()},_createLayer:function(){const t=function(t,e){let i="https://basemaps-api.arcgis.com/arcgis/rest/services/styles/"+t+"?type=style";return e&&(i=i+"&apiKey="+e),i}(this.options.key,this.options.apikey);this._maplibreGL=d({style:t,pane:this.options.pane,opacity:this.options.opacity}),this._ready=!0,this.fire("ready",{},!0),this._maplibreGL.on("styleLoaded",function(t){this._setupAttribution()}.bind(this))},_setupAttribution:function(){const t=this._map;if(i.Util.setEsriAttribution(t),32===this.options.key.length){const e=this._maplibreGL.getMaplibreMap().style.stylesheet.sources,i=[];Object.keys(e).forEach((function(t){i.push(e[t].attribution),e[t].copyrightText&&e[t].copyrightText&&""!==e[t].copyrightText&&e[t].attribution!==e[t].copyrightText&&i.push(e[t].copyrightText)})),t.attributionControl.addAttribution(''+i.join(", ")+"")}else if(this.options.attributionUrls||(this.options.attributionUrls=this._getAttributionUrls(this.options.key)),this._map&&this.options.attributionUrls){if(this._map.attributionControl){for(let e=0;e')}i.Util._updateMapAttribution({target:this._map})}},_getAttributionUrls:function(t){return 0===t.indexOf("OSM:")?["https://static.arcgis.com/attribution/Vector/OpenStreetMap_v2"]:0===t.indexOf("ArcGIS:Imagery")?["https://static.arcgis.com/attribution/World_Imagery","https://static.arcgis.com/attribution/Vector/World_Basemap_v2"]:["https://static.arcgis.com/attribution/Vector/World_Basemap_v2"]},onAdd:function(t){this._map=t,this._initPane(),this._ready?this._asyncAdd():this.once("ready",(function(){this._asyncAdd()}),this)},_initPane:function(){if(this.options.pane||(this.options.key.indexOf(":Labels")>-1?this.options.pane="esri-labels":this.options.pane="tilePane"),!this._map.getPane(this.options.pane)){const t=this._map.createPane(this.options.pane);t.style.pointerEvents="none",t.style.zIndex="esri-labels"===this.options.pane?550:500}},onRemove:function(t){if(t.off("moveend",i.Util._updateMapAttribution),t.removeLayer(this._maplibreGL),t.attributionControl){const e=document.getElementsByClassName("esri-dynamic-attribution");if(e&&e.length>0){const i=e[0].outerHTML;t.attributionControl.removeAttribution(i)}}},_asyncAdd:function(){const t=this._map;t.on("moveend",i.Util._updateMapAttribution),this._maplibreGL.addTo(t,this)}});var f=e.Layer.extend({options:{pane:"overlayPane",portalUrl:"https://www.arcgis.com"},initialize:function(t,i){if(i&&e.setOptions(this,i),this.options.apiKey&&(this.options.apikey=this.options.apiKey),this.options.apikey&&(this.options.token=this.options.apikey),!t)throw new Error("An ITEM ID or SERVICE URL is required for vectorTileLayer.");t&&(this.options.key=t),this._createLayer()},_createLayer:function(){r(this.options.key,this.options,function(t,e,i,r){if(t)throw new Error(t);var n;if(n=r.tileInfo.spatialReference.wkid,c.indexOf(n)>=0||console.warn('This layer is not guaranteed to display properly because its service does not use the Web Mercator projection. The "tileInfo.spatialReference" property is:',r.tileInfo.spatialReference,"\nMore information is available at https://github.com/maplibre/maplibre-gl-js/issues/168 and https://github.com/Esri/esri-leaflet-vector/issues/94."),e=function(t,e,i,r){const n=Object.keys(t.sources);for(let s=0;s1&&(i.layout["text-font"]=[i.layout["text-font"][0]])}return t.sprite&&-1===t.sprite.indexOf("http")&&(t.sprite=e.replace("styles/root.json",t.sprite.replace("../","")),t.sprite+=r?"?token="+r:""),t.glyphs&&-1===t.glyphs.indexOf("http")&&(t.glyphs=e.replace("styles/root.json",t.glyphs.replace("../","")),t.glyphs+=r?"?token="+r:""),t}(e,i,r,this.options.token),!this.getAttribution()){const t=Object.keys(e.sources);this.options.attribution=e.sources[t[t.length-1]].attribution,this._map&&this._map.attributionControl&&this._map.attributionControl.addAttribution(this.getAttribution())}this.options.style&&"function"==typeof this.options.style&&(e=this.options.style(e)),this._maplibreGL=d({style:e,pane:this.options.pane,opacity:this.options.opacity}),this._ready=!0,this.fire("ready",{},!0)}.bind(this))},onAdd:function(t){this._map=t,this._ready?this._asyncAdd():this.once("ready",(function(){this._asyncAdd()}),this)},onRemove:function(t){t.removeLayer(this._maplibreGL)},_asyncAdd:function(){const t=this._map;this._maplibreGL.addTo(t,this)}});t.VERSION="4.0.1",t.VectorBasemapLayer=m,t.VectorTileLayer=f,t.vectorBasemapLayer=function(t,e){return new m(t,e)},t.vectorTileLayer=function(t,e){return new f(t,e)},Object.defineProperty(t,"__esModule",{value:!0})})); diff --git a/media/plg_system_nrframework/js/vendor/esri-leaflet.min.js b/media/plg_system_nrframework/js/vendor/esri-leaflet.min.js new file mode 100644 index 00000000..3ce045f2 --- /dev/null +++ b/media/plg_system_nrframework/js/vendor/esri-leaflet.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("leaflet")):"function"==typeof define&&define.amd?define(["exports","leaflet"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).L=t.L||{},t.L.esri={}),t.L)}(this,function(t,m){"use strict";var e=window.XMLHttpRequest&&"withCredentials"in new window.XMLHttpRequest,i=""===document.documentElement.style.pointerEvents,a={cors:e,pointerEvents:i},G={attributionWidthOffset:55},D=0;function l(t){var e,i,s,r="";for(e in t.f=t.f||"json",t)Object.prototype.hasOwnProperty.call(t,e)&&(i=t[e],s=Object.prototype.toString.call(i),r.length&&(r+="&"),s="[object Array]"===s?"[object Object]"===Object.prototype.toString.call(i[0])?JSON.stringify(i):i.join(","):"[object Object]"===s?JSON.stringify(i):"[object Date]"===s?i.valueOf():i,r+=encodeURIComponent(e)+"="+encodeURIComponent(s));return r}function u(s,r){var o=new window.XMLHttpRequest;return o.onerror=function(t){o.onreadystatechange=m.Util.falseFn,s.call(r,{error:{code:500,message:"XMLHttpRequest error"}},null)},o.onreadystatechange=function(){var e,i;if(4===o.readyState){try{e=JSON.parse(o.responseText)}catch(t){e=null,i={code:500,message:"Could not parse response as JSON. This could also be caused by a CORS or XMLHttpRequest error."}}!i&&e.error&&(i=e.error,e=null),o.onerror=m.Util.falseFn,s.call(r,i,e)}},o.ontimeout=function(){this.onerror()},o}function q(t,e,i,s){i=u(i,s);return i.open("POST",t),null!=s&&void 0!==s.options&&(i.timeout=s.options.timeout),i.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),i.send(l(e)),i}function B(t,e,i,s){i=u(i,s);return i.open("GET",t+"?"+l(e),!0),null!=s&&void 0!==s.options&&(i.timeout=s.options.timeout,s.options.withCredentials&&(i.withCredentials=!0)),i.send(null),i}function s(t,e,i,s){var r=l(e),o=u(i,s),n=(t+"?"+r).length;if(n<=2e3&&a.cors?o.open("GET",t+"?"+r):2e3Esri
'),m.DomUtil.addClass(e.attributionControl._container,"esri-truncated-attribution:hover"),m.DomUtil.addClass(e.attributionControl._container,"esri-truncated-attribution")),e.attributionControl._esriAttributionLayerCount=e.attributionControl._esriAttributionLayerCount+1)}function A(t){t.attributionControl&&(t.attributionControl._esriAttributionLayerCount&&1===t.attributionControl._esriAttributionLayerCount&&(t.attributionControl.setPrefix(j),m.DomUtil.removeClass(t.attributionControl._container,"esri-truncated-attribution:hover"),m.DomUtil.removeClass(t.attributionControl._container,"esri-truncated-attribution")),t.attributionControl._esriAttributionLayerCount=t.attributionControl._esriAttributionLayerCount-1)}function I(t){var e={geometry:null,geometryType:null};return t instanceof m.LatLngBounds?(e.geometry=g(t),e.geometryType="esriGeometryEnvelope",e):((t=(t=t.getLatLng?t.getLatLng():t)instanceof m.LatLng?{type:"Point",coordinates:[t.lng,t.lat]}:t)instanceof m.GeoJSON&&(t=t.getLayers()[0].feature.geometry,e.geometry=n(t),e.geometryType=x(t.type)),"Point"===(t="Feature"===(t=t.toGeoJSON?t.toGeoJSON():t).type?t.geometry:t).type||"LineString"===t.type||"Polygon"===t.type||"MultiPolygon"===t.type?(e.geometry=n(t),e.geometryType=x(t.type),e):void c("invalid geometry passed to spatial query. Should be L.LatLng, L.LatLngBounds, L.Marker or a GeoJSON Point, Line, Polygon or MultiPolygon object"))}function H(t,l){a.cors&&s(t,{},m.Util.bind(function(t,e){if(!t){l._esriAttributions=[];for(var i=0;i=l.minZoom&&n<=l.maxZoom&&(s+=", "+u)}s=s.substr(2),i.innerHTML=s,i.style.maxWidth=L(t),t.fire("attributionupdated",{attribution:s})}}}var X={warn:c,cleanUrl:v,getUrlParams:b,isArcgisOnline:K,geojsonTypeToArcGIS:x,responseToFeatureCollection:_,geojsonToArcGIS:n,arcgisToGeoJSON:W,boundsToExtent:g,extentToBounds:d,calcAttributionWidth:L,setEsriAttribution:S,_setGeometry:I,_getAttributionData:H,_updateMapAttribution:T,_findIdAttributeFromFeature:Q,_findIdAttributeFromResponse:V},C=m.Class.extend({options:{proxy:!1,useCors:e},generateSetter:function(e,t){return m.Util.bind(function(t){return this.params[e]=t,this},t)},initialize:function(t){if(t.request&&t.options?(this._service=t,m.Util.setOptions(this,t.options)):(m.Util.setOptions(this,t),this.options.url=v(t.url)),this.params=m.Util.extend({},this.params||{}),this.setters)for(var e in this.setters){var i=this.setters[e];this[e]=this.generateSetter(i,this)}},token:function(t){return this._service?this._service.authenticate(t):this.params.token=t,this},apikey:function(t){return this.token(t)},format:function(t){return this.params.returnUnformattedValues=!t,this},request:function(t,e){return this.options.requestParams&&m.Util.extend(this.params,this.options.requestParams),this._service?this._service.request(this.path,this.params,t,e):this._request("request",this.path,this.params,t,e)},_request:function(t,e,i,s,r){e=this.options.proxy?this.options.proxy+"?"+this.options.url+e:this.options.url+e;return"get"!==t&&"request"!==t||this.options.useCors?p[t](e,i,s,r):p.get.JSONP(e,i,s,r)}}),Y=C.extend({setters:{offset:"resultOffset",limit:"resultRecordCount",fields:"outFields",precision:"geometryPrecision",featureIds:"objectIds",returnGeometry:"returnGeometry",returnM:"returnM",transform:"datumTransformation",token:"token"},path:"query",params:{returnGeometry:!0,where:"1=1",outSR:4326,outFields:"*"},within:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelContains",this},intersects:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelIntersects",this},contains:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelWithin",this},crosses:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelCrosses",this},touches:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelTouches",this},overlaps:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelOverlaps",this},bboxIntersects:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelEnvelopeIntersects",this},indexIntersects:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelIndexIntersects",this},nearby:function(t,e){return t=m.latLng(t),this.params.geometry=[t.lng,t.lat],this.params.geometryType="esriGeometryPoint",this.params.spatialRel="esriSpatialRelIntersects",this.params.units="esriSRUnit_Meter",this.params.distance=e,this.params.inSR=4326,this},where:function(t){return this.params.where=t,this},between:function(t,e){return this.params.time=[t.valueOf(),e.valueOf()],this},simplify:function(t,e){var i=Math.abs(t.getBounds().getWest()-t.getBounds().getEast());return this.params.maxAllowableOffset=i/t.getSize().y*e,this},orderBy:function(t,e){return e=e||"ASC",this.params.orderByFields=this.params.orderByFields?this.params.orderByFields+",":"",this.params.orderByFields+=[t,e].join(" "),this},run:function(i,s){return this._cleanParams(),this.options.isModern||K(this.options.url)&&void 0===this.options.isModern?(this.params.f="geojson",this.request(function(t,e){this._trapSQLerrors(t),i.call(s,t,e,e)},this)):this.request(function(t,e){this._trapSQLerrors(t),i.call(s,t,e&&_(e),e)},this)},count:function(i,t){return this._cleanParams(),this.params.returnCountOnly=!0,this.request(function(t,e){i.call(this,t,e&&e.count,e)},t)},ids:function(i,t){return this._cleanParams(),this.params.returnIdsOnly=!0,this.request(function(t,e){i.call(this,t,e&&e.objectIds,e)},t)},bounds:function(i,s){return this._cleanParams(),this.params.returnExtentOnly=!0,this.request(function(t,e){e&&e.extent&&d(e.extent)?i.call(s,t,d(e.extent),e):i.call(s,t={message:"Invalid Bounds"},null,e)},s)},distinct:function(){return this.params.returnGeometry=!1,this.params.returnDistinctValues=!0,this},pixelSize:function(t){t=m.point(t);return this.params.pixelSize=[t.x,t.y],this},layer:function(t){return this.path=t+"/query",this},_trapSQLerrors:function(t){t&&"400"===t.code&&c("one common syntax error in query requests is encasing string values in double quotes instead of single quotes")},_cleanParams:function(){delete this.params.returnIdsOnly,delete this.params.returnExtentOnly,delete this.params.returnCountOnly},_setGeometryParams:function(t){this.params.inSR=4326;t=I(t);this.params.geometry=t.geometry,this.params.geometryType=t.geometryType}});function w(t){return new Y(t)}var $=C.extend({setters:{contains:"contains",text:"searchText",fields:"searchFields",spatialReference:"sr",sr:"sr",layers:"layers",returnGeometry:"returnGeometry",maxAllowableOffset:"maxAllowableOffset",precision:"geometryPrecision",dynamicLayers:"dynamicLayers",returnZ:"returnZ",returnM:"returnM",gdbVersion:"gdbVersion",token:"token"},path:"find",params:{sr:4326,contains:!0,returnGeometry:!0,returnZ:!0,returnM:!1},layerDefs:function(t,e){return this.params.layerDefs=this.params.layerDefs?this.params.layerDefs+";":"",this.params.layerDefs+=[t,e].join(":"),this},simplify:function(t,e){var i=Math.abs(t.getBounds().getWest()-t.getBounds().getEast());return this.params.maxAllowableOffset=i/t.getSize().y*e,this},run:function(i,s){return this.request(function(t,e){i.call(s,t,e&&_(e),e)},s)}});function tt(t){return new $(t)}var R=C.extend({path:"identify",between:function(t,e){return this.params.time=[t.valueOf(),e.valueOf()],this}}),et=R.extend({setters:{layers:"layers",precision:"geometryPrecision",tolerance:"tolerance",returnGeometry:"returnGeometry"},params:{sr:4326,layers:"all",tolerance:3,returnGeometry:!0},on:function(t){var e=g(t.getBounds()),t=t.getSize();return this.params.imageDisplay=[t.x,t.y,96],this.params.mapExtent=[e.xmin,e.ymin,e.xmax,e.ymax],this},at:function(t){return 2===t.length&&(t=m.latLng(t)),this._setGeometryParams(t),this},layerDef:function(t,e){return this.params.layerDefs=this.params.layerDefs?this.params.layerDefs+";":"",this.params.layerDefs+=[t,e].join(":"),this},simplify:function(t,e){var i=Math.abs(t.getBounds().getWest()-t.getBounds().getEast());return this.params.maxAllowableOffset=i/t.getSize().y*e,this},run:function(r,o){return this.request(function(t,e){if(t)r.call(o,t,void 0,e);else{var i=_(e);e.results=e.results.reverse();for(var s=0;s'+this.options.attribution+"":t}}),ct=m.TileLayer.extend({options:{zoomOffsetAllowance:.1,errorTileUrl:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEABAMAAACuXLVVAAAAA1BMVEUzNDVszlHHAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAAAAAAAAAB6mUWpAAAADZJREFUeJztwQEBAAAAgiD/r25IQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7waBAAABw08RwAAAAABJRU5ErkJggg=="},statics:{MercatorZoomLevels:{0:156543.033928,1:78271.5169639999,2:39135.7584820001,3:19567.8792409999,4:9783.93962049996,5:4891.96981024998,6:2445.98490512499,7:1222.99245256249,8:611.49622628138,9:305.748113140558,10:152.874056570411,11:76.4370282850732,12:38.2185141425366,13:19.1092570712683,14:9.55462853563415,15:4.77731426794937,16:2.38865713397468,17:1.19432856685505,18:.597164283559817,19:.298582141647617,20:.14929107082381,21:.07464553541191,22:.0373227677059525,23:.0186613838529763}},initialize:function(t){t=b(t=m.Util.setOptions(this,t)),this.tileUrl=(t.proxy?t.proxy+"?":"")+t.url+"tile/{z}/{y}/{x}"+(t.requestParams&&0this.options.maxZoom||ei.max.x)||!e.wrapLat&&(t.yi.max.y))return!1}if(!this.options.bounds)return!0;e=this._cellCoordsToBounds(t);return m.toLatLngBounds(this.options.bounds).overlaps(e)},_keyToBounds:function(t){return this._cellCoordsToBounds(this._keyToCellCoords(t))},_cellCoordsToNwSe:function(t){var e=this._map,i=this.getCellSize(),s=t.scaleBy(i),i=s.add(i);return[e.unproject(s,t.z),e.unproject(i,t.z)]},_cellCoordsToBounds:function(t){t=this._cellCoordsToNwSe(t),t=new m.LatLngBounds(t[0],t[1]);return t=this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_cellCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToCellCoords:function(t){var t=t.split(":"),e=new m.Point(+t[0],+t[1]);return e.z=+t[2],e},_removeCell:function(t){var e,i,s=this._cells[t];s&&(i=this._keyToCellCoords(t),e=this._wrapCoords(i),i=this._cellCoordsToBounds(this._wrapCoords(i)),s.current=!1,delete this._cells[t],this._activeCells[t]=s,this.cellLeave(i,e,t),this.fire("cellleave",{key:t,coords:e,bounds:i}))},_reuseCell:function(t){var e=this._cellCoordsToKey(t),i=(this._cells[e]=this._activeCells[e],this._cells[e].current=!0,this._wrapCoords(t)),t=this._cellCoordsToBounds(this._wrapCoords(t));this.cellEnter(t,i,e),this.fire("cellenter",{key:e,coords:i,bounds:t})},_createCell:function(t){var e=this._cellCoordsToKey(t),i=this._wrapCoords(t),s=this._cellCoordsToBounds(this._wrapCoords(t));this.createCell(s,i,e),this.fire("cellcreate",{key:e,coords:i,bounds:s}),this._cells[e]={coords:t,current:!0},m.Util.requestAnimFrame(this._pruneCells,this)},_cellReady:function(t,e,i){t=this._cellCoordsToKey(t);(i=this._cells[t])&&(i.loaded=+new Date,i.active=!0)},_getCellPos:function(t){return t.scaleBy(this.getCellSize())},_wrapCoords:function(t){var e=new m.Point(this._wrapX?m.Util.wrapNum(t.x,this._wrapX):t.x,this._wrapY?m.Util.wrapNum(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToCellRange:function(t){var e=this.getCellSize();return new m.Bounds(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))}});function M(t){this.values=[].concat(t||[])}M.prototype.query=function(t){t=this.getIndex(t);return this.values[t]},M.prototype.getIndex=function(t){this.dirty&&this.sort();for(var e,i,s=0,r=this.values.length-1;s<=r;)if(e=(s+r)/2|0,+(i=this.values[Math.round(e)]).value<+t)s=1+e;else{if(!(+i.value>+t))return e;r=e-1}return Math.abs(~r)},M.prototype.between=function(t,e){var i=this.getIndex(t),s=this.getIndex(e);if(0===i&&0===s)return[];for(;this.values[i-1]&&this.values[i-1].value===t;)i--;for(;this.values[s+1]&&this.values[s+1].value===e;)s++;return this.values[s]&&this.values[s].value===e&&this.values[s+1]&&s++,this.values.slice(i,s)},M.prototype.insert=function(t){return this.values.splice(this.getIndex(t.value),0,t),this},M.prototype.bulkAdd=function(t,e){return this.values=this.values.concat([].concat(t||[])),e?this.sort():this.dirty=!0,this};var U=k.extend({options:{attribution:null,where:"1=1",fields:["*"],from:!(M.prototype.sort=function(){return this.values.sort(function(t,e){return+e.value-+t.value}).reverse(),this.dirty=!1,this}),to:!1,timeField:!1,timeFilterMode:"server",simplifyFactor:0,precision:6,fetchAllFeatures:!1},initialize:function(t){if(k.prototype.initialize.call(this,t),t=b(t),t=m.Util.setOptions(this,t),this.service=ht(t),this.service.addEventParent(this),"*"!==this.options.fields[0]){for(var e=!1,i=0;ithis.options.maxZoom||t1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n=e[s]=e[s]||[],l={all:n,evt:null,found:null};return t&&i&&P(n)>0&&o(n,(function(e,n){if(e.eventName==t&&e.fn.toString()==i.toString())return l.found=!0,l.evt=n,!1})),l}function a(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=t.onElement,n=t.withCallback,s=t.avoidDuplicate,l=void 0===s||s,a=t.once,h=void 0!==a&&a,d=t.useCapture,c=void 0!==d&&d,u=arguments.length>2?arguments[2]:void 0,g=i||[];function v(e){T(n)&&n.call(u,e,this),h&&v.destroy()}return C(g)&&(g=document.querySelectorAll(g)),v.destroy=function(){o(g,(function(t){var i=r(t,e,v);i.found&&i.all.splice(i.evt,1),t.removeEventListener&&t.removeEventListener(e,v,c)}))},o(g,(function(t){var i=r(t,e,v);(t.addEventListener&&l&&!i.found||!l)&&(t.addEventListener(e,v,c),i.all.push({eventName:e,fn:v}))})),v}function h(e,t){o(t.split(" "),(function(t){return e.classList.add(t)}))}function d(e,t){o(t.split(" "),(function(t){return e.classList.remove(t)}))}function c(e,t){return e.classList.contains(t)}function u(e,t){for(;e!==document.body;){if(!(e=e.parentElement))return!1;if("function"==typeof e.matches?e.matches(t):e.msMatchesSelector(t))return e}}function g(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!e||""===t)return!1;if("none"==t)return T(i)&&i(),!1;var n=x(),s=t.split(" ");o(s,(function(t){h(e,"g"+t)})),a(n,{onElement:e,avoidDuplicate:!1,once:!0,withCallback:function(e,t){o(s,(function(e){d(t,"g"+e)})),T(i)&&i()}})}function v(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(""==t)return e.style.webkitTransform="",e.style.MozTransform="",e.style.msTransform="",e.style.OTransform="",e.style.transform="",!1;e.style.webkitTransform=t,e.style.MozTransform=t,e.style.msTransform=t,e.style.OTransform=t,e.style.transform=t}function f(e){e.style.display="block"}function p(e){e.style.display="none"}function m(e){var t=document.createDocumentFragment(),i=document.createElement("div");for(i.innerHTML=e;i.firstChild;)t.appendChild(i.firstChild);return t}function y(){return{width:window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,height:window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight}}function x(){var e,t=document.createElement("fakeelement"),i={animation:"animationend",OAnimation:"oAnimationEnd",MozAnimation:"animationend",WebkitAnimation:"webkitAnimationEnd"};for(e in i)if(void 0!==t.style[e])return i[e]}function b(e,t,i,n){if(e())t();else{var s;i||(i=100);var l=setInterval((function(){e()&&(clearInterval(l),s&&clearTimeout(s),t())}),i);n&&(s=setTimeout((function(){clearInterval(l)}),n))}}function S(e,t,i){if(I(e))console.error("Inject assets error");else if(T(t)&&(i=t,t=!1),C(t)&&t in window)T(i)&&i();else{var n;if(-1!==e.indexOf(".css")){if((n=document.querySelectorAll('link[href="'+e+'"]'))&&n.length>0)return void(T(i)&&i());var s=document.getElementsByTagName("head")[0],l=s.querySelectorAll('link[rel="stylesheet"]'),o=document.createElement("link");return o.rel="stylesheet",o.type="text/css",o.href=e,o.media="all",l?s.insertBefore(o,l[0]):s.appendChild(o),void(T(i)&&i())}if((n=document.querySelectorAll('script[src="'+e+'"]'))&&n.length>0){if(T(i)){if(C(t))return b((function(){return void 0!==window[t]}),(function(){i()})),!1;i()}}else{var r=document.createElement("script");r.type="text/javascript",r.src=e,r.onload=function(){if(T(i)){if(C(t))return b((function(){return void 0!==window[t]}),(function(){i()})),!1;i()}},document.body.appendChild(r)}}}function w(){return"navigator"in window&&window.navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(Android)|(PlayBook)|(BB10)|(BlackBerry)|(Opera Mini)|(IEMobile)|(webOS)|(MeeGo)/i)}function T(e){return"function"==typeof e}function C(e){return"string"==typeof e}function k(e){return!(!e||!e.nodeType||1!=e.nodeType)}function E(e){return Array.isArray(e)}function A(e){return e&&e.length&&isFinite(e.length)}function L(t){return"object"===e(t)&&null!=t&&!T(t)&&!E(t)}function I(e){return null==e}function O(e,t){return null!==e&&hasOwnProperty.call(e,t)}function P(e){if(L(e)){if(e.keys)return e.keys().length;var t=0;for(var i in e)O(e,i)&&t++;return t}return e.length}function M(e){return!isNaN(parseFloat(e))&&isFinite(e)}function X(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:-1,t=document.querySelectorAll(".gbtn[data-taborder]:not(.disabled)");if(!t.length)return!1;if(1==t.length)return t[0];"string"==typeof e&&(e=parseInt(e));var i=e<0?1:e+1;i>t.length&&(i="1");var n=[];o(t,(function(e){n.push(e.getAttribute("data-taborder"))}));var s=n.filter((function(e){return e>=parseInt(i)})),l=s.sort()[0];return document.querySelector('.gbtn[data-taborder="'.concat(l,'"]'))}function z(e){if(e.events.hasOwnProperty("keyboard"))return!1;e.events.keyboard=a("keydown",{onElement:window,withCallback:function(t,i){var n=(t=t||window.event).keyCode;if(9==n){var s=document.querySelector(".gbtn.focused");if(!s){var l=!(!document.activeElement||!document.activeElement.nodeName)&&document.activeElement.nodeName.toLocaleLowerCase();if("input"==l||"textarea"==l||"button"==l)return}t.preventDefault();var o=document.querySelectorAll(".gbtn[data-taborder]");if(!o||o.length<=0)return;if(!s){var r=X();return void(r&&(r.focus(),h(r,"focused")))}var a=X(s.getAttribute("data-taborder"));d(s,"focused"),a&&(a.focus(),h(a,"focused"))}39==n&&e.nextSlide(),37==n&&e.prevSlide(),27==n&&e.close()}})}function Y(e){return Math.sqrt(e.x*e.x+e.y*e.y)}function q(e,t){var i=function(e,t){var i=Y(e)*Y(t);if(0===i)return 0;var n=function(e,t){return e.x*t.x+e.y*t.y}(e,t)/i;return n>1&&(n=1),Math.acos(n)}(e,t);return function(e,t){return e.x*t.y-t.x*e.y}(e,t)>0&&(i*=-1),180*i/Math.PI}var N=function(){function e(i){t(this,e),this.handlers=[],this.el=i}return n(e,[{key:"add",value:function(e){this.handlers.push(e)}},{key:"del",value:function(e){e||(this.handlers=[]);for(var t=this.handlers.length;t>=0;t--)this.handlers[t]===e&&this.handlers.splice(t,1)}},{key:"dispatch",value:function(){for(var e=0,t=this.handlers.length;e=0)console.log("ignore drag for this touched element",e.target.nodeName.toLowerCase());else{this.now=Date.now(),this.x1=e.touches[0].pageX,this.y1=e.touches[0].pageY,this.delta=this.now-(this.last||this.now),this.touchStart.dispatch(e,this.element),null!==this.preTapPosition.x&&(this.isDoubleTap=this.delta>0&&this.delta<=250&&Math.abs(this.preTapPosition.x-this.x1)<30&&Math.abs(this.preTapPosition.y-this.y1)<30,this.isDoubleTap&&clearTimeout(this.singleTapTimeout)),this.preTapPosition.x=this.x1,this.preTapPosition.y=this.y1,this.last=this.now;var t=this.preV;if(e.touches.length>1){this._cancelLongTap(),this._cancelSingleTap();var i={x:e.touches[1].pageX-this.x1,y:e.touches[1].pageY-this.y1};t.x=i.x,t.y=i.y,this.pinchStartLen=Y(t),this.multipointStart.dispatch(e,this.element)}this._preventTap=!1,this.longTapTimeout=setTimeout(function(){this.longTap.dispatch(e,this.element),this._preventTap=!0}.bind(this),750)}}}},{key:"move",value:function(e){if(e.touches){var t=this.preV,i=e.touches.length,n=e.touches[0].pageX,s=e.touches[0].pageY;if(this.isDoubleTap=!1,i>1){var l=e.touches[1].pageX,o=e.touches[1].pageY,r={x:e.touches[1].pageX-n,y:e.touches[1].pageY-s};null!==t.x&&(this.pinchStartLen>0&&(e.zoom=Y(r)/this.pinchStartLen,this.pinch.dispatch(e,this.element)),e.angle=q(r,t),this.rotate.dispatch(e,this.element)),t.x=r.x,t.y=r.y,null!==this.x2&&null!==this.sx2?(e.deltaX=(n-this.x2+l-this.sx2)/2,e.deltaY=(s-this.y2+o-this.sy2)/2):(e.deltaX=0,e.deltaY=0),this.twoFingerPressMove.dispatch(e,this.element),this.sx2=l,this.sy2=o}else{if(null!==this.x2){e.deltaX=n-this.x2,e.deltaY=s-this.y2;var a=Math.abs(this.x1-this.x2),h=Math.abs(this.y1-this.y2);(a>10||h>10)&&(this._preventTap=!0)}else e.deltaX=0,e.deltaY=0;this.pressMove.dispatch(e,this.element)}this.touchMove.dispatch(e,this.element),this._cancelLongTap(),this.x2=n,this.y2=s,i>1&&e.preventDefault()}}},{key:"end",value:function(e){if(e.changedTouches){this._cancelLongTap();var t=this;e.touches.length<2&&(this.multipointEnd.dispatch(e,this.element),this.sx2=this.sy2=null),this.x2&&Math.abs(this.x1-this.x2)>30||this.y2&&Math.abs(this.y1-this.y2)>30?(e.direction=this._swipeDirection(this.x1,this.x2,this.y1,this.y2),this.swipeTimeout=setTimeout((function(){t.swipe.dispatch(e,t.element)}),0)):(this.tapTimeout=setTimeout((function(){t._preventTap||t.tap.dispatch(e,t.element),t.isDoubleTap&&(t.doubleTap.dispatch(e,t.element),t.isDoubleTap=!1)}),0),t.isDoubleTap||(t.singleTapTimeout=setTimeout((function(){t.singleTap.dispatch(e,t.element)}),250))),this.touchEnd.dispatch(e,this.element),this.preV.x=0,this.preV.y=0,this.zoom=1,this.pinchStartLen=null,this.x1=this.x2=this.y1=this.y2=null}}},{key:"cancelAll",value:function(){this._preventTap=!0,clearTimeout(this.singleTapTimeout),clearTimeout(this.tapTimeout),clearTimeout(this.longTapTimeout),clearTimeout(this.swipeTimeout)}},{key:"cancel",value:function(e){this.cancelAll(),this.touchCancel.dispatch(e,this.element)}},{key:"_cancelLongTap",value:function(){clearTimeout(this.longTapTimeout)}},{key:"_cancelSingleTap",value:function(){clearTimeout(this.singleTapTimeout)}},{key:"_swipeDirection",value:function(e,t,i,n){return Math.abs(e-t)>=Math.abs(i-n)?e-t>0?"Left":"Right":i-n>0?"Up":"Down"}},{key:"on",value:function(e,t){this[e]&&this[e].add(t)}},{key:"off",value:function(e,t){this[e]&&this[e].del(t)}},{key:"destroy",value:function(){return this.singleTapTimeout&&clearTimeout(this.singleTapTimeout),this.tapTimeout&&clearTimeout(this.tapTimeout),this.longTapTimeout&&clearTimeout(this.longTapTimeout),this.swipeTimeout&&clearTimeout(this.swipeTimeout),this.element.removeEventListener("touchstart",this.start),this.element.removeEventListener("touchmove",this.move),this.element.removeEventListener("touchend",this.end),this.element.removeEventListener("touchcancel",this.cancel),this.rotate.del(),this.touchStart.del(),this.multipointStart.del(),this.multipointEnd.del(),this.pinch.del(),this.swipe.del(),this.tap.del(),this.doubleTap.del(),this.longTap.del(),this.singleTap.del(),this.pressMove.del(),this.twoFingerPressMove.del(),this.touchMove.del(),this.touchEnd.del(),this.touchCancel.del(),this.preV=this.pinchStartLen=this.zoom=this.isDoubleTap=this.delta=this.last=this.now=this.tapTimeout=this.singleTapTimeout=this.longTapTimeout=this.swipeTimeout=this.x1=this.x2=this.y1=this.y2=this.preTapPosition=this.rotate=this.touchStart=this.multipointStart=this.multipointEnd=this.pinch=this.swipe=this.tap=this.doubleTap=this.longTap=this.singleTap=this.pressMove=this.touchMove=this.touchEnd=this.touchCancel=this.twoFingerPressMove=null,window.removeEventListener("scroll",this._cancelAllHandler),null}}]),e}();function W(e){var t=function(){var e,t=document.createElement("fakeelement"),i={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(e in i)if(void 0!==t.style[e])return i[e]}(),i=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,n=c(e,"gslide-media")?e:e.querySelector(".gslide-media"),s=u(n,".ginner-container"),l=e.querySelector(".gslide-description");i>769&&(n=s),h(n,"greset"),v(n,"translate3d(0, 0, 0)"),a(t,{onElement:n,once:!0,withCallback:function(e,t){d(n,"greset")}}),n.style.opacity="",l&&(l.style.opacity="")}function B(e){if(e.events.hasOwnProperty("touch"))return!1;var t,i,n,s=y(),l=s.width,o=s.height,r=!1,a=null,g=null,f=null,p=!1,m=1,x=1,b=!1,S=!1,w=null,T=null,C=null,k=null,E=0,A=0,L=!1,I=!1,O={},P={},M=0,X=0,z=document.getElementById("glightbox-slider"),Y=document.querySelector(".goverlay"),q=new _(z,{touchStart:function(t){if(r=!0,(c(t.targetTouches[0].target,"ginner-container")||u(t.targetTouches[0].target,".gslide-desc")||"a"==t.targetTouches[0].target.nodeName.toLowerCase())&&(r=!1),u(t.targetTouches[0].target,".gslide-inline")&&!c(t.targetTouches[0].target.parentNode,"gslide-inline")&&(r=!1),r){if(P=t.targetTouches[0],O.pageX=t.targetTouches[0].pageX,O.pageY=t.targetTouches[0].pageY,M=t.targetTouches[0].clientX,X=t.targetTouches[0].clientY,a=e.activeSlide,g=a.querySelector(".gslide-media"),n=a.querySelector(".gslide-inline"),f=null,c(g,"gslide-image")&&(f=g.querySelector("img")),(window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth)>769&&(g=a.querySelector(".ginner-container")),d(Y,"greset"),t.pageX>20&&t.pageXo){var a=O.pageX-P.pageX;if(Math.abs(a)<=13)return!1}p=!0;var h,d=s.targetTouches[0].clientX,c=s.targetTouches[0].clientY,u=M-d,m=X-c;if(Math.abs(u)>Math.abs(m)?(L=!1,I=!0):(I=!1,L=!0),t=P.pageX-O.pageX,E=100*t/l,i=P.pageY-O.pageY,A=100*i/o,L&&f&&(h=1-Math.abs(i)/o,Y.style.opacity=h,e.settings.touchFollowAxis&&(E=0)),I&&(h=1-Math.abs(t)/l,g.style.opacity=h,e.settings.touchFollowAxis&&(A=0)),!f)return v(g,"translate3d(".concat(E,"%, 0, 0)"));v(g,"translate3d(".concat(E,"%, ").concat(A,"%, 0)"))}},touchEnd:function(){if(r){if(p=!1,S||b)return C=w,void(k=T);var t=Math.abs(parseInt(A)),i=Math.abs(parseInt(E));if(!(t>29&&f))return t<29&&i<25?(h(Y,"greset"),Y.style.opacity=1,W(g)):void 0;e.close()}},multipointEnd:function(){setTimeout((function(){b=!1}),50)},multipointStart:function(){b=!0,m=x||1},pinch:function(e){if(!f||p)return!1;b=!0,f.scaleX=f.scaleY=m*e.zoom;var t=m*e.zoom;if(S=!0,t<=1)return S=!1,t=1,k=null,C=null,w=null,T=null,void f.setAttribute("style","");t>4.5&&(t=4.5),f.style.transform="scale3d(".concat(t,", ").concat(t,", 1)"),x=t},pressMove:function(e){if(S&&!b){var t=P.pageX-O.pageX,i=P.pageY-O.pageY;C&&(t+=C),k&&(i+=k),w=t,T=i;var n="translate3d(".concat(t,"px, ").concat(i,"px, 0)");x&&(n+=" scale3d(".concat(x,", ").concat(x,", 1)")),v(f,n)}},swipe:function(t){if(!S)if(b)b=!1;else{if("Left"==t.direction){if(e.index==e.elements.length-1)return W(g);e.nextSlide()}if("Right"==t.direction){if(0==e.index)return W(g);e.prevSlide()}}}});e.events.touch=q}var H=function(){function e(i,n){var s=this,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(t(this,e),this.img=i,this.slide=n,this.onclose=l,this.img.setZoomEvents)return!1;this.active=!1,this.zoomedIn=!1,this.dragging=!1,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.img.addEventListener("mousedown",(function(e){return s.dragStart(e)}),!1),this.img.addEventListener("mouseup",(function(e){return s.dragEnd(e)}),!1),this.img.addEventListener("mousemove",(function(e){return s.drag(e)}),!1),this.img.addEventListener("click",(function(e){return s.slide.classList.contains("dragging-nav")?(s.zoomOut(),!1):s.zoomedIn?void(s.zoomedIn&&!s.dragging&&s.zoomOut()):s.zoomIn()}),!1),this.img.setZoomEvents=!0}return n(e,[{key:"zoomIn",value:function(){var e=this.widowWidth();if(!(this.zoomedIn||e<=768)){var t=this.img;if(t.setAttribute("data-style",t.getAttribute("style")),t.style.maxWidth=t.naturalWidth+"px",t.style.maxHeight=t.naturalHeight+"px",t.naturalWidth>e){var i=e/2-t.naturalWidth/2;this.setTranslate(this.img.parentNode,i,0)}this.slide.classList.add("zoomed"),this.zoomedIn=!0}}},{key:"zoomOut",value:function(){this.img.parentNode.setAttribute("style",""),this.img.setAttribute("style",this.img.getAttribute("data-style")),this.slide.classList.remove("zoomed"),this.zoomedIn=!1,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.onclose&&"function"==typeof this.onclose&&this.onclose()}},{key:"dragStart",value:function(e){e.preventDefault(),this.zoomedIn?("touchstart"===e.type?(this.initialX=e.touches[0].clientX-this.xOffset,this.initialY=e.touches[0].clientY-this.yOffset):(this.initialX=e.clientX-this.xOffset,this.initialY=e.clientY-this.yOffset),e.target===this.img&&(this.active=!0,this.img.classList.add("dragging"))):this.active=!1}},{key:"dragEnd",value:function(e){var t=this;e.preventDefault(),this.initialX=this.currentX,this.initialY=this.currentY,this.active=!1,setTimeout((function(){t.dragging=!1,t.img.isDragging=!1,t.img.classList.remove("dragging")}),100)}},{key:"drag",value:function(e){this.active&&(e.preventDefault(),"touchmove"===e.type?(this.currentX=e.touches[0].clientX-this.initialX,this.currentY=e.touches[0].clientY-this.initialY):(this.currentX=e.clientX-this.initialX,this.currentY=e.clientY-this.initialY),this.xOffset=this.currentX,this.yOffset=this.currentY,this.img.isDragging=!0,this.dragging=!0,this.setTranslate(this.img,this.currentX,this.currentY))}},{key:"onMove",value:function(e){if(this.zoomedIn){var t=e.clientX-this.img.naturalWidth/2,i=e.clientY-this.img.naturalHeight/2;this.setTranslate(this.img,t,i)}}},{key:"setTranslate",value:function(e,t,i){e.style.transform="translate3d("+t+"px, "+i+"px, 0)"}},{key:"widowWidth",value:function(){return window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth}}]),e}(),V=function(){function e(){var i=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this,e);var s=n.dragEl,l=n.toleranceX,o=void 0===l?40:l,r=n.toleranceY,a=void 0===r?65:r,h=n.slide,d=void 0===h?null:h,c=n.instance,u=void 0===c?null:c;this.el=s,this.active=!1,this.dragging=!1,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.direction=null,this.lastDirection=null,this.toleranceX=o,this.toleranceY=a,this.toleranceReached=!1,this.dragContainer=this.el,this.slide=d,this.instance=u,this.el.addEventListener("mousedown",(function(e){return i.dragStart(e)}),!1),this.el.addEventListener("mouseup",(function(e){return i.dragEnd(e)}),!1),this.el.addEventListener("mousemove",(function(e){return i.drag(e)}),!1)}return n(e,[{key:"dragStart",value:function(e){if(this.slide.classList.contains("zoomed"))this.active=!1;else{"touchstart"===e.type?(this.initialX=e.touches[0].clientX-this.xOffset,this.initialY=e.touches[0].clientY-this.yOffset):(this.initialX=e.clientX-this.xOffset,this.initialY=e.clientY-this.yOffset);var t=e.target.nodeName.toLowerCase();e.target.classList.contains("nodrag")||u(e.target,".nodrag")||-1!==["input","select","textarea","button","a"].indexOf(t)?this.active=!1:(e.preventDefault(),(e.target===this.el||"img"!==t&&u(e.target,".gslide-inline"))&&(this.active=!0,this.el.classList.add("dragging"),this.dragContainer=u(e.target,".ginner-container")))}}},{key:"dragEnd",value:function(e){var t=this;e&&e.preventDefault(),this.initialX=0,this.initialY=0,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.active=!1,this.doSlideChange&&(this.instance.preventOutsideClick=!0,"right"==this.doSlideChange&&this.instance.prevSlide(),"left"==this.doSlideChange&&this.instance.nextSlide()),this.doSlideClose&&this.instance.close(),this.toleranceReached||this.setTranslate(this.dragContainer,0,0,!0),setTimeout((function(){t.instance.preventOutsideClick=!1,t.toleranceReached=!1,t.lastDirection=null,t.dragging=!1,t.el.isDragging=!1,t.el.classList.remove("dragging"),t.slide.classList.remove("dragging-nav"),t.dragContainer.style.transform="",t.dragContainer.style.transition=""}),100)}},{key:"drag",value:function(e){if(this.active){e.preventDefault(),this.slide.classList.add("dragging-nav"),"touchmove"===e.type?(this.currentX=e.touches[0].clientX-this.initialX,this.currentY=e.touches[0].clientY-this.initialY):(this.currentX=e.clientX-this.initialX,this.currentY=e.clientY-this.initialY),this.xOffset=this.currentX,this.yOffset=this.currentY,this.el.isDragging=!0,this.dragging=!0,this.doSlideChange=!1,this.doSlideClose=!1;var t=Math.abs(this.currentX),i=Math.abs(this.currentY);if(t>0&&t>=Math.abs(this.currentY)&&(!this.lastDirection||"x"==this.lastDirection)){this.yOffset=0,this.lastDirection="x",this.setTranslate(this.dragContainer,this.currentX,0);var n=this.shouldChange();if(!this.instance.settings.dragAutoSnap&&n&&(this.doSlideChange=n),this.instance.settings.dragAutoSnap&&n)return this.instance.preventOutsideClick=!0,this.toleranceReached=!0,this.active=!1,this.instance.preventOutsideClick=!0,this.dragEnd(null),"right"==n&&this.instance.prevSlide(),void("left"==n&&this.instance.nextSlide())}if(this.toleranceY>0&&i>0&&i>=t&&(!this.lastDirection||"y"==this.lastDirection)){this.xOffset=0,this.lastDirection="y",this.setTranslate(this.dragContainer,0,this.currentY);var s=this.shouldClose();return!this.instance.settings.dragAutoSnap&&s&&(this.doSlideClose=!0),void(this.instance.settings.dragAutoSnap&&s&&this.instance.close())}}}},{key:"shouldChange",value:function(){var e=!1;if(Math.abs(this.currentX)>=this.toleranceX){var t=this.currentX>0?"right":"left";("left"==t&&this.slide!==this.slide.parentNode.lastChild||"right"==t&&this.slide!==this.slide.parentNode.firstChild)&&(e=t)}return e}},{key:"shouldClose",value:function(){var e=!1;return Math.abs(this.currentY)>=this.toleranceY&&(e=!0),e}},{key:"setTranslate",value:function(e,t,i){var n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];e.style.transition=n?"all .2s ease":"",e.style.transform="translate3d(".concat(t,"px, ").concat(i,"px, 0)")}}]),e}();function j(e,t,i,n){var s=e.querySelector(".gslide-media"),l=new Image,o="gSlideTitle_"+i,r="gSlideDesc_"+i;l.addEventListener("load",(function(){T(n)&&n()}),!1),l.src=t.href,l.alt="",I(t.alt)||""===t.alt||(l.alt=t.alt),""!==t.title&&l.setAttribute("aria-labelledby",o),""!==t.description&&l.setAttribute("aria-describedby",r),t.hasOwnProperty("_hasCustomWidth")&&t._hasCustomWidth&&(l.style.width=t.width),t.hasOwnProperty("_hasCustomHeight")&&t._hasCustomHeight&&(l.style.height=t.height),s.insertBefore(l,s.firstChild)}function F(e,t,i,n){var s=this,l=e.querySelector(".ginner-container"),o="gvideo"+i,r=e.querySelector(".gslide-media"),a=this.getAllPlayers();h(l,"gvideo-container"),r.insertBefore(m('
'),r.firstChild);var d=e.querySelector(".gvideo-wrapper");S(this.settings.plyr.css,"Plyr");var c=t.href,u=location.protocol.replace(":",""),g="",v="",f=!1;"file"==u&&(u="http"),r.style.maxWidth=t.width,S(this.settings.plyr.js,"Plyr",(function(){if(c.match(/vimeo\.com\/([0-9]*)/)){var l=/vimeo.*\/(\d+)/i.exec(c);g="vimeo",v=l[1]}if(c.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/)||c.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/)||c.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/)){var r=function(e){var t="";t=void 0!==(e=e.replace(/(>|<)/gi,"").split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/))[2]?(t=e[2].split(/[^0-9a-z_\-]/i))[0]:e;return t}(c);g="youtube",v=r}if(null!==c.match(/\.(mp4|ogg|webm|mov)$/)){g="local";var u='")}var w=f||m('
'));h(d,"".concat(g,"-video gvideo")),d.appendChild(w),d.setAttribute("data-id",o),d.setAttribute("data-index",i);var C=O(s.settings.plyr,"config")?s.settings.plyr.config:{},k=new Plyr("#"+o,C);k.on("ready",(function(e){var t=e.detail.plyr;a[o]=t,T(n)&&n()})),b((function(){return e.querySelector("iframe")&&"true"==e.querySelector("iframe").dataset.ready}),(function(){s.resize(e)})),k.on("enterfullscreen",R),k.on("exitfullscreen",R)}))}function R(e){var t=u(e.target,".gslide-media");"enterfullscreen"==e.type&&h(t,"fullscreen"),"exitfullscreen"==e.type&&d(t,"fullscreen")}function G(e,t,i,n){var s,l=this,o=e.querySelector(".gslide-media"),r=!(!O(t,"href")||!t.href)&&t.href.split("#").pop().trim(),d=!(!O(t,"content")||!t.content)&&t.content;if(d&&(C(d)&&(s=m('
'.concat(d,"
"))),k(d))){"none"==d.style.display&&(d.style.display="block");var c=document.createElement("div");c.className="ginlined-content",c.appendChild(d),s=c}if(r){var u=document.getElementById(r);if(!u)return!1;var g=u.cloneNode(!0);g.style.height=t.height,g.style.maxWidth=t.width,h(g,"ginlined-content"),s=g}if(!s)return console.error("Unable to append inline slide content",t),!1;o.style.height=t.height,o.style.width=t.width,o.appendChild(s),this.events["inlineclose"+r]=a("click",{onElement:o.querySelectorAll(".gtrigger-close"),withCallback:function(e){e.preventDefault(),l.close()}}),T(n)&&n()}function Z(e,t,i,n){var s=e.querySelector(".gslide-media"),l=function(e){var t=e.url,i=e.allow,n=e.callback,s=e.appendTo,l=document.createElement("iframe");return l.className="vimeo-video gvideo",l.src=t,l.style.width="100%",l.style.height="100%",i&&l.setAttribute("allow",i),l.onload=function(){h(l,"node-ready"),T(n)&&n()},s&&s.appendChild(l),l}({url:t.href,callback:n});s.parentNode.style.maxWidth=t.width,s.parentNode.style.height=t.height,s.appendChild(l)}var $=function(){function e(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this,e),this.defaults={href:"",title:"",type:"",description:"",alt:"",descPosition:"bottom",effect:"",width:"",height:"",content:!1,zoomable:!0,draggable:!0},L(i)&&(this.defaults=l(this.defaults,i))}return n(e,[{key:"sourceType",value:function(e){var t=e;if(null!==(e=e.toLowerCase()).match(/\.(jpeg|jpg|jpe|gif|png|apn|webp|svg)$/))return"image";if(e.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/)||e.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/)||e.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/))return"video";if(e.match(/vimeo\.com\/([0-9]*)/))return"video";if(null!==e.match(/\.(mp4|ogg|webm|mov)$/))return"video";if(null!==e.match(/\.(mp3|wav|wma|aac|ogg)$/))return"audio";if(e.indexOf("#")>-1&&""!==t.split("#").pop().trim())return"inline";return e.indexOf("goajax=true")>-1?"ajax":"external"}},{key:"parseConfig",value:function(e,t){var i=this,n=l({descPosition:t.descPosition},this.defaults);if(L(e)&&!k(e)){O(e,"type")||(O(e,"content")&&e.content?e.type="inline":O(e,"href")&&(e.type=this.sourceType(e.href)));var s=l(n,e);return this.setSize(s,t),s}var r="",a=e.getAttribute("data-glightbox"),h=e.nodeName.toLowerCase();if("a"===h&&(r=e.href),"img"===h&&(r=e.src,n.alt=e.alt),n.href=r,o(n,(function(s,l){O(t,l)&&"width"!==l&&(n[l]=t[l]);var o=e.dataset[l];I(o)||(n[l]=i.sanitizeValue(o))})),n.content&&(n.type="inline"),!n.type&&r&&(n.type=this.sourceType(r)),I(a)){if(!n.title&&"a"==h){var d=e.title;I(d)||""===d||(n.title=d)}if(!n.title&&"img"==h){var c=e.alt;I(c)||""===c||(n.title=c)}}else{var u=[];o(n,(function(e,t){u.push(";\\s?"+t)})),u=u.join("\\s?:|"),""!==a.trim()&&o(n,(function(e,t){var s=a,l=new RegExp("s?"+t+"s?:s?(.*?)("+u+"s?:|$)"),o=s.match(l);if(o&&o.length&&o[1]){var r=o[1].trim().replace(/;\s*$/,"");n[t]=i.sanitizeValue(r)}}))}if(n.description&&"."===n.description.substring(0,1)){var g;try{g=document.querySelector(n.description).innerHTML}catch(e){if(!(e instanceof DOMException))throw e}g&&(n.description=g)}if(!n.description){var v=e.querySelector(".glightbox-desc");v&&(n.description=v.innerHTML)}return this.setSize(n,t,e),this.slideConfig=n,n}},{key:"setSize",value:function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n="video"==e.type?this.checkSize(t.videosWidth):this.checkSize(t.width),s=this.checkSize(t.height);return e.width=O(e,"width")&&""!==e.width?this.checkSize(e.width):n,e.height=O(e,"height")&&""!==e.height?this.checkSize(e.height):s,i&&"image"==e.type&&(e._hasCustomWidth=!!i.dataset.width,e._hasCustomHeight=!!i.dataset.height),e}},{key:"checkSize",value:function(e){return M(e)?"".concat(e,"px"):e}},{key:"sanitizeValue",value:function(e){return"true"!==e&&"false"!==e?e:"true"===e}}]),e}(),U=function(){function e(i,n,s){t(this,e),this.element=i,this.instance=n,this.index=s}return n(e,[{key:"setContent",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(c(t,"loaded"))return!1;var n=this.instance.settings,s=this.slideConfig,l=w();T(n.beforeSlideLoad)&&n.beforeSlideLoad({index:this.index,slide:t,player:!1});var o=s.type,r=s.descPosition,a=t.querySelector(".gslide-media"),d=t.querySelector(".gslide-title"),u=t.querySelector(".gslide-desc"),g=t.querySelector(".gdesc-inner"),v=i,f="gSlideTitle_"+this.index,p="gSlideDesc_"+this.index;if(T(n.afterSlideLoad)&&(v=function(){T(i)&&i(),n.afterSlideLoad({index:e.index,slide:t,player:e.instance.getSlidePlayerInstance(e.index)})}),""==s.title&&""==s.description?g&&g.parentNode.parentNode.removeChild(g.parentNode):(d&&""!==s.title?(d.id=f,d.innerHTML=s.title):d.parentNode.removeChild(d),u&&""!==s.description?(u.id=p,l&&n.moreLength>0?(s.smallDescription=this.slideShortDesc(s.description,n.moreLength,n.moreText),u.innerHTML=s.smallDescription,this.descriptionEvents(u,s)):u.innerHTML=s.description):u.parentNode.removeChild(u),h(a.parentNode,"desc-".concat(r)),h(g.parentNode,"description-".concat(r))),h(a,"gslide-".concat(o)),h(t,"loaded"),"video"!==o){if("external"!==o)return"inline"===o?(G.apply(this.instance,[t,s,this.index,v]),void(n.draggable&&new V({dragEl:t.querySelector(".gslide-inline"),toleranceX:n.dragToleranceX,toleranceY:n.dragToleranceY,slide:t,instance:this.instance}))):void("image"!==o?T(v)&&v():j(t,s,this.index,(function(){var i=t.querySelector("img");n.draggable&&new V({dragEl:i,toleranceX:n.dragToleranceX,toleranceY:n.dragToleranceY,slide:t,instance:e.instance}),s.zoomable&&i.naturalWidth>i.offsetWidth&&(h(i,"zoomable"),new H(i,t,(function(){e.instance.resize()}))),T(v)&&v()})));Z.apply(this,[t,s,this.index,v])}else F.apply(this.instance,[t,s,this.index,v])}},{key:"slideShortDesc",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:50,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=document.createElement("div");n.innerHTML=e;var s=n.innerText,l=i;if((e=s.trim()).length<=t)return e;var o=e.substr(0,t-1);return l?(n=null,o+'... '+i+""):o}},{key:"descriptionEvents",value:function(e,t){var i=this,n=e.querySelector(".desc-more");if(!n)return!1;a("click",{onElement:n,withCallback:function(e,n){e.preventDefault();var s=document.body,l=u(n,".gslide-desc");if(!l)return!1;l.innerHTML=t.description,h(s,"gdesc-open");var o=a("click",{onElement:[s,u(l,".gslide-description")],withCallback:function(e,n){"a"!==e.target.nodeName.toLowerCase()&&(d(s,"gdesc-open"),h(s,"gdesc-closed"),l.innerHTML=t.smallDescription,i.descriptionEvents(l,t),setTimeout((function(){d(s,"gdesc-closed")}),400),o.destroy())}})}})}},{key:"create",value:function(){return m(this.instance.settings.slideHTML)}},{key:"getConfig",value:function(){var e=new $(this.instance.settings.slideExtraAttributes);return this.slideConfig=e.parseConfig(this.element,this.instance.settings),this.slideConfig}}]),e}(),J=w(),K=null!==w()||void 0!==document.createTouch||"ontouchstart"in window||"onmsgesturechange"in window||navigator.msMaxTouchPoints,Q=document.getElementsByTagName("html")[0],ee={selector:".glightbox",elements:null,skin:"clean",theme:"clean",closeButton:!0,startAt:null,autoplayVideos:!0,autofocusVideos:!0,descPosition:"bottom",width:"900px",height:"506px",videosWidth:"960px",beforeSlideChange:null,afterSlideChange:null,beforeSlideLoad:null,afterSlideLoad:null,slideInserted:null,slideRemoved:null,slideExtraAttributes:null,onOpen:null,onClose:null,loop:!1,zoomable:!0,draggable:!0,dragAutoSnap:!1,dragToleranceX:40,dragToleranceY:65,preload:!0,oneSlidePerOpen:!1,touchNavigation:!0,touchFollowAxis:!0,keyboardNavigation:!0,closeOnOutsideClick:!0,plugins:!1,plyr:{css:"https://cdn.plyr.io/3.6.8/plyr.css",js:"https://cdn.plyr.io/3.6.8/plyr.js",config:{ratio:"16:9",fullscreen:{enabled:!0,iosNative:!0},youtube:{noCookie:!0,rel:0,showinfo:0,iv_load_policy:3},vimeo:{byline:!1,portrait:!1,title:!1,transparent:!1}}},openEffect:"zoom",closeEffect:"zoom",slideEffect:"slide",moreText:"See more",moreLength:60,cssEfects:{fade:{in:"fadeIn",out:"fadeOut"},zoom:{in:"zoomIn",out:"zoomOut"},slide:{in:"slideInRight",out:"slideOutLeft"},slideBack:{in:"slideInLeft",out:"slideOutRight"},none:{in:"none",out:"none"}},svg:{close:'',next:' ',prev:''},slideHTML:'
\n
\n
\n
\n
\n
\n
\n

\n
\n
\n
\n
\n
\n
',lightboxHTML:''},te=function(){function e(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this,e),this.customOptions=i,this.settings=l(ee,i),this.effectsClasses=this.getAnimationClasses(),this.videoPlayers={},this.apiEvents=[],this.fullElementsList=!1}return n(e,[{key:"init",value:function(){var e=this,t=this.getSelector();t&&(this.baseEvents=a("click",{onElement:t,withCallback:function(t,i){t.preventDefault(),e.open(i)}})),this.elements=this.getElements()}},{key:"open",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if(0==this.elements.length)return!1;this.activeSlide=null,this.prevActiveSlideIndex=null,this.prevActiveSlide=null;var i=M(t)?t:this.settings.startAt;if(k(e)){var n=e.getAttribute("data-gallery");n&&(this.fullElementsList=this.elements,this.elements=this.getGalleryElements(this.elements,n)),I(i)&&(i=this.getElementIndex(e))<0&&(i=0)}M(i)||(i=0),this.build(),g(this.overlay,"none"==this.settings.openEffect?"none":this.settings.cssEfects.fade.in);var s=document.body,l=window.innerWidth-document.documentElement.clientWidth;if(l>0){var o=document.createElement("style");o.type="text/css",o.className="gcss-styles",o.innerText=".gscrollbar-fixer {margin-right: ".concat(l,"px}"),document.head.appendChild(o),h(s,"gscrollbar-fixer")}h(s,"glightbox-open"),h(Q,"glightbox-open"),J&&(h(document.body,"glightbox-mobile"),this.settings.slideEffect="slide"),this.showSlide(i,!0),1==this.elements.length?(h(this.prevButton,"glightbox-button-hidden"),h(this.nextButton,"glightbox-button-hidden")):(d(this.prevButton,"glightbox-button-hidden"),d(this.nextButton,"glightbox-button-hidden")),this.lightboxOpen=!0,this.trigger("open"),T(this.settings.onOpen)&&this.settings.onOpen(),K&&this.settings.touchNavigation&&B(this),this.settings.keyboardNavigation&&z(this)}},{key:"openAt",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;this.open(null,e)}},{key:"showSlide",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];f(this.loader),this.index=parseInt(t);var n=this.slidesContainer.querySelector(".current");n&&d(n,"current"),this.slideAnimateOut();var s=this.slidesContainer.querySelectorAll(".gslide")[t];if(c(s,"loaded"))this.slideAnimateIn(s,i),p(this.loader);else{f(this.loader);var l=this.elements[t],o={index:this.index,slide:s,slideNode:s,slideConfig:l.slideConfig,slideIndex:this.index,trigger:l.node,player:null};this.trigger("slide_before_load",o),l.instance.setContent(s,(function(){p(e.loader),e.resize(),e.slideAnimateIn(s,i),e.trigger("slide_after_load",o)}))}this.slideDescription=s.querySelector(".gslide-description"),this.slideDescriptionContained=this.slideDescription&&c(this.slideDescription.parentNode,"gslide-media"),this.settings.preload&&(this.preloadSlide(t+1),this.preloadSlide(t-1)),this.updateNavigationClasses(),this.activeSlide=s}},{key:"preloadSlide",value:function(e){var t=this;if(e<0||e>this.elements.length-1)return!1;if(I(this.elements[e]))return!1;var i=this.slidesContainer.querySelectorAll(".gslide")[e];if(c(i,"loaded"))return!1;var n=this.elements[e],s=n.type,l={index:e,slide:i,slideNode:i,slideConfig:n.slideConfig,slideIndex:e,trigger:n.node,player:null};this.trigger("slide_before_load",l),"video"==s||"external"==s?setTimeout((function(){n.instance.setContent(i,(function(){t.trigger("slide_after_load",l)}))}),200):n.instance.setContent(i,(function(){t.trigger("slide_after_load",l)}))}},{key:"prevSlide",value:function(){this.goToSlide(this.index-1)}},{key:"nextSlide",value:function(){this.goToSlide(this.index+1)}},{key:"goToSlide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(this.prevActiveSlide=this.activeSlide,this.prevActiveSlideIndex=this.index,!this.loop()&&(e<0||e>this.elements.length-1))return!1;e<0?e=this.elements.length-1:e>=this.elements.length&&(e=0),this.showSlide(e)}},{key:"insertSlide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;t<0&&(t=this.elements.length);var i=new U(e,this,t),n=i.getConfig(),s=l({},n),o=i.create(),r=this.elements.length-1;s.index=t,s.node=!1,s.instance=i,s.slideConfig=n,this.elements.splice(t,0,s);var a=null,h=null;if(this.slidesContainer){if(t>r)this.slidesContainer.appendChild(o);else{var d=this.slidesContainer.querySelectorAll(".gslide")[t];this.slidesContainer.insertBefore(o,d)}(this.settings.preload&&0==this.index&&0==t||this.index-1==t||this.index+1==t)&&this.preloadSlide(t),0==this.index&&0==t&&(this.index=1),this.updateNavigationClasses(),a=this.slidesContainer.querySelectorAll(".gslide")[t],h=this.getSlidePlayerInstance(t),s.slideNode=a}this.trigger("slide_inserted",{index:t,slide:a,slideNode:a,slideConfig:n,slideIndex:t,trigger:null,player:h}),T(this.settings.slideInserted)&&this.settings.slideInserted({index:t,slide:a,player:h})}},{key:"removeSlide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:-1;if(e<0||e>this.elements.length-1)return!1;var t=this.slidesContainer&&this.slidesContainer.querySelectorAll(".gslide")[e];t&&(this.getActiveSlideIndex()==e&&(e==this.elements.length-1?this.prevSlide():this.nextSlide()),t.parentNode.removeChild(t)),this.elements.splice(e,1),this.trigger("slide_removed",e),T(this.settings.slideRemoved)&&this.settings.slideRemoved(e)}},{key:"slideAnimateIn",value:function(e,t){var i=this,n=e.querySelector(".gslide-media"),s=e.querySelector(".gslide-description"),l={index:this.prevActiveSlideIndex,slide:this.prevActiveSlide,slideNode:this.prevActiveSlide,slideIndex:this.prevActiveSlide,slideConfig:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].slideConfig,trigger:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].node,player:this.getSlidePlayerInstance(this.prevActiveSlideIndex)},o={index:this.index,slide:this.activeSlide,slideNode:this.activeSlide,slideConfig:this.elements[this.index].slideConfig,slideIndex:this.index,trigger:this.elements[this.index].node,player:this.getSlidePlayerInstance(this.index)};if(n.offsetWidth>0&&s&&(p(s),s.style.display=""),d(e,this.effectsClasses),t)g(e,this.settings.cssEfects[this.settings.openEffect].in,(function(){i.settings.autoplayVideos&&i.slidePlayerPlay(e),i.trigger("slide_changed",{prev:l,current:o}),T(i.settings.afterSlideChange)&&i.settings.afterSlideChange.apply(i,[l,o])}));else{var r=this.settings.slideEffect,a="none"!==r?this.settings.cssEfects[r].in:r;this.prevActiveSlideIndex>this.index&&"slide"==this.settings.slideEffect&&(a=this.settings.cssEfects.slideBack.in),g(e,a,(function(){i.settings.autoplayVideos&&i.slidePlayerPlay(e),i.trigger("slide_changed",{prev:l,current:o}),T(i.settings.afterSlideChange)&&i.settings.afterSlideChange.apply(i,[l,o])}))}setTimeout((function(){i.resize(e)}),100),h(e,"current")}},{key:"slideAnimateOut",value:function(){if(!this.prevActiveSlide)return!1;var e=this.prevActiveSlide;d(e,this.effectsClasses),h(e,"prev");var t=this.settings.slideEffect,i="none"!==t?this.settings.cssEfects[t].out:t;this.slidePlayerPause(e),this.trigger("slide_before_change",{prev:{index:this.prevActiveSlideIndex,slide:this.prevActiveSlide,slideNode:this.prevActiveSlide,slideIndex:this.prevActiveSlideIndex,slideConfig:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].slideConfig,trigger:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].node,player:this.getSlidePlayerInstance(this.prevActiveSlideIndex)},current:{index:this.index,slide:this.activeSlide,slideNode:this.activeSlide,slideIndex:this.index,slideConfig:this.elements[this.index].slideConfig,trigger:this.elements[this.index].node,player:this.getSlidePlayerInstance(this.index)}}),T(this.settings.beforeSlideChange)&&this.settings.beforeSlideChange.apply(this,[{index:this.prevActiveSlideIndex,slide:this.prevActiveSlide,player:this.getSlidePlayerInstance(this.prevActiveSlideIndex)},{index:this.index,slide:this.activeSlide,player:this.getSlidePlayerInstance(this.index)}]),this.prevActiveSlideIndex>this.index&&"slide"==this.settings.slideEffect&&(i=this.settings.cssEfects.slideBack.out),g(e,i,(function(){var t=e.querySelector(".ginner-container"),i=e.querySelector(".gslide-media"),n=e.querySelector(".gslide-description");t.style.transform="",i.style.transform="",d(i,"greset"),i.style.opacity="",n&&(n.style.opacity=""),d(e,"prev")}))}},{key:"getAllPlayers",value:function(){return this.videoPlayers}},{key:"getSlidePlayerInstance",value:function(e){var t="gvideo"+e,i=this.getAllPlayers();return!(!O(i,t)||!i[t])&&i[t]}},{key:"stopSlideVideo",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}console.log("stopSlideVideo is deprecated, use slidePlayerPause");var i=this.getSlidePlayerInstance(e);i&&i.playing&&i.pause()}},{key:"slidePlayerPause",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}var i=this.getSlidePlayerInstance(e);i&&i.playing&&i.pause()}},{key:"playSlideVideo",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}console.log("playSlideVideo is deprecated, use slidePlayerPlay");var i=this.getSlidePlayerInstance(e);i&&!i.playing&&i.play()}},{key:"slidePlayerPlay",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}var i=this.getSlidePlayerInstance(e);i&&!i.playing&&(i.play(),this.settings.autofocusVideos&&i.elements.container.focus())}},{key:"setElements",value:function(e){var t=this;this.settings.elements=!1;var i=[];e&&e.length&&o(e,(function(e,n){var s=new U(e,t,n),o=s.getConfig(),r=l({},o);r.slideConfig=o,r.instance=s,r.index=n,i.push(r)})),this.elements=i,this.lightboxOpen&&(this.slidesContainer.innerHTML="",this.elements.length&&(o(this.elements,(function(){var e=m(t.settings.slideHTML);t.slidesContainer.appendChild(e)})),this.showSlide(0,!0)))}},{key:"getElementIndex",value:function(e){var t=!1;return o(this.elements,(function(i,n){if(O(i,"node")&&i.node==e)return t=n,!0})),t}},{key:"getElements",value:function(){var e=this,t=[];this.elements=this.elements?this.elements:[],!I(this.settings.elements)&&E(this.settings.elements)&&this.settings.elements.length&&o(this.settings.elements,(function(i,n){var s=new U(i,e,n),o=s.getConfig(),r=l({},o);r.node=!1,r.index=n,r.instance=s,r.slideConfig=o,t.push(r)}));var i=!1;return this.getSelector()&&(i=document.querySelectorAll(this.getSelector())),i?(o(i,(function(i,n){var s=new U(i,e,n),o=s.getConfig(),r=l({},o);r.node=i,r.index=n,r.instance=s,r.slideConfig=o,r.gallery=i.getAttribute("data-gallery"),t.push(r)})),t):t}},{key:"getGalleryElements",value:function(e,t){return e.filter((function(e){return e.gallery==t}))}},{key:"getSelector",value:function(){return!this.settings.elements&&(this.settings.selector&&"data-"==this.settings.selector.substring(0,5)?"*[".concat(this.settings.selector,"]"):this.settings.selector)}},{key:"getActiveSlide",value:function(){return this.slidesContainer.querySelectorAll(".gslide")[this.index]}},{key:"getActiveSlideIndex",value:function(){return this.index}},{key:"getAnimationClasses",value:function(){var e=[];for(var t in this.settings.cssEfects)if(this.settings.cssEfects.hasOwnProperty(t)){var i=this.settings.cssEfects[t];e.push("g".concat(i.in)),e.push("g".concat(i.out))}return e.join(" ")}},{key:"build",value:function(){var e=this;if(this.built)return!1;var t=document.body.childNodes,i=[];o(t,(function(e){e.parentNode==document.body&&"#"!==e.nodeName.charAt(0)&&e.hasAttribute&&!e.hasAttribute("aria-hidden")&&(i.push(e),e.setAttribute("aria-hidden","true"))}));var n=O(this.settings.svg,"next")?this.settings.svg.next:"",s=O(this.settings.svg,"prev")?this.settings.svg.prev:"",l=O(this.settings.svg,"close")?this.settings.svg.close:"",r=this.settings.lightboxHTML;r=m(r=(r=(r=r.replace(/{nextSVG}/g,n)).replace(/{prevSVG}/g,s)).replace(/{closeSVG}/g,l)),document.body.appendChild(r);var d=document.getElementById("glightbox-body");this.modal=d;var g=d.querySelector(".gclose");this.prevButton=d.querySelector(".gprev"),this.nextButton=d.querySelector(".gnext"),this.overlay=d.querySelector(".goverlay"),this.loader=d.querySelector(".gloader"),this.slidesContainer=document.getElementById("glightbox-slider"),this.bodyHiddenChildElms=i,this.events={},h(this.modal,"glightbox-"+this.settings.skin),this.settings.closeButton&&g&&(this.events.close=a("click",{onElement:g,withCallback:function(t,i){t.preventDefault(),e.close()}})),g&&!this.settings.closeButton&&g.parentNode.removeChild(g),this.nextButton&&(this.events.next=a("click",{onElement:this.nextButton,withCallback:function(t,i){t.preventDefault(),e.nextSlide()}})),this.prevButton&&(this.events.prev=a("click",{onElement:this.prevButton,withCallback:function(t,i){t.preventDefault(),e.prevSlide()}})),this.settings.closeOnOutsideClick&&(this.events.outClose=a("click",{onElement:d,withCallback:function(t,i){e.preventOutsideClick||c(document.body,"glightbox-mobile")||u(t.target,".ginner-container")||u(t.target,".gbtn")||c(t.target,"gnext")||c(t.target,"gprev")||e.close()}})),o(this.elements,(function(t,i){e.slidesContainer.appendChild(t.instance.create()),t.slideNode=e.slidesContainer.querySelectorAll(".gslide")[i]})),K&&h(document.body,"glightbox-touch"),this.events.resize=a("resize",{onElement:window,withCallback:function(){e.resize()}}),this.built=!0}},{key:"resize",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;if((e=e||this.activeSlide)&&!c(e,"zoomed")){var t=y(),i=e.querySelector(".gvideo-wrapper"),n=e.querySelector(".gslide-image"),s=this.slideDescription,l=t.width,o=t.height;if(l<=768?h(document.body,"glightbox-mobile"):d(document.body,"glightbox-mobile"),i||n){var r=!1;if(s&&(c(s,"description-bottom")||c(s,"description-top"))&&!c(s,"gabsolute")&&(r=!0),n)if(l<=768)n.querySelector("img");else if(r){var a=s.offsetHeight,u=n.querySelector("img");u.setAttribute("style","max-height: calc(100vh - ".concat(a,"px)")),s.setAttribute("style","max-width: ".concat(u.offsetWidth,"px;"))}if(i){var g=O(this.settings.plyr.config,"ratio")?this.settings.plyr.config.ratio:"";if(!g){var v=i.clientWidth,f=i.clientHeight,p=v/f;g="".concat(v/p,":").concat(f/p)}var m=g.split(":"),x=this.settings.videosWidth,b=this.settings.videosWidth,S=(b=M(x)||-1!==x.indexOf("px")?parseInt(x):-1!==x.indexOf("vw")?l*parseInt(x)/100:-1!==x.indexOf("vh")?o*parseInt(x)/100:-1!==x.indexOf("%")?l*parseInt(x)/100:parseInt(i.clientWidth))/(parseInt(m[0])/parseInt(m[1]));if(S=Math.floor(S),r&&(o-=s.offsetHeight),b>l||S>o||ob){var w=i.offsetWidth,T=i.offsetHeight,C=o/T,k={width:w*C,height:T*C};i.parentNode.setAttribute("style","max-width: ".concat(k.width,"px")),r&&s.setAttribute("style","max-width: ".concat(k.width,"px;"))}else i.parentNode.style.maxWidth="".concat(x),r&&s.setAttribute("style","max-width: ".concat(x,";"))}}}}},{key:"reload",value:function(){this.init()}},{key:"updateNavigationClasses",value:function(){var e=this.loop();d(this.nextButton,"disabled"),d(this.prevButton,"disabled"),0==this.index&&this.elements.length-1==0?(h(this.prevButton,"disabled"),h(this.nextButton,"disabled")):0!==this.index||e?this.index!==this.elements.length-1||e||h(this.nextButton,"disabled"):h(this.prevButton,"disabled")}},{key:"loop",value:function(){var e=O(this.settings,"loopAtEnd")?this.settings.loopAtEnd:null;return e=O(this.settings,"loop")?this.settings.loop:e,e}},{key:"close",value:function(){var e=this;if(!this.lightboxOpen){if(this.events){for(var t in this.events)this.events.hasOwnProperty(t)&&this.events[t].destroy();this.events=null}return!1}if(this.closing)return!1;this.closing=!0,this.slidePlayerPause(this.activeSlide),this.fullElementsList&&(this.elements=this.fullElementsList),this.bodyHiddenChildElms.length&&o(this.bodyHiddenChildElms,(function(e){e.removeAttribute("aria-hidden")})),h(this.modal,"glightbox-closing"),g(this.overlay,"none"==this.settings.openEffect?"none":this.settings.cssEfects.fade.out),g(this.activeSlide,this.settings.cssEfects[this.settings.closeEffect].out,(function(){if(e.activeSlide=null,e.prevActiveSlideIndex=null,e.prevActiveSlide=null,e.built=!1,e.events){for(var t in e.events)e.events.hasOwnProperty(t)&&e.events[t].destroy();e.events=null}var i=document.body;d(Q,"glightbox-open"),d(i,"glightbox-open touching gdesc-open glightbox-touch glightbox-mobile gscrollbar-fixer"),e.modal.parentNode.removeChild(e.modal),e.trigger("close"),T(e.settings.onClose)&&e.settings.onClose();var n=document.querySelector(".gcss-styles");n&&n.parentNode.removeChild(n),e.lightboxOpen=!1,e.closing=null}))}},{key:"destroy",value:function(){this.close(),this.clearAllEvents(),this.baseEvents&&this.baseEvents.destroy()}},{key:"on",value:function(e,t){var i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!e||!T(t))throw new TypeError("Event name and callback must be defined");this.apiEvents.push({evt:e,once:i,callback:t})}},{key:"once",value:function(e,t){this.on(e,t,!0)}},{key:"trigger",value:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=[];o(this.apiEvents,(function(t,s){var l=t.evt,o=t.once,r=t.callback;l==e&&(r(i),o&&n.push(s))})),n.length&&o(n,(function(e){return t.apiEvents.splice(e,1)}))}},{key:"clearAllEvents",value:function(){this.apiEvents.splice(0,this.apiEvents.length)}},{key:"version",value:function(){return"3.0.9"}}]),e}();return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=new te(e);return t.init(),t}})); \ No newline at end of file diff --git a/media/plg_system_nrframework/js/vendor/inputmask.min.js b/media/plg_system_nrframework/js/vendor/inputmask.min.js new file mode 100644 index 00000000..03dcb61c --- /dev/null +++ b/media/plg_system_nrframework/js/vendor/inputmask.min.js @@ -0,0 +1,8 @@ +/*! + * dist/inputmask.min + * https://github.com/RobinHerbots/Inputmask + * Copyright (c) 2010 - 2023 Robin Herbots + * Licensed under the MIT license + * Version: 5.0.8 + */ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var n in i)("object"==typeof exports?exports:e)[n]=i[n]}}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={8741:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=!("undefined"==typeof window||!window.document||!window.document.createElement);t.default=i},3976:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=i(2839),a={_maxTestPos:500,placeholder:"_",optionalmarker:["[","]"],quantifiermarker:["{","}"],groupmarker:["(",")"],alternatormarker:"|",escapeChar:"\\",mask:null,regex:null,oncomplete:function(){},onincomplete:function(){},oncleared:function(){},repeat:0,greedy:!1,autoUnmask:!1,removeMaskOnSubmit:!1,clearMaskOnLostFocus:!0,insertMode:!0,insertModeVisual:!0,clearIncomplete:!1,alias:null,onKeyDown:function(){},onBeforeMask:null,onBeforePaste:function(e,t){return"function"==typeof t.onBeforeMask?t.onBeforeMask.call(this,e,t):e},onBeforeWrite:null,onUnMask:null,showMaskOnFocus:!0,showMaskOnHover:!0,onKeyValidation:function(){},skipOptionalPartCharacter:" ",numericInput:!1,rightAlign:!1,undoOnEscape:!0,radixPoint:"",_radixDance:!1,groupSeparator:"",keepStatic:null,positionCaretOnTab:!0,tabThrough:!1,supportsInputType:["text","tel","url","password","search"],ignorables:[n.keys.Backspace,n.keys.Tab,n.keys.Pause,n.keys.Escape,n.keys.PageUp,n.keys.PageDown,n.keys.End,n.keys.Home,n.keys.ArrowLeft,n.keys.ArrowUp,n.keys.ArrowRight,n.keys.ArrowDown,n.keys.Insert,n.keys.Delete,n.keys.ContextMenu,n.keys.F1,n.keys.F2,n.keys.F3,n.keys.F4,n.keys.F5,n.keys.F6,n.keys.F7,n.keys.F8,n.keys.F9,n.keys.F10,n.keys.F11,n.keys.F12,n.keys.Process,n.keys.Unidentified,n.keys.Shift,n.keys.Control,n.keys.Alt,n.keys.Tab,n.keys.AltGraph,n.keys.CapsLock],isComplete:null,preValidation:null,postValidation:null,staticDefinitionSymbol:void 0,jitMasking:!1,nullable:!0,inputEventOnly:!1,noValuePatching:!1,positionCaretOnClick:"lvp",casing:null,inputmode:"text",importDataAttributes:!0,shiftPositions:!0,usePrototypeDefinitions:!0,validationEventTimeOut:3e3,substitutes:{}};t.default=a},7392:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={9:{validator:"[0-9\uff10-\uff19]",definitionSymbol:"*"},a:{validator:"[A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",definitionSymbol:"*"},"*":{validator:"[0-9\uff10-\uff19A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]"}}},253:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,i){if(void 0===i)return e.__data?e.__data[t]:null;e.__data=e.__data||{},e.__data[t]=i}},3776:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.Event=void 0,t.off=function(e,t){var i,n;f(this[0])&&e&&(i=this[0].eventRegistry,n=this[0],e.split(" ").forEach((function(e){var a=l(e.split("."),2);(function(e,n){var a,r,o=[];if(e.length>0)if(void 0===t)for(a=0,r=i[e][n].length;a0?n:"global",handler:i[e][n][a]});else o.push({ev:e,namespace:n&&n.length>0?n:"global",handler:t});else if(n.length>0)for(var s in i)for(var l in i[s])if(l===n)if(void 0===t)for(a=0,r=i[s][l].length;ae.length)&&(t=e.length);for(var i=0,n=new Array(t);i0||r.indexOf("Trident/")>0,s=navigator.userAgentData&&navigator.userAgentData.mobile||a.default.navigator&&a.default.navigator.maxTouchPoints||"ontouchstart"in a.default,l=/iphone/i.test(r);t.iphone=l,t.mobile=s,t.ie=o},7184:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return e.replace(i,"\\$1")};var i=new RegExp("(\\"+["/",".","*","+","?","|","(",")","[","]","{","}","\\","$","^"].join("|\\")+")","gim")},6030:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.EventHandlers=void 0;var n=i(8711),a=i(2839),r=i(9845),o=i(7215),s=i(7760),l=i(4713);function c(e,t){var i="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!i){if(Array.isArray(e)||(i=function(e,t){if(!e)return;if("string"==typeof e)return u(e,t);var i=Object.prototype.toString.call(e).slice(8,-1);"Object"===i&&e.constructor&&(i=e.constructor.name);if("Map"===i||"Set"===i)return Array.from(e);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return u(e,t)}(e))||t&&e&&"number"==typeof e.length){i&&(e=i);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,o=!0,s=!1;return{s:function(){i=i.call(e)},n:function(){var e=i.next();return o=e.done,e},e:function(e){s=!0,r=e},f:function(){try{o||null==i.return||i.return()}finally{if(s)throw r}}}}function u(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,n=new Array(t);i=0&&k.end>0&&(e.preventDefault(),n.caret.call(d,m,k.begin,k.end))):(k.begin=n.seekNext.call(d,k.begin,!0),k.end=n.seekNext.call(d,k.begin,!0),k.end=d.length?u.length:d.length,v=f.length>=p.length?f.length:p.length,m="",g=[],y="~";u.length0;){var o=r.pop();["submit","reset"].includes(a)?null!==e.form&&i(e.form).off(a,o):i(e).off(a,o)}delete e.inputmask.events[a]}}}};t.EventRuler=l},219:function(e,t,i){var n=d(i(2394)),a=i(2839),r=d(i(7184)),o=i(8711),s=i(4713);function l(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var i=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=i){var n,a,r,o,s=[],l=!0,c=!1;try{if(r=(i=i.call(e)).next,0===t){if(Object(i)!==i)return;l=!1}else for(;!(l=(n=r.call(i)).done)&&(s.push(n.value),s.length!==t);l=!0);}catch(e){c=!0,a=e}finally{try{if(!l&&null!=i.return&&(o=i.return(),Object(o)!==o))return}finally{if(c)throw a}}return s}}(e,t)||function(e,t){if(!e)return;if("string"==typeof e)return c(e,t);var i=Object.prototype.toString.call(e).slice(8,-1);"Object"===i&&e.constructor&&(i=e.constructor.name);if("Map"===i||"Set"===i)return Array.from(e);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return c(e,t)}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,n=new Array(t);i0&&t--,Date.prototype.setMonth.call(this,t)},"month",function(){return Date.prototype.getMonth.call(this)+1}],mm:["0[1-9]|1[012]",function(e){var t=e?parseInt(e):0;return t>0&&t--,Date.prototype.setMonth.call(this,t)},"month",function(){return M(Date.prototype.getMonth.call(this)+1,2)}],mmm:[""],mmmm:[""],yy:["[0-9]{2}",Date.prototype.setFullYear,"year",function(){return M(Date.prototype.getFullYear.call(this),2)}],yyyy:["[0-9]{4}",Date.prototype.setFullYear,"year",function(){return M(Date.prototype.getFullYear.call(this),4)}],h:["[1-9]|1[0-2]",Date.prototype.setHours,"hours",Date.prototype.getHours],hh:["0[1-9]|1[0-2]",Date.prototype.setHours,"hours",function(){return M(Date.prototype.getHours.call(this),2)}],hx:[function(e){return"[0-9]{".concat(e,"}")},Date.prototype.setHours,"hours",function(e){return Date.prototype.getHours}],H:["1?[0-9]|2[0-3]",Date.prototype.setHours,"hours",Date.prototype.getHours],HH:["0[0-9]|1[0-9]|2[0-3]",Date.prototype.setHours,"hours",function(){return M(Date.prototype.getHours.call(this),2)}],Hx:[function(e){return"[0-9]{".concat(e,"}")},Date.prototype.setHours,"hours",function(e){return function(){return M(Date.prototype.getHours.call(this),e)}}],M:["[1-5]?[0-9]",Date.prototype.setMinutes,"minutes",Date.prototype.getMinutes],MM:["0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]",Date.prototype.setMinutes,"minutes",function(){return M(Date.prototype.getMinutes.call(this),2)}],s:["[1-5]?[0-9]",Date.prototype.setSeconds,"seconds",Date.prototype.getSeconds],ss:["0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]",Date.prototype.setSeconds,"seconds",function(){return M(Date.prototype.getSeconds.call(this),2)}],l:["[0-9]{3}",Date.prototype.setMilliseconds,"milliseconds",function(){return M(Date.prototype.getMilliseconds.call(this),3)},3],L:["[0-9]{2}",Date.prototype.setMilliseconds,"milliseconds",function(){return M(Date.prototype.getMilliseconds.call(this),2)},2],t:["[ap]",k,"ampm",b,1],tt:["[ap]m",k,"ampm",b,2],T:["[AP]",k,"ampm",b,1],TT:["[AP]M",k,"ampm",b,2],Z:[".*",void 0,"Z",function(){var e=this.toString().match(/\((.+)\)/)[1];e.includes(" ")&&(e=(e=e.replace("-"," ").toUpperCase()).split(" ").map((function(e){return l(e,1)[0]})).join(""));return e}],o:[""],S:[""]},y={isoDate:"yyyy-mm-dd",isoTime:"HH:MM:ss",isoDateTime:"yyyy-mm-dd'T'HH:MM:ss",isoUtcDateTime:"UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"};function k(e){var t=this.getHours();e.toLowerCase().includes("p")?this.setHours(t+12):e.toLowerCase().includes("a")&&t>=12&&this.setHours(t-12)}function b(){var e=this.getHours();return(e=e||12)>=12?"PM":"AM"}function x(e){var t=new RegExp("\\d+$").exec(e[0]);if(t&&void 0!==t[0]){var i=g[e[0][0]+"x"].slice("");return i[0]=i[0](t[0]),i[3]=i[3](t[0]),i}if(g[e[0]])return g[e[0]]}function P(e){if(!e.tokenizer){var t=[],i=[];for(var n in g)if(/\.*x$/.test(n)){var a=n[0]+"\\d+";-1===i.indexOf(a)&&i.push(a)}else-1===t.indexOf(n[0])&&t.push(n[0]);e.tokenizer="("+(i.length>0?i.join("|")+"|":"")+t.join("+|")+")+?|.",e.tokenizer=new RegExp(e.tokenizer,"g")}return e.tokenizer}function w(e,t,i){if(!m)return!0;if(void 0===e.rawday||!isFinite(e.rawday)&&new Date(e.date.getFullYear(),isFinite(e.rawmonth)?e.month:e.date.getMonth()+1,0).getDate()>=e.day||"29"==e.day&&(!isFinite(e.rawyear)||void 0===e.rawyear||""===e.rawyear)||new Date(e.date.getFullYear(),isFinite(e.rawmonth)?e.month:e.date.getMonth()+1,0).getDate()>=e.day)return t;if("29"==e.day){var n=E(t.pos,i);if("yyyy"===n.targetMatch[0]&&t.pos-n.targetMatchIndex==2)return t.remove=t.pos+1,t}else if("02"==e.month&&"30"==e.day&&void 0!==t.c)return e.day="03",e.date.setDate(3),e.date.setMonth(1),t.insert=[{pos:t.pos,c:"0"},{pos:t.pos+1,c:t.c}],t.caret=o.seekNext.call(this,t.pos+1),t;return!1}function S(e,t,i,n){var a,o,s="";for(P(i).lastIndex=0;a=P(i).exec(e);){if(void 0===t)if(o=x(a))s+="("+o[0]+")";else switch(a[0]){case"[":s+="(";break;case"]":s+=")?";break;default:s+=(0,r.default)(a[0])}else if(o=x(a))if(!0!==n&&o[3])s+=o[3].call(t.date);else o[2]?s+=t["raw"+o[2]]:s+=a[0];else s+=a[0]}return s}function M(e,t,i){for(e=String(e),t=t||2;e.length=e+1){i=n,n=P(t).exec(t.inputFormat);break}}return{targetMatchIndex:a-r,nextMatch:n,targetMatch:i}}n.default.extendAliases({datetime:{mask:function(e){return e.numericInput=!1,g.S=e.i18n.ordinalSuffix.join("|"),e.inputFormat=y[e.inputFormat]||e.inputFormat,e.displayFormat=y[e.displayFormat]||e.displayFormat||e.inputFormat,e.outputFormat=y[e.outputFormat]||e.outputFormat||e.inputFormat,e.placeholder=""!==e.placeholder?e.placeholder:e.inputFormat.replace(/[[\]]/,""),e.regex=S(e.inputFormat,void 0,e),e.min=_(e.min,e.inputFormat,e),e.max=_(e.max,e.inputFormat,e),null},placeholder:"",inputFormat:"isoDateTime",displayFormat:null,outputFormat:null,min:null,max:null,skipOptionalPartCharacter:"",i18n:{dayNames:["Mon","Tue","Wed","Thu","Fri","Sat","Sun","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],monthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","January","February","March","April","May","June","July","August","September","October","November","December"],ordinalSuffix:["st","nd","rd","th"]},preValidation:function(e,t,i,n,a,r,o,s){if(s)return!0;if(isNaN(i)&&e[t]!==i){var l=E(t,a);if(l.nextMatch&&l.nextMatch[0]===i&&l.targetMatch[0].length>1){var c=g[l.targetMatch[0]][0];if(new RegExp(c).test("0"+e[t-1]))return e[t]=e[t-1],e[t-1]="0",{fuzzy:!0,buffer:e,refreshFromBuffer:{start:t-1,end:t+1},pos:t+1}}}return!0},postValidation:function(e,t,i,n,a,r,o,l){var c,u;if(o)return!0;if(!1===n&&(((c=E(t+1,a)).targetMatch&&c.targetMatchIndex===t&&c.targetMatch[0].length>1&&void 0!==g[c.targetMatch[0]]||(c=E(t+2,a)).targetMatch&&c.targetMatchIndex===t+1&&c.targetMatch[0].length>1&&void 0!==g[c.targetMatch[0]])&&(u=g[c.targetMatch[0]][0]),void 0!==u&&(void 0!==r.validPositions[t+1]&&new RegExp(u).test(i+"0")?(e[t]=i,e[t+1]="0",n={pos:t+2,caret:t}):new RegExp(u).test("0"+i)&&(e[t]="0",e[t+1]=i,n={pos:t+2})),!1===n))return n;if(n.fuzzy&&(e=n.buffer,t=n.pos),(c=E(t,a)).targetMatch&&c.targetMatch[0]&&void 0!==g[c.targetMatch[0]]){var f=g[c.targetMatch[0]];u=f[0];var d=e.slice(c.targetMatchIndex,c.targetMatchIndex+c.targetMatch[0].length);if(!1===new RegExp(u).test(d.join(""))&&2===c.targetMatch[0].length&&r.validPositions[c.targetMatchIndex]&&r.validPositions[c.targetMatchIndex+1]&&(r.validPositions[c.targetMatchIndex+1].input="0"),"year"==f[2])for(var p=s.getMaskTemplate.call(this,!1,1,void 0,!0),h=t+1;h=s.getTime())&&(e.date.setFullYear(v),e.year=n,t.insert=[{pos:t.pos+1,c:o[0]},{pos:t.pos+2,c:o[1]}])}}return t}(y,m,a)),m=function(e,t,i,n,a){if(!t)return t;if(t&&i.min&&!isNaN(i.min.date.getTime())){var r;for(e.reset(),P(i).lastIndex=0;r=P(i).exec(i.inputFormat);){var o;if((o=x(r))&&o[3]){for(var s=o[1],l=e[o[2]],c=i.min[o[2]],u=i.max?i.max[o[2]]:c,f=[],d=!1,p=0;pc[p]):(f[p]=c[p],"year"===o[2]&&l.length-1==p&&c!=u&&(f=(parseInt(f.join(""))+1).toString().split("")),"ampm"===o[2]&&c!=u&&i.min.date.getTime()>e.date.getTime()&&(f[p]=u[p]));s.call(e._date,f.join(""))}}t=i.min.date.getTime()<=e.date.getTime(),e.reInit()}return t&&i.max&&(isNaN(i.max.date.getTime())||(t=i.max.date.getTime()>=e.date.getTime())),t}(y,m=w.call(this,y,m,a),a,r)),void 0!==t&&m&&n.pos!==t?{buffer:S(a.inputFormat,y,a).split(""),refreshFromBuffer:{start:t,end:n.pos},pos:n.caret||n.pos}:m},onKeyDown:function(e,t,i,n){e.ctrlKey&&e.key===a.keys.ArrowRight&&(this.inputmask._valueSet(O(new Date,n)),p(this).trigger("setvalue"))},onUnMask:function(e,t,i){return t?S(i.outputFormat,_(e,i.inputFormat,i),i,!0):t},casing:function(e,t,i,n){return 0==t.nativeDef.indexOf("[ap]")?e.toLowerCase():0==t.nativeDef.indexOf("[AP]")?e.toUpperCase():e},onBeforeMask:function(e,t){return"[object Date]"===Object.prototype.toString.call(e)&&(e=O(e,t)),e},insertMode:!1,insertModeVisual:!1,shiftPositions:!1,keepStatic:!1,inputmode:"numeric",prefillYear:!0}})},3851:function(e,t,i){var n,a=(n=i(2394))&&n.__esModule?n:{default:n},r=i(8711),o=i(4713);a.default.extendDefinitions({A:{validator:"[A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",casing:"upper"},"&":{validator:"[0-9A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",casing:"upper"},"#":{validator:"[0-9A-Fa-f]",casing:"upper"}});var s=new RegExp("25[0-5]|2[0-4][0-9]|[01][0-9][0-9]");function l(e,t,i,n,a){return i-1>-1&&"."!==t.buffer[i-1]?(e=t.buffer[i-1]+e,e=i-2>-1&&"."!==t.buffer[i-2]?t.buffer[i-2]+e:"0"+e):e="00"+e,s.test(e)}a.default.extendAliases({cssunit:{regex:"[+-]?[0-9]+\\.?([0-9]+)?(px|em|rem|ex|%|in|cm|mm|pt|pc)"},url:{regex:"(https?|ftp)://.*",autoUnmask:!1,keepStatic:!1,tabThrough:!0},ip:{mask:"i{1,3}.j{1,3}.k{1,3}.l{1,3}",definitions:{i:{validator:l},j:{validator:l},k:{validator:l},l:{validator:l}},onUnMask:function(e,t,i){return e},inputmode:"decimal",substitutes:{",":"."}},email:{mask:function(e){var t=e.separator,i=e.quantifier,n="*{1,64}[.*{1,64}][.*{1,64}][.*{1,63}]@-{1,63}.-{1,63}[.-{1,63}][.-{1,63}]",a=n;if(t)for(var r=0;r0&&t>0&&(!i.digitsOptional||n)){var a=e.indexOf(i.radixPoint),r=!1;i.negationSymbol.back===e[e.length-1]&&(r=!0,e.length--),-1===a&&(e.push(i.radixPoint),a=e.length-1);for(var o=1;o<=t;o++)isFinite(e[a+o])||(e[a+o]="0")}return r&&e.push(i.negationSymbol.back),e}function f(e,t){var i=0;for(var n in"+"===e&&(i=r.seekNext.call(this,t.validPositions.length-1)),t.tests)if((n=parseInt(n))>=i)for(var a=0,o=t.tests[n].length;a1&&(e.placeholder=e.placeholder.charAt(0)),"radixFocus"===e.positionCaretOnClick&&""===e.placeholder&&(e.positionCaretOnClick="lvp");var t="0",i=e.radixPoint;!0===e.numericInput&&void 0===e.__financeInput?(t="1",e.positionCaretOnClick="radixFocus"===e.positionCaretOnClick?"lvp":e.positionCaretOnClick,e.digitsOptional=!1,isNaN(e.digits)&&(e.digits=2),e._radixDance=!1,i=","===e.radixPoint?"?":"!",""!==e.radixPoint&&void 0===e.definitions[i]&&(e.definitions[i]={},e.definitions[i].validator="["+e.radixPoint+"]",e.definitions[i].placeholder=e.radixPoint,e.definitions[i].static=!0,e.definitions[i].generated=!0)):(e.__financeInput=!1,e.numericInput=!0);var n,r="[+]";if(r+=c(e.prefix,e),""!==e.groupSeparator?(void 0===e.definitions[e.groupSeparator]&&(e.definitions[e.groupSeparator]={},e.definitions[e.groupSeparator].validator="["+e.groupSeparator+"]",e.definitions[e.groupSeparator].placeholder=e.groupSeparator,e.definitions[e.groupSeparator].static=!0,e.definitions[e.groupSeparator].generated=!0),r+=e._mask(e)):r+="9{+}",void 0!==e.digits&&0!==e.digits){var o=e.digits.toString().split(",");isFinite(o[0])&&o[1]&&isFinite(o[1])?r+=i+t+"{"+e.digits+"}":(isNaN(e.digits)||parseInt(e.digits)>0)&&(e.digitsOptional||e.jitMasking?(n=r+i+t+"{0,"+e.digits+"}",e.keepStatic=!0):r+=i+t+"{"+e.digits+"}")}else e.inputmode="numeric";return r+=c(e.suffix,e),r+="[-]",n&&(r=[n+c(e.suffix,e)+"[-]",r]),e.greedy=!1,function(e){void 0===e.parseMinMaxOptions&&(null!==e.min&&(e.min=e.min.toString().replace(new RegExp((0,a.default)(e.groupSeparator),"g"),""),","===e.radixPoint&&(e.min=e.min.replace(e.radixPoint,".")),e.min=isFinite(e.min)?parseFloat(e.min):NaN,isNaN(e.min)&&(e.min=Number.MIN_VALUE)),null!==e.max&&(e.max=e.max.toString().replace(new RegExp((0,a.default)(e.groupSeparator),"g"),""),","===e.radixPoint&&(e.max=e.max.replace(e.radixPoint,".")),e.max=isFinite(e.max)?parseFloat(e.max):NaN,isNaN(e.max)&&(e.max=Number.MAX_VALUE)),e.parseMinMaxOptions="done")}(e),""!==e.radixPoint&&e.substituteRadixPoint&&(e.substitutes["."==e.radixPoint?",":"."]=e.radixPoint),r},_mask:function(e){return"("+e.groupSeparator+"999){+|1}"},digits:"*",digitsOptional:!0,enforceDigitsOnBlur:!1,radixPoint:".",positionCaretOnClick:"radixFocus",_radixDance:!0,groupSeparator:"",allowMinus:!0,negationSymbol:{front:"-",back:""},prefix:"",suffix:"",min:null,max:null,SetMaxOnOverflow:!1,step:1,inputType:"text",unmaskAsNumber:!1,roundingFN:Math.round,inputmode:"decimal",shortcuts:{k:"1000",m:"1000000"},placeholder:"0",greedy:!1,rightAlign:!0,insertMode:!0,autoUnmask:!1,skipOptionalPartCharacter:"",usePrototypeDefinitions:!1,stripLeadingZeroes:!0,substituteRadixPoint:!0,definitions:{0:{validator:p},1:{validator:p,definitionSymbol:"9"},9:{validator:"[0-9\uff10-\uff19\u0660-\u0669\u06f0-\u06f9]",definitionSymbol:"*"},"+":{validator:function(e,t,i,n,a){return a.allowMinus&&("-"===e||e===a.negationSymbol.front)}},"-":{validator:function(e,t,i,n,a){return a.allowMinus&&e===a.negationSymbol.back}}},preValidation:function(e,t,i,n,a,r,o,s){if(!1!==a.__financeInput&&i===a.radixPoint)return!1;var l=e.indexOf(a.radixPoint),c=t;if(t=function(e,t,i,n,a){return a._radixDance&&a.numericInput&&t!==a.negationSymbol.back&&e<=i&&(i>0||t==a.radixPoint)&&(void 0===n.validPositions[e-1]||n.validPositions[e-1].input!==a.negationSymbol.back)&&(e-=1),e}(t,i,l,r,a),"-"===i||i===a.negationSymbol.front){if(!0!==a.allowMinus)return!1;var u=!1,p=d("+",r),h=d("-",r);return-1!==p&&(u=[p,h]),!1!==u?{remove:u,caret:c-a.negationSymbol.back.length}:{insert:[{pos:f.call(this,"+",r),c:a.negationSymbol.front,fromIsValid:!0},{pos:f.call(this,"-",r),c:a.negationSymbol.back,fromIsValid:void 0}],caret:c+a.negationSymbol.back.length}}if(i===a.groupSeparator)return{caret:c};if(s)return!0;if(-1!==l&&!0===a._radixDance&&!1===n&&i===a.radixPoint&&void 0!==a.digits&&(isNaN(a.digits)||parseInt(a.digits)>0)&&l!==t)return{caret:a._radixDance&&t===l-1?l+1:l};if(!1===a.__financeInput)if(n){if(a.digitsOptional)return{rewritePosition:o.end};if(!a.digitsOptional){if(o.begin>l&&o.end<=l)return i===a.radixPoint?{insert:{pos:l+1,c:"0",fromIsValid:!0},rewritePosition:l}:{rewritePosition:l+1};if(o.begin0&&""===this.__valueGet.call(this.el))return{rewritePosition:l};return{rewritePosition:t}},postValidation:function(e,t,i,n,a,r,o){if(!1===n)return n;if(o)return!0;if(null!==a.min||null!==a.max){var s=a.onUnMask(e.slice().reverse().join(""),void 0,l.extend({},a,{unmaskAsNumber:!0}));if(null!==a.min&&sa.min.toString().length||s<0))return!1;if(null!==a.max&&s>a.max)return!!a.SetMaxOnOverflow&&{refreshFromBuffer:!0,buffer:u(a.max.toString().replace(".",a.radixPoint).split(""),a.digits,a).reverse()}}return n},onUnMask:function(e,t,i){if(""===t&&!0===i.nullable)return t;var n=e.replace(i.prefix,"");return n=(n=n.replace(i.suffix,"")).replace(new RegExp((0,a.default)(i.groupSeparator),"g"),""),""!==i.placeholder.charAt(0)&&(n=n.replace(new RegExp(i.placeholder.charAt(0),"g"),"0")),i.unmaskAsNumber?(""!==i.radixPoint&&-1!==n.indexOf(i.radixPoint)&&(n=n.replace(a.default.call(this,i.radixPoint),".")),n=(n=n.replace(new RegExp("^"+(0,a.default)(i.negationSymbol.front)),"-")).replace(new RegExp((0,a.default)(i.negationSymbol.back)+"$"),""),Number(n)):n},isComplete:function(e,t){var i=(t.numericInput?e.slice().reverse():e).join("");return i=(i=(i=(i=(i=i.replace(new RegExp("^"+(0,a.default)(t.negationSymbol.front)),"-")).replace(new RegExp((0,a.default)(t.negationSymbol.back)+"$"),"")).replace(t.prefix,"")).replace(t.suffix,"")).replace(new RegExp((0,a.default)(t.groupSeparator)+"([0-9]{3})","g"),"$1"),","===t.radixPoint&&(i=i.replace((0,a.default)(t.radixPoint),".")),isFinite(i)},onBeforeMask:function(e,t){var i=t.radixPoint||",";isFinite(t.digits)&&(t.digits=parseInt(t.digits)),"number"!=typeof e&&"number"!==t.inputType||""===i||(e=e.toString().replace(".",i));var n="-"===e.charAt(0)||e.charAt(0)===t.negationSymbol.front,r=e.split(i),o=r[0].replace(/[^\-0-9]/g,""),s=r.length>1?r[1].replace(/[^0-9]/g,""):"",l=r.length>1;e=o+(""!==s?i+s:s);var c=0;if(""!==i&&(c=t.digitsOptional?t.digitst.max&&(e=t.max.toString().replace(".",i))}return n&&"-"!==e.charAt(0)&&(e="-"+e),u(e.toString().split(""),c,t,l).join("")},onBeforeWrite:function(e,t,i,n){function r(e,t){if(!1!==n.__financeInput||t){var i=e.indexOf(n.radixPoint);-1!==i&&e.splice(i,1)}if(""!==n.groupSeparator)for(;-1!==(i=e.indexOf(n.groupSeparator));)e.splice(i,1);return e}var o,s;if(n.stripLeadingZeroes&&(s=function(e,t){var i=new RegExp("(^"+(""!==t.negationSymbol.front?(0,a.default)(t.negationSymbol.front)+"?":"")+(0,a.default)(t.prefix)+")(.*)("+(0,a.default)(t.suffix)+(""!=t.negationSymbol.back?(0,a.default)(t.negationSymbol.back)+"?":"")+"$)").exec(e.slice().reverse().join("")),n=i?i[2]:"",r=!1;return n&&(n=n.split(t.radixPoint.charAt(0))[0],r=new RegExp("^[0"+t.groupSeparator+"]*").exec(n)),!(!r||!(r[0].length>1||r[0].length>0&&r[0].length0;d--)delete this.maskset.validPositions[c+d],delete t[c+d];if(e)switch(e.type){case"blur":case"checkval":if(null!==n.min){var p=n.onUnMask(t.slice().reverse().join(""),void 0,l.extend({},n,{unmaskAsNumber:!0}));if(null!==n.min&&p1)return this.inputmask.__valueSet.call(this,parseFloat(this.inputmask.unmaskedvalue())*parseInt(s)),r.trigger("setvalue"),!1}if(e.ctrlKey)switch(e.key){case o.keys.ArrowUp:return this.inputmask.__valueSet.call(this,parseFloat(this.inputmask.unmaskedvalue())+parseInt(n.step)),r.trigger("setvalue"),!1;case o.keys.ArrowDown:return this.inputmask.__valueSet.call(this,parseFloat(this.inputmask.unmaskedvalue())-parseInt(n.step)),r.trigger("setvalue"),!1}if(!e.shiftKey&&(e.key===o.keys.Delete||e.key===o.keys.Backspace||e.key===o.keys.BACKSPACE_SAFARI)&&i.begin!==t.length){if(t[e.key===o.keys.Delete?i.begin-1:i.end]===n.negationSymbol.front)return a=t.slice().reverse(),""!==n.negationSymbol.front&&a.shift(),""!==n.negationSymbol.back&&a.pop(),r.trigger("setvalue",[a.join(""),i.begin]),!1;if(!0===n._radixDance){var f=t.indexOf(n.radixPoint);if(n.digitsOptional){if(0===f)return(a=t.slice().reverse()).pop(),r.trigger("setvalue",[a.join(""),i.begin>=a.length?a.length:i.begin]),!1}else if(-1!==f&&(i.begin=a.length?f+1:i.begin]),!1}}}}},currency:{prefix:"",groupSeparator:",",alias:"numeric",digits:2,digitsOptional:!1},decimal:{alias:"numeric"},integer:{alias:"numeric",inputmode:"numeric",digits:0},percentage:{alias:"numeric",min:0,max:100,suffix:" %",digits:0,allowMinus:!1},indianns:{alias:"numeric",_mask:function(e){return"("+e.groupSeparator+"99){*|1}("+e.groupSeparator+"999){1|1}"},groupSeparator:",",radixPoint:".",placeholder:"0",digits:2,digitsOptional:!1}})},9380:function(e,t,i){var n;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=((n=i(8741))&&n.__esModule?n:{default:n}).default?window:{};t.default=a},7760:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.HandleNativePlaceholder=function(e,t){var i=e?e.inputmask:this;if(s.ie){if(e.inputmask._valueGet()!==t&&(e.placeholder!==t||""===e.placeholder)){var n=r.getBuffer.call(i).slice(),a=e.inputmask._valueGet();if(a!==t){var o=r.getLastValidPosition.call(i);-1===o&&a===r.getBufferTemplate.call(i).join("")?n=[]:-1!==o&&u.call(i,n),d(e,n)}}}else e.placeholder!==t&&(e.placeholder=t,""===e.placeholder&&e.removeAttribute("placeholder"))},t.applyInputValue=c,t.checkVal=f,t.clearOptionalTail=u,t.unmaskedvalue=function(e){var t=e?e.inputmask:this,i=t.opts,n=t.maskset;if(e){if(void 0===e.inputmask)return e.value;e.inputmask&&e.inputmask.refreshValue&&c(e,e.inputmask._valueGet(!0))}for(var a=[],o=n.validPositions,s=0,l=o.length;s0&&" "===i[n-1];)n--;var o=0===n&&!r.isMask.call(c,e)&&(a.getTest.call(c,e).match.nativeDef===t.charAt(0)||!0===a.getTest.call(c,e).match.static&&a.getTest.call(c,e).match.nativeDef==="'"+t.charAt(0)||" "===a.getTest.call(c,e).match.nativeDef&&(a.getTest.call(c,e+1).match.nativeDef===t.charAt(0)||!0===a.getTest.call(c,e+1).match.static&&a.getTest.call(c,e+1).match.nativeDef==="'"+t.charAt(0)));if(!o&&n>0&&!r.isMask.call(c,e,!1,!0)){var s=r.seekNext.call(c,e);c.caretPos.begin0){var x,P,w=r.seekNext.call(c,-1,void 0,!1);if(!o.isComplete.call(c,r.getBuffer.call(c))&&k.length<=w||o.isComplete.call(c,r.getBuffer.call(c))&&k.length>0&&k.length!==w&&0===k[0])for(var S=w;void 0!==(x=k.shift());){var M=new p.Event("_checkval");if((P=u.validPositions[x]).generatedInput=!0,M.key=P.input,(g=l.EventHandlers.keypressEvent.call(c,M,!0,!1,i,S))&&void 0!==g.pos&&g.pos!==x&&u.validPositions[g.pos]&&!0===u.validPositions[g.pos].match.static)k.push(g.pos);else if(!g)break;S++}}t&&d.call(c,e,r.getBuffer.call(c),g?g.forwardPosition:c.caretPos.begin,s||new p.Event("checkval"),s&&("input"===s.type&&c.undoValue!==r.getBuffer.call(c).join("")||"paste"===s.type)),f.skipOptionalPartCharacter=y}function d(e,t,i,a,s){var l=e?e.inputmask:this,c=l.opts,u=l.dependencyLib;if(a&&"function"==typeof c.onBeforeWrite){var f=c.onBeforeWrite.call(l,a,t,i,c);if(f){if(f.refreshFromBuffer){var d=f.refreshFromBuffer;o.refreshFromBuffer.call(l,!0===d?d:d.start,d.end,f.buffer||t),t=r.getBuffer.call(l,!0)}void 0!==i&&(i=void 0!==f.caret?f.caret:i)}}if(void 0!==e&&(e.inputmask._valueSet(t.join("")),void 0===i||void 0!==a&&"blur"===a.type||r.caret.call(l,e,i,void 0,void 0,void 0!==a&&"keydown"===a.type&&(a.key===n.keys.Delete||a.key===n.keys.Backspace)),!0===s)){var p=u(e),h=e.inputmask._valueGet();e.inputmask.skipInputEvent=!0,p.trigger("input"),setTimeout((function(){h===r.getBufferTemplate.call(l).join("")?p.trigger("cleared"):!0===o.isComplete.call(l,t)&&p.trigger("complete")}),0)}}},2394:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=i(157),a=m(i(4963)),r=m(i(9380)),o=i(2391),s=i(4713),l=i(8711),c=i(7215),u=i(7760),f=i(9716),d=m(i(7392)),p=m(i(3976)),h=m(i(8741));function v(e){return v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},v(e)}function m(e){return e&&e.__esModule?e:{default:e}}var g=r.default.document,y="_inputmask_opts";function k(e,t,i){if(h.default){if(!(this instanceof k))return new k(e,t,i);this.dependencyLib=a.default,this.el=void 0,this.events={},this.maskset=void 0,!0!==i&&("[object Object]"===Object.prototype.toString.call(e)?t=e:(t=t||{},e&&(t.alias=e)),this.opts=a.default.extend(!0,{},this.defaults,t),this.noMasksCache=t&&void 0!==t.definitions,this.userOptions=t||{},b(this.opts.alias,t,this.opts)),this.refreshValue=!1,this.undoValue=void 0,this.$el=void 0,this.skipInputEvent=!1,this.validationEvent=!1,this.ignorable=!1,this.maxLength,this.mouseEnter=!1,this.clicked=0,this.originalPlaceholder=void 0,this.isComposing=!1,this.hasAlternator=!1}}function b(e,t,i){var n=k.prototype.aliases[e];return n?(n.alias&&b(n.alias,void 0,i),a.default.extend(!0,i,n),a.default.extend(!0,i,t),!0):(null===i.mask&&(i.mask=e),!1)}k.prototype={dataAttribute:"data-inputmask",defaults:p.default,definitions:d.default,aliases:{},masksCache:{},get isRTL(){return this.opts.isRTL||this.opts.numericInput},mask:function(e){var t=this;return"string"==typeof e&&(e=g.getElementById(e)||g.querySelectorAll(e)),(e=e.nodeName?[e]:Array.isArray(e)?e:[].slice.call(e)).forEach((function(e,i){var s=a.default.extend(!0,{},t.opts);if(function(e,t,i,n){function o(t,a){var o=""===n?t:n+"-"+t;null!==(a=void 0!==a?a:e.getAttribute(o))&&("string"==typeof a&&(0===t.indexOf("on")?a=r.default[a]:"false"===a?a=!1:"true"===a&&(a=!0)),i[t]=a)}if(!0===t.importDataAttributes){var s,l,c,u,f=e.getAttribute(n);if(f&&""!==f&&(f=f.replace(/'/g,'"'),l=JSON.parse("{"+f+"}")),l)for(u in c=void 0,l)if("alias"===u.toLowerCase()){c=l[u];break}for(s in o("alias",c),i.alias&&b(i.alias,i,t),t){if(l)for(u in c=void 0,l)if(u.toLowerCase()===s.toLowerCase()){c=l[u];break}o(s,c)}}a.default.extend(!0,t,i),("rtl"===e.dir||t.rightAlign)&&(e.style.textAlign="right");("rtl"===e.dir||t.numericInput)&&(e.dir="ltr",e.removeAttribute("dir"),t.isRTL=!0);return Object.keys(i).length}(e,s,a.default.extend(!0,{},t.userOptions),t.dataAttribute)){var l=(0,o.generateMaskSet)(s,t.noMasksCache);void 0!==l&&(void 0!==e.inputmask&&(e.inputmask.opts.autoUnmask=!0,e.inputmask.remove()),e.inputmask=new k(void 0,void 0,!0),e.inputmask.opts=s,e.inputmask.noMasksCache=t.noMasksCache,e.inputmask.userOptions=a.default.extend(!0,{},t.userOptions),e.inputmask.el=e,e.inputmask.$el=(0,a.default)(e),e.inputmask.maskset=l,a.default.data(e,y,t.userOptions),n.mask.call(e.inputmask))}})),e&&e[0]&&e[0].inputmask||this},option:function(e,t){return"string"==typeof e?this.opts[e]:"object"===v(e)?(a.default.extend(this.userOptions,e),this.el&&!0!==t&&this.mask(this.el),this):void 0},unmaskedvalue:function(e){if(this.maskset=this.maskset||(0,o.generateMaskSet)(this.opts,this.noMasksCache),void 0===this.el||void 0!==e){var t=("function"==typeof this.opts.onBeforeMask&&this.opts.onBeforeMask.call(this,e,this.opts)||e).split("");u.checkVal.call(this,void 0,!1,!1,t),"function"==typeof this.opts.onBeforeWrite&&this.opts.onBeforeWrite.call(this,void 0,l.getBuffer.call(this),0,this.opts)}return u.unmaskedvalue.call(this,this.el)},remove:function(){if(this.el){a.default.data(this.el,y,null);var e=this.opts.autoUnmask?(0,u.unmaskedvalue)(this.el):this._valueGet(this.opts.autoUnmask);e!==l.getBufferTemplate.call(this).join("")?this._valueSet(e,this.opts.autoUnmask):this._valueSet(""),f.EventRuler.off(this.el),Object.getOwnPropertyDescriptor&&Object.getPrototypeOf?Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this.el),"value")&&this.__valueGet&&Object.defineProperty(this.el,"value",{get:this.__valueGet,set:this.__valueSet,configurable:!0}):g.__lookupGetter__&&this.el.__lookupGetter__("value")&&this.__valueGet&&(this.el.__defineGetter__("value",this.__valueGet),this.el.__defineSetter__("value",this.__valueSet)),this.el.inputmask=void 0}return this.el},getemptymask:function(){return this.maskset=this.maskset||(0,o.generateMaskSet)(this.opts,this.noMasksCache),(this.isRTL?l.getBufferTemplate.call(this).reverse():l.getBufferTemplate.call(this)).join("")},hasMaskedValue:function(){return!this.opts.autoUnmask},isComplete:function(){return this.maskset=this.maskset||(0,o.generateMaskSet)(this.opts,this.noMasksCache),c.isComplete.call(this,l.getBuffer.call(this))},getmetadata:function(){if(this.maskset=this.maskset||(0,o.generateMaskSet)(this.opts,this.noMasksCache),Array.isArray(this.maskset.metadata)){var e=s.getMaskTemplate.call(this,!0,0,!1).join("");return this.maskset.metadata.forEach((function(t){return t.mask!==e||(e=t,!1)})),e}return this.maskset.metadata},isValid:function(e){if(this.maskset=this.maskset||(0,o.generateMaskSet)(this.opts,this.noMasksCache),e){var t=("function"==typeof this.opts.onBeforeMask&&this.opts.onBeforeMask.call(this,e,this.opts)||e).split("");u.checkVal.call(this,void 0,!0,!1,t)}else e=this.isRTL?l.getBuffer.call(this).slice().reverse().join(""):l.getBuffer.call(this).join("");for(var i=l.getBuffer.call(this),n=l.determineLastRequiredPosition.call(this),a=i.length-1;a>n&&!l.isMask.call(this,a);a--);return i.splice(n,a+1-n),c.isComplete.call(this,i)&&e===(this.isRTL?l.getBuffer.call(this).slice().reverse().join(""):l.getBuffer.call(this).join(""))},format:function(e,t){this.maskset=this.maskset||(0,o.generateMaskSet)(this.opts,this.noMasksCache);var i=("function"==typeof this.opts.onBeforeMask&&this.opts.onBeforeMask.call(this,e,this.opts)||e).split("");u.checkVal.call(this,void 0,!0,!1,i);var n=this.isRTL?l.getBuffer.call(this).slice().reverse().join(""):l.getBuffer.call(this).join("");return t?{value:n,metadata:this.getmetadata()}:n},setValue:function(e){this.el&&(0,a.default)(this.el).trigger("setvalue",[e])},analyseMask:o.analyseMask},k.extendDefaults=function(e){a.default.extend(!0,k.prototype.defaults,e)},k.extendDefinitions=function(e){a.default.extend(!0,k.prototype.definitions,e)},k.extendAliases=function(e){a.default.extend(!0,k.prototype.aliases,e)},k.format=function(e,t,i){return k(t).format(e,i)},k.unmask=function(e,t){return k(t).unmaskedvalue(e)},k.isValid=function(e,t){return k(t).isValid(e)},k.remove=function(e){"string"==typeof e&&(e=g.getElementById(e)||g.querySelectorAll(e)),(e=e.nodeName?[e]:e).forEach((function(e){e.inputmask&&e.inputmask.remove()}))},k.setValue=function(e,t){"string"==typeof e&&(e=g.getElementById(e)||g.querySelectorAll(e)),(e=e.nodeName?[e]:e).forEach((function(e){e.inputmask?e.inputmask.setValue(t):(0,a.default)(e).trigger("setvalue",[t])}))},k.dependencyLib=a.default,r.default.Inputmask=k;var x=k;t.default=x},5296:function(e,t,i){function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}var a=h(i(9380)),r=h(i(2394)),o=h(i(8741));function s(e,t){for(var i=0;ie.length)&&(t=e.length);for(var i=0,n=new Array(t);i0){if(y(l=v[v.length-1],o),l.isAlternator){c=v.pop();for(var e=0;e0?(l=v[v.length-1]).matches.push(c):h.matches.push(c)}}else y(h,o)}function b(e){var t=new a.default(!0);return t.openGroup=!1,t.matches=e,t}function x(){if((s=v.pop()).openGroup=!1,void 0!==s)if(v.length>0){if((l=v[v.length-1]).matches.push(s),l.isAlternator){for(var e=(c=v.pop()).matches[0].matches?c.matches[0].matches.length:1,t=0;t0?(l=v[v.length-1]).matches.push(c):h.matches.push(c)}}else h.matches.push(s);else k()}function P(e){var t=e.pop();return t.isQuantifier&&(t=b([e.pop(),t])),t}t&&(i.optionalmarker[0]=void 0,i.optionalmarker[1]=void 0);for(;n=t?d.exec(e):f.exec(e);){if(o=n[0],t){switch(o.charAt(0)){case"?":o="{0,1}";break;case"+":case"*":o="{"+o+"}";break;case"|":if(0===v.length){var w=b(h.matches);w.openGroup=!0,v.push(w),h.matches=[],g=!0}}switch(o){case"\\d":o="[0-9]";break;case"\\p":o+=d.exec(e)[0],o+=d.exec(e)[0]}}if(p)k();else switch(o.charAt(0)){case"$":case"^":t||k();break;case i.escapeChar:p=!0,t&&k();break;case i.optionalmarker[1]:case i.groupmarker[1]:x();break;case i.optionalmarker[0]:v.push(new a.default(!1,!0));break;case i.groupmarker[0]:v.push(new a.default(!0));break;case i.quantifiermarker[0]:var S=new a.default(!1,!1,!0),M=(o=o.replace(/[{}?]/g,"")).split("|"),_=M[0].split(","),O=isNaN(_[0])?_[0]:parseInt(_[0]),E=1===_.length?O:isNaN(_[1])?_[1]:parseInt(_[1]),T=isNaN(M[1])?M[1]:parseInt(M[1]);"*"!==O&&"+"!==O||(O="*"===E?0:1),S.quantifier={min:O,max:E,jit:T};var j=v.length>0?v[v.length-1].matches:h.matches;(n=j.pop()).isGroup||(n=b([n])),j.push(n),j.push(S);break;case i.alternatormarker:if(v.length>0){var A=(l=v[v.length-1]).matches[l.matches.length-1];u=l.openGroup&&(void 0===A.matches||!1===A.isGroup&&!1===A.isAlternator)?v.pop():P(l.matches)}else u=P(h.matches);if(u.isAlternator)v.push(u);else if(u.alternatorGroup?(c=v.pop(),u.alternatorGroup=!1):c=new a.default(!1,!1,!1,!0),c.matches.push(u),v.push(c),u.openGroup){u.openGroup=!1;var D=new a.default(!0);D.alternatorGroup=!0,v.push(D)}break;default:k()}}g&&x();for(;v.length>0;)s=v.pop(),h.matches.push(s);h.matches.length>0&&(!function e(n){n&&n.matches&&n.matches.forEach((function(a,r){var o=n.matches[r+1];(void 0===o||void 0===o.matches||!1===o.isQuantifier)&&a&&a.isGroup&&(a.isGroup=!1,t||(y(a,i.groupmarker[0],0),!0!==a.openGroup&&y(a,i.groupmarker[1]))),e(a)}))}(h),m.push(h));(i.numericInput||i.isRTL)&&function e(t){for(var n in t.matches=t.matches.reverse(),t.matches)if(Object.prototype.hasOwnProperty.call(t.matches,n)){var a=parseInt(n);if(t.matches[n].isQuantifier&&t.matches[a+1]&&t.matches[a+1].isGroup){var r=t.matches[n];t.matches.splice(n,1),t.matches.splice(a+1,0,r)}void 0!==t.matches[n].matches?t.matches[n]=e(t.matches[n]):t.matches[n]=((o=t.matches[n])===i.optionalmarker[0]?o=i.optionalmarker[1]:o===i.optionalmarker[1]?o=i.optionalmarker[0]:o===i.groupmarker[0]?o=i.groupmarker[1]:o===i.groupmarker[1]&&(o=i.groupmarker[0]),o)}var o;return t}(m[0]);return m},t.generateMaskSet=function(e,t){var i;function a(e,t){var i=t.repeat,n=t.groupmarker,a=t.quantifiermarker,r=t.keepStatic;if(i>0||"*"===i||"+"===i){var l="*"===i?0:"+"===i?1:i;e=n[0]+e+n[1]+a[0]+l+","+i+a[1]}if(!0===r){var c=e.match(new RegExp("(.)\\[([^\\]]*)\\]","g"));c&&c.forEach((function(t,i){var n=function(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var i=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=i){var n,a,r,o,s=[],l=!0,c=!1;try{if(r=(i=i.call(e)).next,0===t){if(Object(i)!==i)return;l=!1}else for(;!(l=(n=r.call(i)).done)&&(s.push(n.value),s.length!==t);l=!0);}catch(e){c=!0,a=e}finally{try{if(!l&&null!=i.return&&(o=i.return(),Object(o)!==o))return}finally{if(c)throw a}}return s}}(e,t)||function(e,t){if(!e)return;if("string"==typeof e)return s(e,t);var i=Object.prototype.toString.call(e).slice(8,-1);"Object"===i&&e.constructor&&(i=e.constructor.name);if("Map"===i||"Set"===i)return Array.from(e);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return s(e,t)}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}(t.split("["),2),a=n[0],r=n[1];r=r.replace("]",""),e=e.replace(new RegExp("".concat((0,o.default)(a),"\\[").concat((0,o.default)(r),"\\]")),a.charAt(0)===r.charAt(0)?"(".concat(a,"|").concat(a).concat(r,")"):"".concat(a,"[").concat(r,"]"))}))}return e}function l(e,i,o){var s,l,c=!1;return null!==e&&""!==e||((c=null!==o.regex)?e=(e=o.regex).replace(/^(\^)(.*)(\$)$/,"$2"):(c=!0,e=".*")),1===e.length&&!1===o.greedy&&0!==o.repeat&&(o.placeholder=""),e=a(e,o),l=c?"regex_"+o.regex:o.numericInput?e.split("").reverse().join(""):e,null!==o.keepStatic&&(l="ks_"+o.keepStatic+l),void 0===r.default.prototype.masksCache[l]||!0===t?(s={mask:e,maskToken:r.default.prototype.analyseMask(e,c,o),validPositions:[],_buffer:void 0,buffer:void 0,tests:{},excludes:{},metadata:i,maskLength:void 0,jitOffset:{}},!0!==t&&(r.default.prototype.masksCache[l]=s,s=n.default.extend(!0,{},r.default.prototype.masksCache[l]))):s=n.default.extend(!0,{},r.default.prototype.masksCache[l]),s}"function"==typeof e.mask&&(e.mask=e.mask(e));if(Array.isArray(e.mask)){if(e.mask.length>1){null===e.keepStatic&&(e.keepStatic=!0);var c=e.groupmarker[0];return(e.isRTL?e.mask.reverse():e.mask).forEach((function(t){c.length>1&&(c+=e.alternatormarker),void 0!==t.mask&&"function"!=typeof t.mask?c+=t.mask:c+=t})),l(c+=e.groupmarker[1],e.mask,e)}e.mask=e.mask.pop()}i=e.mask&&void 0!==e.mask.mask&&"function"!=typeof e.mask.mask?l(e.mask.mask,e.mask,e):l(e.mask,e.mask,e);null===e.keepStatic&&(e.keepStatic=!1);return i};var n=l(i(4963)),a=l(i(9695)),r=l(i(2394)),o=l(i(7184));function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,n=new Array(t);i>>0;if(0===n)return!1;for(var a=0|t,r=Math.max(a>=0?a:n-Math.abs(a),0);rthis.length)&&-1!==this.indexOf(e,t)})},8711:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.caret=function(e,t,i,n,a){var r,o=this,s=this.opts;if(void 0===t)return"selectionStart"in e&&"selectionEnd"in e?(t=e.selectionStart,i=e.selectionEnd):window.getSelection?(r=window.getSelection().getRangeAt(0)).commonAncestorContainer.parentNode!==e&&r.commonAncestorContainer!==e||(t=r.startOffset,i=r.endOffset):document.selection&&document.selection.createRange&&(i=(t=0-(r=document.selection.createRange()).duplicate().moveStart("character",-e.inputmask._valueGet().length))+r.text.length),{begin:n?t:c.call(o,t),end:n?i:c.call(o,i)};if(Array.isArray(t)&&(i=o.isRTL?t[0]:t[1],t=o.isRTL?t[1]:t[0]),void 0!==t.begin&&(i=o.isRTL?t.begin:t.end,t=o.isRTL?t.end:t.begin),"number"==typeof t){t=n?t:c.call(o,t),i="number"==typeof(i=n?i:c.call(o,i))?i:t;var l=parseInt(((e.ownerDocument.defaultView||window).getComputedStyle?(e.ownerDocument.defaultView||window).getComputedStyle(e,null):e.currentStyle).fontSize)*i;if(e.scrollLeft=l>e.scrollWidth?l:0,e.inputmask.caretPos={begin:t,end:i},s.insertModeVisual&&!1===s.insertMode&&t===i&&(a||i++),e===(e.inputmask.shadowRoot||e.ownerDocument).activeElement)if("setSelectionRange"in e)e.setSelectionRange(t,i);else if(window.getSelection){if(r=document.createRange(),void 0===e.firstChild||null===e.firstChild){var u=document.createTextNode("");e.appendChild(u)}r.setStart(e.firstChild,tf&&(((i=d[t]).match.optionality||i.match.optionalQuantifier&&i.match.newBlockMarker||v&&(v!==d[t].locator[p.alternation]&&1!=i.match.static||!0===i.match.static&&i.locator[p.alternation]&&a.checkAlternationMatch.call(r,i.locator[p.alternation].toString().split(","),v.toString().split(","))&&""!==n.getTests.call(r,t)[0].def))&&c[t]===n.getPlaceholder.call(r,t,i.match));t--)u--;return e?{l:u,def:d[u]?d[u].match:void 0}:u},t.determineNewCaretPosition=function(e,t,i){var a=this,c=a.maskset,u=a.opts;t&&(a.isRTL?e.end=e.begin:e.begin=e.end);if(e.begin===e.end){switch(i=i||u.positionCaretOnClick){case"none":break;case"select":e={begin:0,end:r.call(a).length};break;case"ignore":e.end=e.begin=l.call(a,o.call(a));break;case"radixFocus":if(a.clicked>1&&0==c.validPositions.length)break;if(function(e){if(""!==u.radixPoint&&0!==u.digits){var t=c.validPositions;if(void 0===t[e]||t[e].input===n.getPlaceholder.call(a,e)){if(e=y||d===h)&&(h=y)}e.end=e.begin=h}}return e}},t.getBuffer=r,t.getBufferTemplate=function(){var e=this.maskset;void 0===e._buffer&&(e._buffer=n.getMaskTemplate.call(this,!1,1),void 0===e.buffer&&(e.buffer=e._buffer.slice()));return e._buffer},t.getLastValidPosition=o,t.isMask=s,t.resetMaskSet=function(e){var t=this.maskset;t.buffer=void 0,!0!==e&&(t.validPositions=[],t.p=0)},t.seekNext=l,t.seekPrevious=function(e,t){var i=this,a=e-1;if(e<=0)return 0;for(;a>0&&(!0===t&&(!0!==n.getTest.call(i,a).match.newBlockMarker||!s.call(i,a,void 0,!0))||!0!==t&&!s.call(i,a,void 0,!0));)a--;return a},t.translatePosition=c;var n=i(4713),a=i(7215);function r(e){var t=this,i=t.maskset;return void 0!==i.buffer&&!0!==e||(i.buffer=n.getMaskTemplate.call(t,!0,o.call(t),!0),void 0===i._buffer&&(i._buffer=i.buffer.slice())),i.buffer}function o(e,t,i){var n=this.maskset,a=-1,r=-1,o=i||n.validPositions;void 0===e&&(e=-1);for(var s=0,l=o.length;s=e&&(r=s));return-1===a||a==e?r:-1==r||e-a-1){if(i){var s=n.getTests.call(a,e);return s.length>1+(""===s[s.length-1].match.def?1:0)}var l=n.determineTestTemplate.call(a,e,n.getTests.call(a,e)),c=n.getPlaceholder.call(a,e,l.match);return l.match.def!==c}return!1}function l(e,t,i){var a=this;void 0===i&&(i=!0);for(var r=e+1;""!==n.getTest.call(a,r).match.def&&(!0===t&&(!0!==n.getTest.call(a,r).match.newBlockMarker||!s.call(a,r,void 0,!0))||!0!==t&&!s.call(a,r,void 0,i));)r++;return r}function c(e){var t=this.opts,i=this.el;return!this.isRTL||"number"!=typeof e||t.greedy&&""===t.placeholder||!i||(e=this._valueGet().length-e)<0&&(e=0),e}},4713:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.determineTestTemplate=c,t.getDecisionTaker=o,t.getMaskTemplate=function(e,t,i,n,a){var r=this,o=this.opts,u=this.maskset,f=o.greedy;a&&o.greedy&&(o.greedy=!1,r.maskset.tests={});t=t||0;var p,h,v,m,g=[],y=0;do{if(!0===e&&u.validPositions[y])h=(v=a&&u.validPositions[y].match.optionality&&void 0===u.validPositions[y+1]&&(!0===u.validPositions[y].generatedInput||u.validPositions[y].input==o.skipOptionalPartCharacter&&y>0)?c.call(r,y,d.call(r,y,p,y-1)):u.validPositions[y]).match,p=v.locator.slice(),g.push(!0===i?v.input:!1===i?h.nativeDef:s.call(r,y,h));else{h=(v=l.call(r,y,p,y-1)).match,p=v.locator.slice();var k=!0!==n&&(!1!==o.jitMasking?o.jitMasking:h.jit);(m=(m&&h.static&&h.def!==o.groupSeparator&&null===h.fn||u.validPositions[y-1]&&h.static&&h.def!==o.groupSeparator&&null===h.fn)&&u.tests[y])||!1===k||void 0===k||"number"==typeof k&&isFinite(k)&&k>y?g.push(!1===i?h.nativeDef:s.call(r,g.length,h)):m=!1}y++}while(!0!==h.static||""!==h.def||t>y);""===g[g.length-1]&&g.pop();!1===i&&void 0!==u.maskLength||(u.maskLength=y-1);return o.greedy=f,g},t.getPlaceholder=s,t.getTest=u,t.getTestTemplate=l,t.getTests=d,t.isSubsetOf=f;var n,a=(n=i(2394))&&n.__esModule?n:{default:n};function r(e,t){var i=(null!=e.alternation?e.mloc[o(e)]:e.locator).join("");if(""!==i)for(;i.length0&&(t=t.split(",")[0]),void 0!==t?t.toString():""}function s(e,t,i){var n=this.opts,a=this.maskset;if(void 0!==(t=t||u.call(this,e).match).placeholder||!0===i)return"function"==typeof t.placeholder?t.placeholder(n):t.placeholder;if(!0===t.static){if(e>-1&&void 0===a.validPositions[e]){var r,o=d.call(this,e),s=[];if(o.length>1+(""===o[o.length-1].match.def?1:0))for(var l=0;l1&&/[0-9a-bA-Z]/.test(s[0].match.def)))return n.placeholder.charAt(e%n.placeholder.length)}return t.def}return n.placeholder.charAt(e%n.placeholder.length)}function l(e,t,i){return this.maskset.validPositions[e]||c.call(this,e,d.call(this,e,t?t.slice():t,i))}function c(e,t){var i=this.opts,n=0,a=function(e,t){var i=0,n=!1;t.forEach((function(e){e.match.optionality&&(0!==i&&i!==e.match.optionality&&(n=!0),(0===i||i>e.match.optionality)&&(i=e.match.optionality))})),i&&(0==e||1==t.length?i=0:n||(i=0));return i}(e,t);e=e>0?e-1:0;var o,s,l,c=r(u.call(this,e));i.greedy&&t.length>1&&""===t[t.length-1].match.def&&(n=1);for(var f=0;f0&&"master"===l.match.newBlockMarker&&(!d.match.optionality||d.match.optionality-a<1||!d.match.newBlockMarker)||l&&!i.greedy&&l.match.optionalQuantifier&&!d.match.optionalQuantifier)&&(s=p,l=d)}return l}function u(e,t){var i=this.maskset;return i.validPositions[e]?i.validPositions[e]:(t||d.call(this,e))[0]}function f(e,t,i){function n(e){for(var t,i=[],n=-1,a=0,r=e.length;at.alternation)for(var a=t.alternation;ae+u._maxTestPos)throw"Inputmask: There is probably an error in your mask definition or in the code. Create an issue on github with an example of the mask you are using. "+l.mask;if(h===e&&void 0===r.matches){if(m.push({match:r,locator:s.reverse(),cd:y,mloc:{}}),!r.optionality||void 0!==p||!(u.definitions&&u.definitions[r.nativeDef]&&u.definitions[r.nativeDef].optional||a.default.prototype.definitions[r.nativeDef]&&a.default.prototype.definitions[r.nativeDef].optional))return!0;g=!0,h=e}else if(void 0!==r.matches){if(r.isGroup&&p!==r)return function(){if(r=c(t.matches[t.matches.indexOf(r)+1],s,p))return!0}();if(r.isOptional)return function(){var t=r,a=m.length;if(r=k(r,i,s,p),m.length>0){if(m.forEach((function(e,t){t>=a&&(e.match.optionality=e.match.optionality?e.match.optionality+1:1)})),n=m[m.length-1].match,void 0!==p||!v(n,t))return r;g=!0,h=e}}();if(r.isAlternator)return function(){o.hasAlternator=!0;var n,a,v,y=r,k=[],b=m.slice(),S=s.length,M=!1,_=i.length>0?i.shift():-1;if(-1===_||"string"==typeof _){var O,E=h,T=i.slice(),j=[];if("string"==typeof _)j=_.split(",");else for(O=0;O=u.keepStatic)&&(j=j.slice(0,1));for(var R=0;Ry.matches[0].matches.length)break;n=m.slice(),h=E,m=[];for(var F=0;F0,r=k.length>0,i=T.slice()}else r=c(y.matches[_]||t.matches[_],[_].concat(s),p);if(r)return!0}();if(r.isQuantifier&&p!==t.matches[t.matches.indexOf(r)-1])return function(){for(var a=r,o=!1,f=i.length>0?i.shift():0;f<(isNaN(a.quantifier.max)?f+1:a.quantifier.max)&&h<=e;f++){var d=t.matches[t.matches.indexOf(a)-1];if(r=c(d,[f].concat(s),d)){if(m.forEach((function(t,i){(n=b(d,t.match)?t.match:m[m.length-1].match).optionalQuantifier=f>=a.quantifier.min,n.jit=(f+1)*(d.matches.indexOf(n)+1)>a.quantifier.jit,n.optionalQuantifier&&v(n,d)&&(g=!0,h=e,u.greedy&&null==l.validPositions[e-1]&&f>a.quantifier.min&&-1!=["*","+"].indexOf(a.quantifier.max)&&(m.pop(),y=void 0),o=!0,r=!1),!o&&n.jit&&(l.jitOffset[e]=d.matches.length-d.matches.indexOf(n))})),o)break;return!0}}}();if(r=k(r,i,s,p))return!0}else h++}for(var p=i.length>0?i.shift():0;pe)break}}function b(e,t){var i=-1!=e.matches.indexOf(t);return i||e.matches.forEach((function(e,n){void 0===e.matches||i||(i=b(e,t))})),i}if(e>-1){if(void 0===t){for(var x,P=e-1;void 0===(x=l.validPositions[P]||l.tests[P])&&P>-1;)P--;void 0!==x&&P>-1&&(v=function(e,t){var i,n=[];return Array.isArray(t)||(t=[t]),t.length>0&&(void 0===t[0].alternation||!0===u.keepStatic?0===(n=c.call(o,e,t.slice()).locator.slice()).length&&(n=t[0].locator.slice()):t.forEach((function(e){""!==e.def&&(0===n.length?(i=e.alternation,n=e.locator.slice()):e.locator[i]&&-1===n[i].toString().indexOf(e.locator[i])&&(n[i]+=","+e.locator[i]))}))),n}(P,x),y=v.join(""),h=P)}if(l.tests[e]&&l.tests[e][0].cd===y)return l.tests[e];for(var w=v.shift();we)break}}return(0===m.length||g)&&m.push({match:{fn:null,static:!0,optionality:!1,casing:null,def:"",placeholder:""},locator:[],mloc:{},cd:y}),void 0!==t&&l.tests[e]?r=s.extend(!0,[],m):(l.tests[e]=s.extend(!0,[],m),r=l.tests[e]),m.forEach((function(e){e.match.optionality=e.match.defOptionality||!1})),r}},7215:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.alternate=s,t.checkAlternationMatch=function(e,t,i){for(var n,a=this.opts.greedy?t:t.slice(0,1),r=!1,o=void 0!==i?i.split(","):[],s=0;s=r.getBuffer.call(c).length&&h>=i.end&&(i.end=h+1);t===a.keys.Backspace?i.end-i.begin<1&&(i.begin=r.seekPrevious.call(c,i.begin)):t===a.keys.Delete&&i.begin===i.end&&(i.end=r.isMask.call(c,i.end,!0,!0)?i.end+1:r.seekNext.call(c,i.end)+1);if(!1!==(p=v.call(c,i))){if(!0!==o&&!1!==f.keepStatic||null!==f.regex&&-1!==n.getTest.call(c,i.begin).match.def.indexOf("|")){var m=s.call(c,!0);if(m){var g=void 0!==m.caret?m.caret:m.pos?r.seekNext.call(c,m.pos.begin?m.pos.begin:m.pos):r.getLastValidPosition.call(c,-1,!0);(t!==a.keys.Delete||i.begin>g)&&i.begin}}!0!==o&&(u.p=t===a.keys.Delete?i.begin+p:i.begin,u.p=r.determineNewCaretPosition.call(c,{begin:u.p,end:u.p},!1,!1===f.insertMode&&t===a.keys.Backspace?"none":void 0).begin)}},t.isComplete=c,t.isSelection=u,t.isValid=f,t.refreshFromBuffer=p,t.revalidateMask=v;var n=i(4713),a=i(2839),r=i(8711),o=i(6030);function s(e,t,i,a,o,l){var c,u,d,p,h,v,m,g,y,k,b,x=this,P=this.dependencyLib,w=this.opts,S=x.maskset,M=P.extend(!0,[],S.validPositions),_=P.extend(!0,{},S.tests),O=!1,E=!1,T=void 0!==o?o:r.getLastValidPosition.call(x);if(l&&(k=l.begin,b=l.end,l.begin>l.end&&(k=l.end,b=l.begin)),-1===T&&void 0===o)c=0,u=(p=n.getTest.call(x,c)).alternation;else for(;T>=0;T--)if((d=S.validPositions[T])&&void 0!==d.alternation){if(T<=(e||0)&&p&&p.locator[d.alternation]!==d.locator[d.alternation])break;c=T,u=S.validPositions[c].alternation,p=d}if(void 0!==u){m=parseInt(c),S.excludes[m]=S.excludes[m]||[],!0!==e&&S.excludes[m].push((0,n.getDecisionTaker)(p)+":"+p.alternation);var j=[],A=-1;for(h=m;h=b)&&j.push(v.input),delete S.validPositions[h];for(-1===A&&void 0!==t&&(j.push(t),A=j.length-1);void 0!==S.excludes[m]&&S.excludes[m].length<10;){for(S.tests={},r.resetMaskSet.call(x,!0),O=!0,h=0;ht:e.end-e.begin>t}function f(e,t,i,a,o,d,m){var g=this,y=this.dependencyLib,k=this.opts,b=g.maskset;i=!0===i;var x=e;function P(e){if(void 0!==e){if(void 0!==e.remove&&(Array.isArray(e.remove)||(e.remove=[e.remove]),e.remove.sort((function(e,t){return g.isRTL?e.pos-t.pos:t.pos-e.pos})).forEach((function(e){v.call(g,{begin:e,end:e+1})})),e.remove=void 0),void 0!==e.insert&&(Array.isArray(e.insert)||(e.insert=[e.insert]),e.insert.sort((function(e,t){return g.isRTL?t.pos-e.pos:e.pos-t.pos})).forEach((function(e){""!==e.c&&f.call(g,e.pos,e.c,void 0===e.strict||e.strict,void 0!==e.fromIsValid?e.fromIsValid:a)})),e.insert=void 0),e.refreshFromBuffer&&e.buffer){var t=e.refreshFromBuffer;p.call(g,!0===t?t:t.start,t.end,e.buffer),e.refreshFromBuffer=void 0}void 0!==e.rewritePosition&&(x=e.rewritePosition,e=!0)}return e}function w(t,i,o){var s=!1;return n.getTests.call(g,t).every((function(c,f){var d=c.match;if(r.getBuffer.call(g,!0),!1!==(s=(!d.jit||void 0!==b.validPositions[r.seekPrevious.call(g,t)])&&(null!=d.fn?d.fn.test(i,b,t,o,k,u.call(g,e)):(i===d.def||i===k.skipOptionalPartCharacter)&&""!==d.def&&{c:n.getPlaceholder.call(g,t,d,!0)||d.def,pos:t}))){var p=void 0!==s.c?s.c:i,h=t;return p=p===k.skipOptionalPartCharacter&&!0===d.static?n.getPlaceholder.call(g,t,d,!0)||d.def:p,!0!==(s=P(s))&&void 0!==s.pos&&s.pos!==t&&(h=s.pos),!0!==s&&void 0===s.pos&&void 0===s.c?!1:(!1===v.call(g,e,y.extend({},c,{input:l.call(g,p,d,h)}),a,h)&&(s=!1),!1)}return!0})),s}void 0!==e.begin&&(x=g.isRTL?e.end:e.begin);var S=!0,M=y.extend(!0,{},b.validPositions);if(!1===k.keepStatic&&void 0!==b.excludes[x]&&!0!==o&&!0!==a)for(var _=x;_<(g.isRTL?e.begin:e.end);_++)void 0!==b.excludes[_]&&(b.excludes[_]=void 0,delete b.tests[_]);if("function"==typeof k.preValidation&&!0!==a&&!0!==d&&(S=P(S=k.preValidation.call(g,r.getBuffer.call(g),x,t,u.call(g,e),k,b,e,i||o))),!0===S){if(S=w(x,t,i),(!i||!0===a)&&!1===S&&!0!==d){var O=b.validPositions[x];if(!O||!0!==O.match.static||O.match.def!==t&&t!==k.skipOptionalPartCharacter){if(k.insertMode||void 0===b.validPositions[r.seekNext.call(g,x)]||e.end>x){var E=!1;if(b.jitOffset[x]&&void 0===b.validPositions[r.seekNext.call(g,x)]&&!1!==(S=f.call(g,x+b.jitOffset[x],t,!0,!0))&&(!0!==o&&(S.caret=x),E=!0),e.end>x&&(b.validPositions[x]=void 0),!E&&!r.isMask.call(g,x,k.keepStatic&&0===x))for(var T=x+1,j=r.seekNext.call(g,x,!1,0!==x);T<=j;T++)if(!1!==(S=w(T,t,i))){S=h.call(g,x,void 0!==S.pos?S.pos:T)||S,x=T;break}}}else S={caret:r.seekNext.call(g,x)}}g.hasAlternator&&!0!==o&&!i&&(!1===S&&k.keepStatic&&(c.call(g,r.getBuffer.call(g))||0===x)?S=s.call(g,x,t,i,a,void 0,e):(u.call(g,e)&&b.tests[x]&&b.tests[x].length>1&&k.keepStatic||1==S&&!0!==k.numericInput&&b.tests[x]&&b.tests[x].length>1&&r.getLastValidPosition.call(g,void 0,!0)>x)&&(S=s.call(g,!0))),!0===S&&(S={pos:x})}if("function"==typeof k.postValidation&&!0!==a&&!0!==d){var A=k.postValidation.call(g,r.getBuffer.call(g,!0),void 0!==e.begin?g.isRTL?e.end:e.begin:e,t,S,k,b,i,m);void 0!==A&&(S=!0===A?S:A)}S&&void 0===S.pos&&(S.pos=x),!1===S||!0===d?(r.resetMaskSet.call(g,!0),b.validPositions=y.extend(!0,[],M)):h.call(g,void 0,x,!0);var D=P(S);void 0!==g.maxLength&&(r.getBuffer.call(g).length>g.maxLength&&!a&&(r.resetMaskSet.call(g,!0),b.validPositions=y.extend(!0,[],M),D=!1));return D}function d(e,t,i){for(var a=this.maskset,r=!1,o=n.getTests.call(this,e),s=0;s0&&!o.validPositions[e];e--);for(var l=e;le+1?t[e+1]&&!0===t[e+1].match.static&&t[e+1]:t[e+1];return a&&r}return!1}var p=0,h=void 0!==e.begin?e.begin:e,v=void 0!==e.end?e.end:e,m=!0;if(e.begin>e.end&&(h=e.end,v=e.begin),a=void 0!==a?a:h,void 0===i&&(h!==v||l.insertMode&&void 0!==s.validPositions[a]||void 0===t||t.match.optionalQuantifier||t.match.optionality)){var g,y=c.extend(!0,{},s.validPositions),k=r.getLastValidPosition.call(o,void 0,!0);for(s.p=h,g=k;g>=h;g--)delete s.validPositions[g],void 0===t&&delete s.tests[g+1];var b,x,P=a,w=P;for(t&&(s.validPositions[a]=c.extend(!0,{},t),w++,P++),g=t?v:v-1;g<=k;g++){if(void 0!==(b=y[g])&&!0!==b.generatedInput&&(g>=v||g>=h&&u(g,y,{begin:h,end:v}))){for(;""!==n.getTest.call(o,w).match.def;){if(!1!==(x=d.call(o,w,b,l))||"+"===b.match.def){"+"===b.match.def&&r.getBuffer.call(o,!0);var S=f.call(o,w,b.input,"+"!==b.match.def,!0);if(m=!1!==S,P=(S.pos||w)+1,!m&&x)break}else m=!1;if(m){void 0===t&&b.match.static&&g===e.begin&&p++;break}if(!m&&r.getBuffer.call(o),w>s.maskLength)break;w++}""==n.getTest.call(o,w).match.def&&(m=!1),w=P}if(!m)break}if(!m)return s.validPositions=c.extend(!0,[],y),r.resetMaskSet.call(o,!0),!1}else t&&n.getTest.call(o,a).match.cd===t.match.cd&&(s.validPositions[a]=c.extend(!0,{},t));return r.resetMaskSet.call(o,!0),p}}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}var n={};return function(){var e,t=n;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,i(7149),i(3194),i(9302),i(4013),i(3851),i(219),i(207),i(5296);var a=((e=i(2394))&&e.__esModule?e:{default:e}).default;t.default=a}(),n}()})); \ No newline at end of file diff --git a/media/plg_system_nrframework/js/vendor/jquery-clockpicker.min.js b/media/plg_system_nrframework/js/vendor/jquery-clockpicker.min.js new file mode 100644 index 00000000..c8006a38 --- /dev/null +++ b/media/plg_system_nrframework/js/vendor/jquery-clockpicker.min.js @@ -0,0 +1,6 @@ +/*! + * ClockPicker v0.0.7 (http://weareoutman.github.io/clockpicker/) + * Copyright 2014 Wang Shenwei. + * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE) + */ +!function(){function t(t){return document.createElementNS(p,t)}function i(t){return(10>t?"0":"")+t}function e(t){var i=++m+"";return t?t+i:i}function s(s,r){function p(t,i){var e=u.offset(),s=/^touch/.test(t.type),o=e.left+b,n=e.top+b,p=(s?t.originalEvent.touches[0]:t).pageX-o,h=(s?t.originalEvent.touches[0]:t).pageY-n,k=Math.sqrt(p*p+h*h),v=!1;if(!i||!(g-y>k||k>g+y)){t.preventDefault();var m=setTimeout(function(){c.addClass("clockpicker-moving")},200);l&&u.append(x.canvas),x.setHand(p,h,!i,!0),a.off(d).on(d,function(t){t.preventDefault();var i=/^touch/.test(t.type),e=(i?t.originalEvent.touches[0]:t).pageX-o,s=(i?t.originalEvent.touches[0]:t).pageY-n;(v||e!==p||s!==h)&&(v=!0,x.setHand(e,s,!1,!0))}),a.off(f).on(f,function(t){a.off(f),t.preventDefault();var e=/^touch/.test(t.type),s=(e?t.originalEvent.changedTouches[0]:t).pageX-o,l=(e?t.originalEvent.changedTouches[0]:t).pageY-n;(i||v)&&s===p&&l===h&&x.setHand(s,l),"hours"===x.currentView?x.toggleView("minutes",A/2):r.autoclose&&(x.minutesView.addClass("clockpicker-dial-out"),setTimeout(function(){x.done()},A/2)),u.prepend(j),clearTimeout(m),c.removeClass("clockpicker-moving"),a.off(d)})}}var h=n(V),u=h.find(".clockpicker-plate"),v=h.find(".clockpicker-hours"),m=h.find(".clockpicker-minutes"),T=h.find(".clockpicker-am-pm-block"),C="INPUT"===s.prop("tagName"),H=C?s:s.find("input"),P=s.find(".input-group-addon"),x=this;if(this.id=e("cp"),this.element=s,this.options=r,this.isAppended=!1,this.isShown=!1,this.currentView="hours",this.isInput=C,this.input=H,this.addon=P,this.popover=h,this.plate=u,this.hoursView=v,this.minutesView=m,this.amPmBlock=T,this.spanHours=h.find(".clockpicker-span-hours"),this.spanMinutes=h.find(".clockpicker-span-minutes"),this.spanAmPm=h.find(".clockpicker-span-am-pm"),this.amOrPm="PM",r.twelvehour){{var S=['
','",'","
"].join("");n(S)}n('').on("click",function(){x.amOrPm="AM",n(".clockpicker-span-am-pm").empty().append("AM")}).appendTo(this.amPmBlock),n('').on("click",function(){x.amOrPm="PM",n(".clockpicker-span-am-pm").empty().append("PM")}).appendTo(this.amPmBlock)}r.autoclose||n('").click(n.proxy(this.done,this)).appendTo(h),"top"!==r.placement&&"bottom"!==r.placement||"top"!==r.align&&"bottom"!==r.align||(r.align="left"),"left"!==r.placement&&"right"!==r.placement||"left"!==r.align&&"right"!==r.align||(r.align="top"),h.addClass(r.placement),h.addClass("clockpicker-align-"+r.align),this.spanHours.click(n.proxy(this.toggleView,this,"hours")),this.spanMinutes.click(n.proxy(this.toggleView,this,"minutes")),H.on("focus.clockpicker click.clockpicker",n.proxy(this.show,this)),P.on("click.clockpicker",n.proxy(this.toggle,this));var E,D,I,B,z=n('
');if(r.twelvehour)for(E=1;13>E;E+=1)D=z.clone(),I=E/6*Math.PI,B=g,D.css("font-size","120%"),D.css({left:b+Math.sin(I)*B-y,top:b-Math.cos(I)*B-y}),D.html(0===E?"00":E),v.append(D),D.on(k,p);else for(E=0;24>E;E+=1){D=z.clone(),I=E/6*Math.PI;var O=E>0&&13>E;B=O?w:g,D.css({left:b+Math.sin(I)*B-y,top:b-Math.cos(I)*B-y}),O&&D.css("font-size","120%"),D.html(0===E?"00":E),v.append(D),D.on(k,p)}for(E=0;60>E;E+=5)D=z.clone(),I=E/30*Math.PI,D.css({left:b+Math.sin(I)*g-y,top:b-Math.cos(I)*g-y}),D.css("font-size","120%"),D.html(i(E)),m.append(D),D.on(k,p);if(u.on(k,function(t){0===n(t.target).closest(".clockpicker-tick").length&&p(t,!0)}),l){var j=h.find(".clockpicker-canvas"),L=t("svg");L.setAttribute("class","clockpicker-svg"),L.setAttribute("width",M),L.setAttribute("height",M);var U=t("g");U.setAttribute("transform","translate("+b+","+b+")");var W=t("circle");W.setAttribute("class","clockpicker-canvas-bearing"),W.setAttribute("cx",0),W.setAttribute("cy",0),W.setAttribute("r",2);var N=t("line");N.setAttribute("x1",0),N.setAttribute("y1",0);var X=t("circle");X.setAttribute("class","clockpicker-canvas-bg"),X.setAttribute("r",y);var Y=t("circle");Y.setAttribute("class","clockpicker-canvas-fg"),Y.setAttribute("r",3.5),U.appendChild(N),U.appendChild(X),U.appendChild(Y),U.appendChild(W),L.appendChild(U),j.append(L),this.hand=N,this.bg=X,this.fg=Y,this.bearing=W,this.g=U,this.canvas=j}o(this.options.init)}function o(t){t&&"function"==typeof t&&t()}var c,n=window.jQuery,r=n(window),a=n(document),p="http://www.w3.org/2000/svg",l="SVGAngle"in window&&function(){var t,i=document.createElement("div");return i.innerHTML="",t=(i.firstChild&&i.firstChild.namespaceURI)==p,i.innerHTML="",t}(),h=function(){var t=document.createElement("div").style;return"transition"in t||"WebkitTransition"in t||"MozTransition"in t||"msTransition"in t||"OTransition"in t}(),u="ontouchstart"in window,k="mousedown"+(u?" touchstart":""),d="mousemove.clockpicker"+(u?" touchmove.clockpicker":""),f="mouseup.clockpicker"+(u?" touchend.clockpicker":""),v=navigator.vibrate?"vibrate":navigator.webkitVibrate?"webkitVibrate":null,m=0,b=100,g=80,w=54,y=13,M=2*b,A=h?350:1,V=['
','
','
',''," : ",'','',"
",'
','
','
','
','
',"
",'',"","
","
"].join("");s.DEFAULTS={"default":"",fromnow:0,placement:"bottom",align:"left",donetext:"完成",autoclose:!1,twelvehour:!1,vibrate:!0},s.prototype.toggle=function(){this[this.isShown?"hide":"show"]()},s.prototype.locate=function(){var t=this.element,i=this.popover,e=t.offset(),s=t.outerWidth(),o=t.outerHeight(),c=this.options.placement,n=this.options.align,r={};switch(i.show(),c){case"bottom":r.top=e.top+o;break;case"right":r.left=e.left+s;break;case"top":r.top=e.top-i.outerHeight();break;case"left":r.left=e.left-i.outerWidth()}switch(n){case"left":r.left=e.left;break;case"right":r.left=e.left+s-i.outerWidth();break;case"top":r.top=e.top;break;case"bottom":r.top=e.top+o-i.outerHeight()}i.css(r)},s.prototype.show=function(){if(!this.isShown){o(this.options.beforeShow);var t=this;this.isAppended||(c=n(document.body).append(this.popover),r.on("resize.clockpicker"+this.id,function(){t.isShown&&t.locate()}),this.isAppended=!0);var e=((this.input.prop("value")||this.options["default"]||"")+"").split(":");if("now"===e[0]){var s=new Date(+new Date+this.options.fromnow);e=[s.getHours(),s.getMinutes()]}this.hours=+e[0]||0,this.minutes=+e[1]||0,this.spanHours.html(i(this.hours)),this.spanMinutes.html(i(this.minutes)),this.toggleView("hours"),this.locate(),this.isShown=!0,a.on("click.clockpicker."+this.id+" focusin.clockpicker."+this.id,function(i){var e=n(i.target);0===e.closest(t.popover).length&&0===e.closest(t.addon).length&&0===e.closest(t.input).length&&t.hide()}),a.on("keyup.clockpicker."+this.id,function(i){27===i.keyCode&&t.hide()}),o(this.options.afterShow)}},s.prototype.hide=function(){o(this.options.beforeHide),this.isShown=!1,a.off("click.clockpicker."+this.id+" focusin.clockpicker."+this.id),a.off("keyup.clockpicker."+this.id),this.popover.hide(),o(this.options.afterHide)},s.prototype.toggleView=function(t,i){var e=!1;"minutes"===t&&"visible"===n(this.hoursView).css("visibility")&&(o(this.options.beforeHourSelect),e=!0);var s="hours"===t,c=s?this.hoursView:this.minutesView,r=s?this.minutesView:this.hoursView;this.currentView=t,this.spanHours.toggleClass("text-primary",s),this.spanMinutes.toggleClass("text-primary",!s),r.addClass("clockpicker-dial-out"),c.css("visibility","visible").removeClass("clockpicker-dial-out"),this.resetClock(i),clearTimeout(this.toggleViewTimer),this.toggleViewTimer=setTimeout(function(){r.css("visibility","hidden")},A),e&&o(this.options.afterHourSelect)},s.prototype.resetClock=function(t){var i=this.currentView,e=this[i],s="hours"===i,o=Math.PI/(s?6:30),c=e*o,n=s&&e>0&&13>e?w:g,r=Math.sin(c)*n,a=-Math.cos(c)*n,p=this;l&&t?(p.canvas.addClass("clockpicker-canvas-out"),setTimeout(function(){p.canvas.removeClass("clockpicker-canvas-out"),p.setHand(r,a)},t)):this.setHand(r,a)},s.prototype.setHand=function(t,e,s,o){var c,r=Math.atan2(t,-e),a="hours"===this.currentView,p=Math.PI/(a||s?6:30),h=Math.sqrt(t*t+e*e),u=this.options,k=a&&(g+w)/2>h,d=k?w:g;if(u.twelvehour&&(d=g),0>r&&(r=2*Math.PI+r),c=Math.round(r/p),r=c*p,u.twelvehour?a?0===c&&(c=12):(s&&(c*=5),60===c&&(c=0)):a?(12===c&&(c=0),c=k?0===c?12:c:0===c?0:c+12):(s&&(c*=5),60===c&&(c=0)),this[this.currentView]!==c&&v&&this.options.vibrate&&(this.vibrateTimer||(navigator[v](10),this.vibrateTimer=setTimeout(n.proxy(function(){this.vibrateTimer=null},this),100))),this[this.currentView]=c,this[a?"spanHours":"spanMinutes"].html(i(c)),!l)return void this[a?"hoursView":"minutesView"].find(".clockpicker-tick").each(function(){var t=n(this);t.toggleClass("active",c===+t.html())});o||!a&&c%5?(this.g.insertBefore(this.hand,this.bearing),this.g.insertBefore(this.bg,this.fg),this.bg.setAttribute("class","clockpicker-canvas-bg clockpicker-canvas-bg-trans")):(this.g.insertBefore(this.hand,this.bg),this.g.insertBefore(this.fg,this.bg),this.bg.setAttribute("class","clockpicker-canvas-bg"));var f=Math.sin(r)*d,m=-Math.cos(r)*d;this.hand.setAttribute("x2",f),this.hand.setAttribute("y2",m),this.bg.setAttribute("cx",f),this.bg.setAttribute("cy",m),this.fg.setAttribute("cx",f),this.fg.setAttribute("cy",m)},s.prototype.done=function(){o(this.options.beforeDone),this.hide();var t=this.input.prop("value"),e=i(this.hours)+":"+i(this.minutes);this.options.twelvehour&&(e+=this.amOrPm),this.input.prop("value",e),e!==t&&(this.input.triggerHandler("change"),this.isInput||this.element.trigger("change")),this.options.autoclose&&this.input.trigger("blur"),o(this.options.afterDone)},s.prototype.remove=function(){this.element.removeData("clockpicker"),this.input.off("focus.clockpicker click.clockpicker"),this.addon.off("click.clockpicker"),this.isShown&&this.hide(),this.isAppended&&(r.off("resize.clockpicker"+this.id),this.popover.remove())},n.fn.clockpicker=function(t){var i=Array.prototype.slice.call(arguments,1);return this.each(function(){var e=n(this),o=e.data("clockpicker");if(o)"function"==typeof o[t]&&o[t].apply(o,i);else{var c=n.extend({},s.DEFAULTS,e.data(),"object"==typeof t&&t);e.data("clockpicker",new s(e,c))}})}}(); \ No newline at end of file diff --git a/media/plg_system_nrframework/js/vendor/jquery.rateyo.min.js b/media/plg_system_nrframework/js/vendor/jquery.rateyo.min.js new file mode 100644 index 00000000..5a6c1668 --- /dev/null +++ b/media/plg_system_nrframework/js/vendor/jquery.rateyo.min.js @@ -0,0 +1,3 @@ +/*rateYo V2.2.0, A simple and flexible star rating plugin +prashanth pamidi (https://github.com/prrashi)*/ +!function(a){"use strict";function b(){var a=!1;return function(b){(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(b)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(b.substr(0,4)))&&(a=!0)}(navigator.userAgent||navigator.vendor||window.opera),a}function c(a,b,c){return a===b?a=b:a===c&&(a=c),a}function d(a,b,c){var d=a>=b&&a<=c;if(!d)throw Error("Invalid Rating, expected value between "+b+" and "+c);return a}function e(a){return"undefined"!=typeof a}function f(a,b,c){var d=(b-a)*(c/100);return d=Math.round(a+d).toString(16),1===d.length&&(d="0"+d),d}function g(a,b,c){if(!a||!b)return null;c=e(c)?c:0,a=q(a),b=q(b);var d=f(a.r,b.r,c),g=f(a.b,b.b,c),h=f(a.g,b.g,c);return"#"+d+h+g}function h(f,i){function k(a){e(a)||(a=i.rating),Z=a;var b=a/P,c=b*R;b>1&&(c+=(Math.ceil(b)-1)*T),r(i.ratedFill),c=i.rtl?100-c:c,X.css("width",c+"%")}function l(){U=Q*i.numStars+S*(i.numStars-1),R=Q/U*100,T=S/U*100,f.width(U),k()}function n(a){var b=i.starWidth=a;return Q=window.parseFloat(i.starWidth.replace("px","")),W.find("svg").attr({width:i.starWidth,height:b}),X.find("svg").attr({width:i.starWidth,height:b}),l(),f}function p(a){return i.spacing=a,S=parseFloat(i.spacing.replace("px","")),W.find("svg:not(:first-child)").css({"margin-left":a}),X.find("svg:not(:first-child)").css({"margin-left":a}),l(),f}function q(a){i.normalFill=a;var b=(i.rtl?X:W).find("svg");return b.attr({fill:i.normalFill}),f}function r(a){if(i.multiColor){var b=Z-Y,c=b/i.maxValue*100,d=i.multiColor||{},e=d.startColor||o.startColor,h=d.endColor||o.endColor;a=g(e,h,c)}else _=a;i.ratedFill=a;var j=(i.rtl?W:X).find("svg");return j.attr({fill:i.ratedFill}),f}function s(a){a=!!a,i.rtl=a,q(i.normalFill),k()}function t(a){i.multiColor=a,r(a?a:_)}function u(b){i.numStars=b,P=i.maxValue/i.numStars,W.empty(),X.empty();for(var c=0;ca&&C(a),k(),f}function w(a){return i.precision=a,C(i.rating),f}function x(a){return i.halfStar=a,f}function y(a){return i.fullStar=a,f}function z(a){var b=a%P,c=P/2,d=i.halfStar,e=i.fullStar;return e||d?(e||d&&b>c?a+=P-b:(a-=b,b>0&&(a+=c)),a):a}function A(a){var b=W.offset(),c=b.left,d=c+W.width(),e=i.maxValue,f=a.pageX,g=0;if(fd)g=e;else{var h=(f-c)/(d-c);if(S>0){h*=100;for(var j=h;j>0;)j>R?(g+=P,j-=R+T):(g+=j/R*P,j=0)}else g=h*i.maxValue;g=z(g)}return i.rtl&&(g=e-g),g}function B(a){return i.readOnly=a,f.attr("readonly",!0),N(),a||(f.removeAttr("readonly"),M()),f}function C(a){var b=a,e=i.maxValue;return"string"==typeof b&&("%"===b[b.length-1]&&(b=b.substr(0,b.length-1),e=100,v(e)),b=parseFloat(b)),d(b,Y,e),b=parseFloat(b.toFixed(i.precision)),c(parseFloat(b),Y,e),i.rating=b,k(),$&&f.trigger("rateyo.set",{rating:b}),f}function D(a){return i.onInit=a,f}function E(a){return i.onSet=a,f}function F(a){return i.onChange=a,f}function G(a){var b=A(a).toFixed(i.precision),d=i.maxValue;b=c(parseFloat(b),Y,d),k(b),f.trigger("rateyo.change",{rating:b})}function H(){b()||(k(),f.trigger("rateyo.change",{rating:i.rating}))}function I(a){var b=A(a).toFixed(i.precision);b=parseFloat(b),O.rating(b)}function J(a,b){i.onInit&&"function"==typeof i.onInit&&i.onInit.apply(this,[b.rating,O])}function K(a,b){i.onChange&&"function"==typeof i.onChange&&i.onChange.apply(this,[b.rating,O])}function L(a,b){i.onSet&&"function"==typeof i.onSet&&i.onSet.apply(this,[b.rating,O])}function M(){f.on("mousemove",G).on("mouseenter",G).on("mouseleave",H).on("click",I).on("rateyo.init",J).on("rateyo.change",K).on("rateyo.set",L)}function N(){f.off("mousemove",G).off("mouseenter",G).off("mouseleave",H).off("click",I).off("rateyo.init",J).off("rateyo.change",K).off("rateyo.set",L)}this.node=f.get(0);var O=this;f.empty().addClass("jq-ry-container");var P,Q,R,S,T,U,V=a("
").addClass("jq-ry-group-wrapper").appendTo(f),W=a("
").addClass("jq-ry-normal-group").addClass("jq-ry-group").appendTo(V),X=a("
").addClass("jq-ry-rated-group").addClass("jq-ry-group").appendTo(V),Y=0,Z=i.rating,$=!1,_=i.ratedFill;this.rating=function(a){return e(a)?(C(a),f):i.rating},this.destroy=function(){return i.readOnly||N(),h.prototype.collection=j(f.get(0),this.collection),f.removeClass("jq-ry-container").children().remove(),f},this.method=function(a){if(!a)throw Error("Method name not specified!");if(!e(this[a]))throw Error("Method "+a+" doesn't exist!");var b=Array.prototype.slice.apply(arguments,[]),c=b.slice(1),d=this[a];return d.apply(this,c)},this.option=function(a,b){if(!e(a))return i;var c;switch(a){case"starWidth":c=n;break;case"numStars":c=u;break;case"normalFill":c=q;break;case"ratedFill":c=r;break;case"multiColor":c=t;break;case"maxValue":c=v;break;case"precision":c=w;break;case"rating":c=C;break;case"halfStar":c=x;break;case"fullStar":c=y;break;case"readOnly":c=B;break;case"spacing":c=p;break;case"rtl":c=s;break;case"onInit":c=D;break;case"onSet":c=E;break;case"onChange":c=F;break;default:throw Error("No such option as "+a)}return e(b)?c(b):i[a]},u(i.numStars),B(i.readOnly),i.rtl&&s(i.rtl),this.collection.push(this),this.rating(i.rating,!0),$=!0,f.trigger("rateyo.init",{rating:i.rating})}function i(b,c){var d;return a.each(c,function(){if(b===this.node)return d=this,!1}),d}function j(b,c){return a.each(c,function(a){if(b===this.node){var d=c.slice(0,a),e=c.slice(a+1,c.length);return c=d.concat(e),!1}}),c}function k(b){var c=h.prototype.collection,d=a(this);if(0===d.length)return d;var e=Array.prototype.slice.apply(arguments,[]);if(0===e.length)b=e[0]={};else{if(1!==e.length||"object"!=typeof e[0]){if(e.length>=1&&"string"==typeof e[0]){var f=e[0],g=e.slice(1),j=[];return a.each(d,function(a,b){var d=i(b,c);if(!d)throw Error("Trying to set options before even initialization");var e=d[f];if(!e)throw Error("Method "+f+" does not exist!");var h=e.apply(d,g);j.push(h)}),j=1===j.length?j[0]:j}throw Error("Invalid Arguments")}b=e[0]}return b=a.extend({},n,b),a.each(d,function(){var d=i(this,c);if(!d)return new h(a(this),a.extend({},b))})}function l(){return k.apply(this,Array.prototype.slice.apply(arguments,[]))}var m='',n={starWidth:"32px",normalFill:"gray",ratedFill:"#f39c12",numStars:5,maxValue:5,precision:1,rating:0,fullStar:!1,halfStar:!1,readOnly:!1,spacing:"0px",rtl:!1,multiColor:null,onInit:null,onChange:null,onSet:null,starSvg:null},o={startColor:"#c0392b",endColor:"#f1c40f"},p=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i,q=function(a){if(!p.test(a))return null;var b=p.exec(a),c=parseInt(b[1],16),d=parseInt(b[2],16),e=parseInt(b[3],16);return{r:c,g:d,b:e}};h.prototype.collection=[],window.RateYo=h,a.fn.rateYo=l}(window.jQuery); diff --git a/media/plg_system_nrframework/js/vendor/justified.layout.min.js b/media/plg_system_nrframework/js/vendor/justified.layout.min.js new file mode 100644 index 00000000..6d93129e --- /dev/null +++ b/media/plg_system_nrframework/js/vendor/justified.layout.min.js @@ -0,0 +1,12 @@ +/*! + * Flickr's Justified Gallery [fjGallery] v2.2.0 (https://flickr-justified-gallery.nkdev.info) + * Copyright 2024 nK + * Licensed under MIT (https://github.com/nk-o/flickr-justified-gallery/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).fjGallery=e()}(this,(function(){"use strict";let t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var e=t;function i(t,e,i){var o=(i||{}).atBegin;return function(t,e,i){var o,n=i||{},s=n.noTrailing,a=void 0!==s&&s,r=n.noLeading,h=void 0!==r&&r,c=n.debounceMode,l=void 0===c?void 0:c,g=!1,d=0;function u(){o&&clearTimeout(o)}function p(){for(var i=arguments.length,n=new Array(i),s=0;st?h?(d=Date.now(),a||(o=setTimeout(l?f:p,t))):p():!0!==a&&(o=setTimeout(l?f:p,void 0===l?t-c:t)))}return p.cancel=function(t){var e=(t||{}).upcomingOnly,i=void 0!==e&&e;u(),g=!i},p}(t,e,{debounceMode:!1!==(void 0!==o&&o)})}var o=function(t){var e=[],i=null,o=function(){for(var o=arguments.length,n=new Array(o),s=0;s=1?(this.items.push(t),this.completeLayout(i/t.aspectRatio,"justify"),!0):othis.maxAspectRatio?0===this.items.length?(this.items.push(Object.assign({},t)),this.completeLayout(i/o,"justify"),!0):(s=this.width-(this.items.length-1)*this.spacing,a=this.items.reduce((function(t,e){return t+e.aspectRatio}),0),r=s/this.targetRowHeight,Math.abs(o-n)>Math.abs(a-r)?(this.completeLayout(s/a,"justify"),!1):(this.items.push(Object.assign({},t)),this.completeLayout(i/o,"justify"),!0)):(this.items.push(Object.assign({},t)),this.completeLayout(i/o,"justify"),!0)},isLayoutComplete:function(){return this.height>0},completeLayout:function(t,e){let i=this.left;const o=this.width-(this.items.length-1)*this.spacing;let n,s,a,r,h,c;(void 0===e||["justify","center","left"].indexOf(e)<0)&&(e="left"),s=Math.max(this.edgeCaseMinRowHeight,Math.min(t,this.edgeCaseMaxRowHeight)),t!==s?(this.height=s,n=o/s/(o/t)):(this.height=t,n=1),this.items.forEach((function(t){t.row=this.index,t.top=this.top,t.width=t.aspectRatio*this.height*n,t.height=this.height,t.left=i,i+=t.width+this.spacing}),this),"justify"===e?(i-=this.spacing+this.left,a=(i-this.width)/this.items.length,r=this.items.map((function(t,e){return Math.round((e+1)*a)})),1===this.items.length?(h=this.items[0],h.width-=Math.round(a)):this.items.forEach((function(t,e){e>0?(t.left-=r[e-1],t.width-=r[e]-r[e-1]):t.width-=r[e]}))):"center"===e&&(c=(this.width-i)/2,this.items.forEach((function(t){t.left+=c+this.spacing}),this))},forceComplete:function(t,e){"number"==typeof e?this.completeLayout(e,this.widowLayoutStyle):this.completeLayout(this.targetRowHeight,this.widowLayoutStyle)},getItems:function(){return this.items}};var h=n((function(t,e){let i={};const o={},n={containerWidth:1060,containerPadding:10,boxSpacing:10,targetRowHeight:320,targetRowHeightTolerance:.25,edgeCaseMinRowHeight:.5,edgeCaseMaxRowHeight:2.5,maxNumRows:Number.POSITIVE_INFINITY,forceAspectRatio:!1,showWidows:!0,fullWidthBreakoutRowCadence:!1,widowLayoutStyle:"left"},s={},h={};return e=e||{},i=Object.assign(n,e),s.top=isNaN(parseFloat(i.containerPadding.top))?i.containerPadding:i.containerPadding.top,s.right=isNaN(parseFloat(i.containerPadding.right))?i.containerPadding:i.containerPadding.right,s.bottom=isNaN(parseFloat(i.containerPadding.bottom))?i.containerPadding:i.containerPadding.bottom,s.left=isNaN(parseFloat(i.containerPadding.left))?i.containerPadding:i.containerPadding.left,h.horizontal=isNaN(parseFloat(i.boxSpacing.horizontal))?i.boxSpacing:i.boxSpacing.horizontal,h.vertical=isNaN(parseFloat(i.boxSpacing.vertical))?i.boxSpacing:i.boxSpacing.vertical,i.containerPadding=s,i.boxSpacing=h,o._layoutItems=[],o._awakeItems=[],o._inViewportItems=[],o._leadingOrphans=[],o._trailingOrphans=[],o._containerHeight=i.containerPadding.top,o._rows=[],o._orphans=[],i._widowCount=0,function(t,e,i){let o,n,s,h=[];return t.forceAspectRatio&&i.forEach((function(e){e.forcedAspectRatio=!0,e.aspectRatio=t.forceAspectRatio})),i.some((function(i,s){if(isNaN(i.aspectRatio))throw new Error("Item "+s+" has an invalid aspect ratio");if(n||(n=a(t,e)),o=n.addItem(i),n.isLayoutComplete()){if(h=h.concat(r(t,e,n)),e._rows.length>=t.maxNumRows)return n=null,!0;if(n=a(t,e),!o&&(o=n.addItem(i),n.isLayoutComplete())){if(h=h.concat(r(t,e,n)),e._rows.length>=t.maxNumRows)return n=null,!0;n=a(t,e)}}})),n&&n.getItems().length&&t.showWidows&&(e._rows.length?(s=e._rows[e._rows.length-1].isBreakoutRow?e._rows[e._rows.length-1].targetRowHeight:e._rows[e._rows.length-1].height,n.forceComplete(!1,s)):n.forceComplete(!1),h=h.concat(r(t,e,n)),t._widowCount=n.getItems().length),e._containerHeight=e._containerHeight-t.boxSpacing.vertical,e._containerHeight=e._containerHeight+t.containerPadding.bottom,{containerHeight:e._containerHeight,widowCount:t._widowCount,boxes:e._layoutItems}}(i,o,t.map((function(t){return t.width&&t.height?{aspectRatio:t.width/t.height}:{aspectRatio:t}})))}));function c(t,e){let i,o=!1,n=!1;const s=()=>{o?e(o):t.naturalWidth&&(o={width:t.naturalWidth,height:t.naturalHeight},e(o),clearInterval(i),n&&c())},a=()=>{s()},r=()=>{s()},h=()=>{t.naturalWidth>0&&s()},c=()=>{n=!1,t.removeEventListener("load",a),t.removeEventListener("error",r)};h(),o||(n=!0,t.addEventListener("load",a),t.addEventListener("error",r),i=setInterval(h,100))}const l=[],g=o((()=>{l.forEach((t=>{t.resize()}))}));var d;e.addEventListener("resize",g),e.addEventListener("orientationchange",g),e.addEventListener("load",g),d=()=>{g()},"complete"===document.readyState||"interactive"===document.readyState?d():document.addEventListener("DOMContentLoaded",d,{capture:!0,once:!0,passive:!0});let u=0;class p{constructor(t,e){const n=this;n.instanceID=u,u+=1,n.$container=t,n.images=[],n.defaults={itemSelector:".fj-gallery-item",imageSelector:"img",gutter:10,rowHeight:320,rowHeightTolerance:.25,maxRowsCount:Number.POSITIVE_INFINITY,edgeCaseMinRowHeight:.5,edgeCaseMaxRowHeight:2.5,lastRow:"left",transitionDuration:"0.3s",calculateItemsHeight:!1,resizeDebounce:100,isRtl:"rtl"===n.css(n.$container,"direction"),onInit:null,onDestroy:null,onAppendImages:null,onBeforeJustify:null,onJustify:null};const s=n.$container.dataset||{},a={};Object.keys(s).forEach((t=>{const e=t.substr(0,1).toLowerCase()+t.substr(1);e&&void 0!==n.defaults[e]&&(a[e]=s[t])})),n.options={...n.defaults,...a,...e},n.pureOptions={...n.options},n.resize=i(n.options.resizeDebounce,n.resize),n.justify=o(n.justify.bind(n)),n.init()}css(t,i){return"string"==typeof i?e.getComputedStyle(t).getPropertyValue(i):(Object.keys(i).forEach((e=>{t.style[e]=i[e]})),t)}applyTransition(t,e){const i=this;i.onTransitionEnd(t)(),i.css(t,{"transition-property":e.join(", "),"transition-duration":i.options.transitionDuration}),t.addEventListener("transitionend",i.onTransitionEnd(t,e),!1)}onTransitionEnd(t){const e=this;return()=>{e.css(t,{"transition-property":"","transition-duration":""}),t.removeEventListener("transitionend",e.onTransitionEnd(t))}}addToFjGalleryList(){l.push(this),g()}removeFromFjGalleryList(){const t=this;l.forEach(((e,i)=>{e.instanceID===t.instanceID&&l.splice(i,1)}))}init(){const t=this;t.appendImages(t.$container.querySelectorAll(t.options.itemSelector)),t.addToFjGalleryList(),t.options.onInit&&t.options.onInit.call(t)}appendImages(t){const i=this;e.jQuery&&t instanceof e.jQuery&&(t=t.get()),t&&t.length&&(t.forEach((t=>{if(t&&!t.fjGalleryImage&&t.querySelector){const e=t.querySelector(i.options.imageSelector);if(e){t.fjGalleryImage=i;const o={$item:t,$image:e,width:parseFloat(e.getAttribute("width"))||!1,height:parseFloat(e.getAttribute("height"))||!1,loadSizes(){const t=this;c(e,(e=>{t.width===e.width&&t.height===e.height||(t.width=e.width,t.height=e.height,i.resize())}))}};o.loadSizes(),i.images.push(o)}}})),i.options.onAppendImages&&i.options.onAppendImages.call(i,[t]),i.justify())}justify(){const t=this,e=[];t.justifyCount=(t.justifyCount||0)+1,t.options.onBeforeJustify&&t.options.onBeforeJustify.call(t),t.images.forEach((t=>{t.width&&t.height&&e.push(t.width/t.height)}));const i={containerWidth:t.$container.getBoundingClientRect().width,containerPadding:{top:parseFloat(t.css(t.$container,"padding-top"))||0,right:parseFloat(t.css(t.$container,"padding-right"))||0,bottom:parseFloat(t.css(t.$container,"padding-bottom"))||0,left:parseFloat(t.css(t.$container,"padding-left"))||0},boxSpacing:t.options.gutter,targetRowHeight:t.options.rowHeight,targetRowHeightTolerance:t.options.rowHeightTolerance,maxNumRows:t.options.maxRowsCount,edgeCaseMinRowHeight:t.options.edgeCaseMinRowHeight,edgeCaseMaxRowHeight:t.options.edgeCaseMaxRowHeight,showWidows:"hide"!==t.options.lastRow},o=h(e,i);if(o.widowCount&&("center"===t.options.lastRow||"right"===t.options.lastRow)){const e=o.boxes[o.boxes.length-1];let n=i.containerWidth-e.width-e.left;"center"===t.options.lastRow&&(n/=2),"right"===t.options.lastRow&&(n-=i.containerPadding.right);for(let t=1;t<=o.widowCount;t+=1)o.boxes[o.boxes.length-t].left=o.boxes[o.boxes.length-t].left+n}t.options.isRtl&&o.boxes.forEach(((t,e)=>{o.boxes[e].left=i.containerWidth-o.boxes[e].left-o.boxes[e].width-i.containerPadding.right+i.containerPadding.left}));let n=0,s=0;const a={};t.images.forEach(((e,i)=>{if(o.boxes[n]&&e.width&&e.height){if(t.options.calculateItemsHeight&&void 0===a[o.boxes[n].row]&&Object.keys(a).length&&(s+=a[Object.keys(a).pop()]-o.boxes[i-1].height),t.options.transitionDuration&&t.justifyCount>1&&t.applyTransition(e.$item,["transform"]),t.css(e.$item,{display:"",position:"absolute",transform:`translateX(${o.boxes[n].left}px) translateY(${o.boxes[n].top+s}px) translateZ(0)`,width:`${o.boxes[n].width}px`}),t.options.calculateItemsHeight){const t=e.$item.getBoundingClientRect();(void 0===a[o.boxes[n].row]||a[o.boxes[n].row]{t.css(e.$item,{position:"",transform:"",transition:"",width:"",height:""})})),t.images.forEach((t=>{delete t.$item.fjGalleryImage})),delete t.$container.fjGallery}resize(){this.justify()}}const f=function(t,e,...i){("object"==typeof HTMLElement?t instanceof HTMLElement:t&&"object"==typeof t&&null!==t&&1===t.nodeType&&"string"==typeof t.nodeName)&&(t=[t]);const o=t.length;let n,s=0;for(;s'}else if(m){u=''}h.innerHTML=u+n.text;h.href="#";t.DomEvent.on(h,"mouseover",this._onItemMouseOver,this).on(h,"mouseout",this._onItemMouseOut,this).on(h,"mousedown",t.DomEvent.stopPropagation).on(h,"click",r);if(t.Browser.touch){t.DomEvent.on(h,this._touchstart,t.DomEvent.stopPropagation)}if(!t.Browser.pointer){t.DomEvent.on(h,"click",this._onItemMouseOut,this)}return{id:t.Util.stamp(h),el:h,callback:r}},_removeItem:function(e){var n,i,o,s,h;for(o=0,s=this._items.length;on.x){i.style.left="auto";i.style.right=Math.min(Math.max(n.x-e.x,0),n.x-o.x-1)+"px"}else{i.style.left=Math.max(e.x,0)+"px";i.style.right="auto"}if(e.y+o.y>n.y){i.style.top="auto";i.style.bottom=Math.min(Math.max(n.y-e.y,0),n.y-o.y-1)+"px"}else{i.style.top=Math.max(e.y,0)+"px";i.style.bottom="auto"}},_getElementSize:function(t){var e=this._size,n=t.style.display;if(!e||this._sizeChanged){e={};t.style.left="-999999px";t.style.right="auto";t.style.display="block";e.x=t.offsetWidth;e.y=t.offsetHeight;t.style.left="auto";t.style.display=n;this._sizeChanged=false}return e},_onKeyDown:function(t){var e=t.keyCode;if(e===27){this._hide()}},_onItemMouseOver:function(e){t.DomUtil.addClass(e.target||e.srcElement,"over")},_onItemMouseOut:function(e){t.DomUtil.removeClass(e.target||e.srcElement,"over")}});t.Map.addInitHook("addHandler","contextmenu",t.Map.ContextMenu);t.Mixin.ContextMenu={bindContextMenu:function(e){t.setOptions(this,e);this._initContextMenu();return this},unbindContextMenu:function(){this.off("contextmenu",this._showContextMenu,this);return this},addContextMenuItem:function(t){this.options.contextmenuItems.push(t)},removeContextMenuItemWithIndex:function(t){var e=[];for(var n=0;n=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=c(t);var e=this.min,i=this.max,n=t.min,o=(t=t.max).x>=e.x&&n.x<=i.x,t=t.y>=e.y&&n.y<=i.y;return o&&t},overlaps:function(t){t=c(t);var e=this.min,i=this.max,n=t.min,o=(t=t.max).x>e.x&&n.xe.y&&n.y=n.lat&&i.lat<=o.lat&&e.lng>=n.lng&&i.lng<=o.lng},intersects:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),o=(t=t.getNorthEast()).lat>=e.lat&&n.lat<=i.lat,t=t.lng>=e.lng&&n.lng<=i.lng;return o&&t},overlaps:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),o=(t=t.getNorthEast()).lat>e.lat&&n.late.lng&&n.lng","http://www.w3.org/2000/svg"===(jt.firstChild&&jt.firstChild.namespaceURI));function w(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var b={ie:Me,ielt9:pt,edge:n,webkit:mt,android:ft,android23:gt,androidStock:vt,opera:yt,chrome:xt,gecko:wt,safari:bt,phantom:Pt,opera12:o,win:Lt,ie3d:Tt,webkit3d:Mt,gecko3d:_t,any3d:zt,mobile:qi,mobileWebkit:Ct,mobileWebkit3d:Zt,msPointer:St,pointer:Et,touch:Ot,touchNative:kt,mobileOpera:At,mobileGecko:Bt,retina:It,passiveEvents:Rt,canvas:Nt,svg:Dt,vml:!Dt&&function(){try{var t=document.createElement("div"),e=(t.innerHTML='',t.firstChild);return e.style.behavior="url(#default#VML)",e&&"object"==typeof e.adj}catch(t){return!1}}(),inlineSvg:jt,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},Ht=b.msPointer?"MSPointerDown":"pointerdown",Ft=b.msPointer?"MSPointerMove":"pointermove",Wt=b.msPointer?"MSPointerUp":"pointerup",Ut=b.msPointer?"MSPointerCancel":"pointercancel",Vt={touchstart:Ht,touchmove:Ft,touchend:Wt,touchcancel:Ut},qt={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&O(e),$t(t,e)},touchmove:$t,touchend:$t,touchcancel:$t},Gt={},Kt=!1;function Yt(t){Gt[t.pointerId]=t}function Xt(t){Gt[t.pointerId]&&(Gt[t.pointerId]=t)}function Jt(t){delete Gt[t.pointerId]}function $t(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){for(var i in e.touches=[],Gt)e.touches.push(Gt[i]);e.changedTouches=[e],t(e)}}var Qt=200;var te,ee,ie,ne,oe,se=fe(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),re=fe(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),ae="webkitTransition"===re||"OTransition"===re?re+"End":"transitionend";function he(t){return"string"==typeof t?document.getElementById(t):t}function le(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];return"auto"===(i=i&&"auto"!==i||!document.defaultView?i:(t=document.defaultView.getComputedStyle(t,null))?t[e]:null)?null:i}function P(t,e,i){return(t=document.createElement(t)).className=e||"",i&&i.appendChild(t),t}function T(t){var e=t.parentNode;e&&e.removeChild(t)}function ue(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ce(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function de(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function _e(t,e){return void 0!==t.classList?t.classList.contains(e):0<(t=me(t)).length&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t)}function M(t,e){var i;if(void 0!==t.classList)for(var n=W(e),o=0,s=n.length;othis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),t=this._limitCenter(i,this._zoom,g(t));return i.equals(t)||this.panTo(t,e),this._enforcingBounds=!1,this},panInside:function(t,e){var i=m((e=e||{}).paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),t=this.project(t),s=(i=c([(s=this.getPixelBounds()).min.add(i),s.max.subtract(n)])).getSize();return i.contains(t)||(this._enforcingBounds=!0,n=t.subtract(i.getCenter()),i=i.extend(t).getSize().subtract(s),o.x+=n.x<0?-i.x:i.x,o.y+=n.y<0?-i.y:i.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var e=this.getSize(),i=(this._sizeChanged=!0,this._lastCenter=null,this.getSize()),n=e.divideBy(2).round(),o=i.divideBy(2).round();return(n=n.subtract(o)).x||n.y?(t.animate&&t.pan?this.panBy(n):(t.pan&&this._rawPanBy(n),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(a(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){var e,i;return t=this._locateOptions=l({timeout:1e4,watch:!1},t),"geolocation"in navigator?(e=a(this._handleGeolocationResponse,this),i=a(this._handleGeolocationError,this),t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t)):this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e;this._container._leaflet_id&&(e=t.code,t=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+t+"."}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e,i,n=new v(t.coords.latitude,t.coords.longitude),o=n.toBounds(2*t.coords.accuracy),s=this._locateOptions,r=(s.setView&&(e=this.getBoundsZoom(o),this.setView(n,s.maxZoom?Math.min(e,s.maxZoom):e)),{latlng:n,bounds:o,timestamp:t.timestamp});for(i in t.coords)"number"==typeof t.coords[i]&&(r[i]=t.coords[i]);this.fire("locationfound",r)}},addHandler:function(t,e){return e&&(e=this[t]=new e(this),this._handlers.push(e),this.options[t]&&e.enable()),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}for(var t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),T(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(r(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)T(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){return e=P("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),e||this._mapPane),t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new s(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=g(t),i=m(i||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),t=t.getSouthEast(),i=this.getSize().subtract(i),t=c(this.project(t,n),this.project(r,n)).getSize(),r=b.any3d?this.options.zoomSnap:1,a=i.x/t.x,i=i.y/t.y,t=e?Math.max(a,i):Math.min(a,i),n=this.getScaleZoom(t,n);return r&&(n=Math.round(n/(r/100))*(r/100),n=e?Math.ceil(n/r)*r:Math.floor(n/r)*r),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new p(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){return new f(t=this._getTopLeftPoint(t,e),t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var i=this.options.crs;return e=void 0===e?this._zoom:e,i.scale(t)/i.scale(e)},getScaleZoom:function(t,e){var i=this.options.crs,t=(e=void 0===e?this._zoom:e,i.zoom(t*i.scale(e)));return isNaN(t)?1/0:t},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(x(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(m(t),e)},layerPointToLatLng:function(t){return t=m(t).add(this.getPixelOrigin()),this.unproject(t)},latLngToLayerPoint:function(t){return this.project(x(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(x(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(g(t))},distance:function(t,e){return this.options.crs.distance(x(t),x(e))},containerPointToLayerPoint:function(t){return m(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return m(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){return t=this.containerPointToLayerPoint(m(t)),this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(x(t)))},mouseEventToContainerPoint:function(t){return Ie(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){if(!(t=this._container=he(t)))throw new Error("Map container not found.");if(t._leaflet_id)throw new Error("Map container is already initialized.");S(t,"scroll",this._onScroll,this),this._containerId=d(t)},_initLayout:function(){var t=this._container,e=(this._fadeAnimated=this.options.fadeAnimation&&b.any3d,M(t,"leaflet-container"+(b.touch?" leaflet-touch":"")+(b.retina?" leaflet-retina":"")+(b.ielt9?" leaflet-oldie":"")+(b.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":"")),le(t,"position"));"absolute"!==e&&"relative"!==e&&"fixed"!==e&&"sticky"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Z(this._mapPane,new p(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(M(t.markerPane,"leaflet-zoom-hide"),M(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e,i){Z(this._mapPane,new p(0,0));var n=!this._loaded,o=(this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset"),this._zoom!==e);this._moveStart(o,i)._move(t,e)._moveEnd(o),this.fire("viewreset"),n&&this.fire("load")},_moveStart:function(t,e){return t&&this.fire("zoomstart"),e||this.fire("movestart"),this},_move:function(t,e,i,n){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),n?i&&i.pinch&&this.fire("zoom",i):((o||i&&i.pinch)&&this.fire("zoom",i),this.fire("move",i)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return r(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Z(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={};var e=t?k:S;e((this._targets[d(this._container)]=this)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&e(window,"resize",this._onResize,this),b.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){r(this._resizeRequest),this._resizeRequest=y(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var i,n=[],o="mouseout"===e||"mouseover"===e,s=t.target||t.srcElement,r=!1;s;){if((i=this._targets[d(s)])&&("click"===e||"preclick"===e)&&this._draggableMoved(i)){r=!0;break}if(i&&i.listens(e,!0)){if(o&&!De(s,t))break;if(n.push(i),o)break}if(s===this._container)break;s=s.parentNode}return n.length||r||o||!this.listens(e,!0)?n:[this]},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e,i=t.target||t.srcElement;!this._loaded||i._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(i)||("mousedown"===(e=t.type)&&we(i),this._fireDOMEvent(t,e))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,i){"click"===t.type&&((a=l({},t)).type="preclick",this._fireDOMEvent(a,a.type,i));var n=this._findEventTargets(t,e);if(i){for(var o=[],s=0;sthis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),n=this._getCenterOffset(t)._divideBy(1-1/n);if(!0!==i.animate&&!this.getSize().contains(n))return!1;y(function(){this._moveStart(!0,!1)._animateZoom(t,e,!0)},this)}return!0},_animateZoom:function(t,e,i,n){this._mapPane&&(i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,M(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:n}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&z(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function He(t){return new B(t)}var Fe,B=et.extend({options:{position:"topright"},initialize:function(t){h(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),t=t._controlCorners[i];return M(e,"leaflet-control"),-1!==i.indexOf("bottom")?t.insertBefore(e,t.firstChild):t.appendChild(e),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(T(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",(e=document.createElement("div")).innerHTML=t,e.firstChild},_addItem:function(t){var e,i=document.createElement("label"),n=this._map.hasLayer(t.layer);t.overlay?((e=document.createElement("input")).type="checkbox",e.className="leaflet-control-layers-selector",e.defaultChecked=n):e=this._createRadioElement("leaflet-base-layers_"+d(this),n),this._layerControlInputs.push(e),e.layerId=d(t.layer),S(e,"click",this._onInputClick,this);(n=document.createElement("span")).innerHTML=" "+t.name;var o=document.createElement("span");return i.appendChild(o),o.appendChild(e),o.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(i),this._checkDisabledLayers(),i},_onInputClick:function(){var t,e,i=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=i.length-1;0<=s;s--)t=i[s],e=this._getLayer(t.layerId).layer,t.checked?n.push(e):t.checked||o.push(e);for(s=0;se.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section;S(t,"click",O),this.expand(),setTimeout(function(){k(t,"click",O)})}})),Ue=B.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=P("div",e+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+"-in",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+"-out",i,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,i,n,o){return(i=P("a",i,n)).innerHTML=t,i.href="#",i.title=e,i.setAttribute("role","button"),i.setAttribute("aria-label",e),Oe(i),S(i,"click",Ae),S(i,"click",o,this),S(i,"click",this._refocusOnMap,this),i},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";z(this._zoomInButton,e),z(this._zoomOutButton,e),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),!this._disabled&&t._zoom!==t.getMinZoom()||(M(this._zoomOutButton,e),this._zoomOutButton.setAttribute("aria-disabled","true")),!this._disabled&&t._zoom!==t.getMaxZoom()||(M(this._zoomInButton,e),this._zoomInButton.setAttribute("aria-disabled","true"))}}),Ve=(A.mergeOptions({zoomControl:!0}),A.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new Ue,this.addControl(this.zoomControl))}),B.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",i=P("div",e),n=this.options;return this._addScales(n,e+"-line",i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=P("div",e,i)),t.imperial&&(this._iScale=P("div",e,i))},_update:function(){var t=(e=this._map).getSize().y/2,e=e.distance(e.containerPointToLatLng([0,t]),e.containerPointToLatLng([this.options.maxWidth,t]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t);this._updateScale(this._mScale,e<1e3?e+" m":e/1e3+" km",e/t)},_updateImperial:function(t){var e,i;5280<(t=3.2808399*t)?(i=this._getRoundNum(e=t/5280),this._updateScale(this._iScale,i+" mi",i/e)):(i=this._getRoundNum(t),this._updateScale(this._iScale,i+" ft",i/t))},_updateScale:function(t,e,i){t.style.width=Math.round(this.options.maxWidth*i)+"px",t.innerHTML=e},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1);return e*(10<=(t=t/e)?10:5<=t?5:3<=t?3:2<=t?2:1)}})),qe=B.extend({options:{position:"bottomright",prefix:''+(b.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){h(this,t),this._attributions={}},onAdd:function(t){for(var e in(t.attributionControl=this)._container=P("div","leaflet-control-attribution"),Oe(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),t.on("layeradd",this._addAttribution,this),this._container},onRemove:function(t){t.off("layeradd",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once("remove",function(){this.removeAttribution(t.layer.getAttribution())},this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t,e=[];for(t in this._attributions)this._attributions[t]&&e.push(t);var i=[];this.options.prefix&&i.push(this.options.prefix),e.length&&i.push(e.join(", ")),this._container.innerHTML=i.join(' ')}}});A.mergeOptions({attributionControl:!0}),A.addInitHook(function(){this.options.attributionControl&&(new qe).addTo(this)}),B.Layers=We,B.Zoom=Ue,B.Scale=Ve,B.Attribution=qe,He.layers=function(t,e,i){return new We(t,e,i)},He.zoom=function(t){return new Ue(t)},He.scale=function(t){return new Ve(t)},He.attribution=function(t){return new qe(t)};(n=et.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}})).addTo=function(t,e){return t.addHandler(e,this),this};var mt={Events:e},Ge=b.touch?"touchstart mousedown":"mousedown",Ke=it.extend({options:{clickTolerance:3},initialize:function(t,e,i,n){h(this,n),this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(S(this._dragStartTarget,Ge,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Ke._dragging===this&&this.finishDrag(!0),k(this._dragStartTarget,Ge,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var e,i;this._enabled&&(this._moved=!1,_e(this._element,"leaflet-zoom-anim")||(t.touches&&1!==t.touches.length?Ke._dragging===this&&this.finishDrag():Ke._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Ke._dragging=this)._preventOutline&&we(this._element),ye(),ie(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=Pe(this._element),this._startPoint=new p(i.clientX,i.clientY),this._startPos=ve(this._element),this._parentScale=Le(e),i="mousedown"===t.type,S(document,i?"mousemove":"touchmove",this._onMove,this),S(document,i?"mouseup":"touchend touchcancel",this._onUp,this)))))},_onMove:function(t){var e;this._enabled&&(t.touches&&1e&&(s.push(t[r]),a=r);return ae.max.x&&(i|=2),t.ye.max.y&&(i|=8),i}function ti(t,e,i,n){var o=e.x,e=e.y,s=i.x-o,r=i.y-e,a=s*s+r*r;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(l=!l);return l||mi.prototype._containsPoint.call(this,t,!0)}})),gi=ai.extend({initialize:function(t,e){h(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,o=u(t)?t:t.features;if(o){for(e=0,i=o.length;es.x&&(r=i.x+a-s.x+o.x),i.x-r-n.x<(a=0)&&(r=i.x-n.x),i.y+e+o.y>s.y&&(a=i.y+e-s.y+o.y),i.y-a-n.y<0&&(a=i.y-n.y),(r||a)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([r,a]))))},_getAnchor:function(){return m(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}})),ki=(A.mergeOptions({closePopupOnClick:!0}),A.include({openPopup:function(t,e,i){return this._initOverlay(Ei,t,e,i).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),o.include({bindPopup:function(t,e){return this._popup=this._initOverlay(Ei,this._popup,t,e),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof ai||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var e;this._popup&&this._map&&(Ae(t),e=t.layer||t.target,this._popup._source!==e||e instanceof di?(this._popup._source=e,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}}),Si.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Si.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(t){Si.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var t=Si.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=P("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+d(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),i=i.layerPointToContainerPoint(t),s=this.options.direction,r=n.offsetWidth,a=n.offsetHeight,h=m(this.options.offset),l=this._getAnchor(),i="top"===s?(e=r/2,a):"bottom"===s?(e=r/2,0):(e="center"===s?r/2:"right"===s?0:"left"===s?r:i.xthis.options.maxZoom||nthis.options.maxZoom||void 0!==this.options.minZoom&&oi.max.x)||!e.wrapLat&&(t.yi.max.y))return!1}return!this.options.bounds||(e=this._tileCoordsToBounds(t),g(this.options.bounds).overlaps(e))},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),i=n.add(i);return[e.unproject(n,t.z),e.unproject(i,t.z)]},_tileCoordsToBounds:function(t){return t=new s((t=this._tileCoordsToNwSe(t))[0],t[1]),this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var e=new p(+(t=t.split(":"))[0],+t[1]);return e.z=+t[2],e},_removeTile:function(t){var e=this._tiles[t];e&&(T(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){M(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=_,t.onmousemove=_,b.ielt9&&this.options.opacity<1&&C(t,this.options.opacity)},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&y(a(this._tileReady,this,t,null,o)),Z(o,i),this._tiles[n]={el:o,coords:t,current:!0},e.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,e,i){e&&this.fire("tileerror",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);(i=this._tiles[n])&&(i.loaded=+new Date,this._map._fadeAnimated?(C(i.el,0),r(this._fadeFrame),this._fadeFrame=y(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(M(i.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),b.ielt9||!this._map._fadeAnimated?y(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new p(this._wrapX?H(t.x,this._wrapX):t.x,this._wrapY?H(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new f(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}})),Bi=Ai.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=h(this,e)).detectRetina&&b.retina&&0')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),Mt={_initContainer:function(){this._container=P("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Ni.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=Hi("shape");M(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=Hi("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[d(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;T(e),t.removeInteractiveTarget(e),delete this._layers[d(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(e=e||(t._stroke=Hi("stroke")),o.appendChild(e),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=u(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=n.lineCap.replace("butt","flat"),e.joinstyle=n.lineJoin):e&&(o.removeChild(e),t._stroke=null),n.fill?(i=i||(t._fill=Hi("fill")),o.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(o.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){ce(t._container)},_bringToBack:function(t){de(t._container)}},Fi=b.vml?Hi:ct,Wi=Ni.extend({_initContainer:function(){this._container=Fi("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=Fi("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){T(this._container),k(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,e,i;this._map._animatingZoom&&this._bounds||(Ni.prototype._update.call(this),e=(t=this._bounds).getSize(),i=this._container,this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute("width",e.x),i.setAttribute("height",e.y)),Z(i,t.min),i.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update"))},_initPath:function(t){var e=t._path=Fi("path");t.options.className&&M(e,t.options.className),t.options.interactive&&M(e,"leaflet-interactive"),this._updateStyle(t),this._layers[d(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){T(t._path),t.removeInteractiveTarget(t._path),delete this._layers[d(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,t=t.options;e&&(t.stroke?(e.setAttribute("stroke",t.color),e.setAttribute("stroke-opacity",t.opacity),e.setAttribute("stroke-width",t.weight),e.setAttribute("stroke-linecap",t.lineCap),e.setAttribute("stroke-linejoin",t.lineJoin),t.dashArray?e.setAttribute("stroke-dasharray",t.dashArray):e.removeAttribute("stroke-dasharray"),t.dashOffset?e.setAttribute("stroke-dashoffset",t.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),t.fill?(e.setAttribute("fill",t.fillColor||t.color),e.setAttribute("fill-opacity",t.fillOpacity),e.setAttribute("fill-rule",t.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,dt(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n="a"+i+","+(Math.max(Math.round(t._radiusY),1)||i)+" 0 1,0 ",e=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+n+2*i+",0 "+n+2*-i+",0 ";this._setPath(t,e)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){ce(t._path)},_bringToBack:function(t){de(t._path)}});function Ui(t){return b.svg||b.vml?new Wi(t):null}b.vml&&Wi.include(Mt),A.include({getRenderer:function(t){return t=(t=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer()),this.hasLayer(t)||this.addLayer(t),t},_getPaneRenderer:function(t){var e;return"overlayPane"!==t&&void 0!==t&&(void 0===(e=this._paneRenderers[t])&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e)},_createRenderer:function(t){return this.options.preferCanvas&&ji(t)||Ui(t)}});var Vi=fi.extend({initialize:function(t,e){fi.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=g(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),_t=(Wi.create=Fi,Wi.pointsToPath=dt,gi.geometryToLayer=vi,gi.coordsToLatLng=xi,gi.coordsToLatLngs=wi,gi.latLngToCoords=bi,gi.latLngsToCoords=Pi,gi.getFeature=Li,gi.asFeature=Ti,A.mergeOptions({boxZoom:!0}),n.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){S(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){k(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){T(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),ie(),ye(),this._startPoint=this._map.mouseEventToContainerPoint(t),S(document,{contextmenu:Ae,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=P("div","leaflet-zoom-box",this._container),M(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var e=(t=new f(this._point,this._startPoint)).getSize();Z(this._box,t.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(T(this._box),z(this._container,"leaflet-crosshair")),Te(),xe(),k(document,{contextmenu:Ae,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0),t=new s(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point)),this._map.fitBounds(t).fire("boxzoomend",{boxZoomBounds:t})))},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}})),zt=(A.addInitHook("addHandler","boxZoom",_t),A.mergeOptions({doubleClickZoom:!0}),n.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,i=t.originalEvent.shiftKey?i-n:i+n;"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}})),qi=(A.addInitHook("addHandler","doubleClickZoom",zt),A.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),n.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Ke(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))),M(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){z(this._map._container,"leaflet-grab"),z(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t,e=this._map;e._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=g(this._map.options.maxBounds),this._offsetLimit=c(this._map.latLngToContainerPoint(t.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(t.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))):this._offsetLimit=null,e.fire("movestart").fire("dragstart"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var e,i;this._map.options.inertia&&(e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(i),this._times.push(e),this._prunePositions(e)),this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=((o=this._draggable._newPos.x)-e+i)%t+e-i,o=(o+e+i)%t-e-i,t=Math.abs(n+i)e.getMaxZoom()&&1b}return!1}function Y(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}function $d(a,b,c,d){var e=R.hasOwnProperty(b)?R[b]:null;if(null!==e?0!==e.type:d||!(2h||e[g]!==f[h]){var k="\n"+e[g].replace(" at new "," at ");a.displayName&&k.includes("")&&(k=k.replace("",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{ce=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:"")?bc(a): +""}function gj(a){switch(a.tag){case 5:return bc(a.type);case 16:return bc("Lazy");case 13:return bc("Suspense");case 19:return bc("SuspenseList");case 0:case 2:case 15:return a=be(a.type,!1),a;case 11:return a=be(a.type.render,!1),a;case 1:return a=be(a.type,!0),a;default:return""}}function de(a){if(null==a)return null;if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Bb:return"Fragment";case Cb:return"Portal";case ee:return"Profiler";case fe:return"StrictMode"; +case ge:return"Suspense";case he:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case gg:return(a.displayName||"Context")+".Consumer";case hg:return(a._context.displayName||"Context")+".Provider";case ie:var b=a.render;a=a.displayName;a||(a=b.displayName||b.name||"",a=""!==a?"ForwardRef("+a+")":"ForwardRef");return a;case je:return b=a.displayName||null,null!==b?b:de(a.type)||"Memo";case Ta:b=a._payload;a=a._init;try{return de(a(b))}catch(c){}}return null}function hj(a){var b=a.type; +switch(a.tag){case 24:return"Cache";case 9:return(b.displayName||"Context")+".Consumer";case 10:return(b._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=b.render,a=a.displayName||a.name||"",b.displayName||(""!==a?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return b;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return de(b);case 8:return b===fe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler"; +case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"===typeof b)return b.displayName||b.name||null;if("string"===typeof b)return b}return null}function Ua(a){switch(typeof a){case "boolean":case "number":case "string":case "undefined":return a;case "object":return a;default:return""}}function ig(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"=== +b)}function ij(a){var b=ig(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker= +null;delete a[b]}}}}function Pc(a){a._valueTracker||(a._valueTracker=ij(a))}function jg(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=ig(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Qc(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function ke(a,b){var c=b.checked;return E({},b,{defaultChecked:void 0,defaultValue:void 0, +value:void 0,checked:null!=c?c:a._wrapperState.initialChecked})}function kg(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=Ua(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function lg(a,b){b=b.checked;null!=b&&$d(a,"checked",b,!1)}function le(a,b){lg(a,b);var c=Ua(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!= +c)a.value=""+c}else a.value!==""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?me(a,b.type,c):b.hasOwnProperty("defaultValue")&&me(a,b.type,Ua(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function mg(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue; +c||b===a.value||(a.value=b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function me(a,b,c){if("number"!==b||Qc(a.ownerDocument)!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function Db(a,b,c,d){a=a.options;if(b){b={};for(var e=0;e>>=0;return 0===a?32:31-(rj(a)/sj|0)|0}function hc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a& +4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function Vc(a,b){var c=a.pendingLanes;if(0===c)return 0;var d=0,e=a.suspendedLanes,f=a.pingedLanes,g=c&268435455;if(0!==g){var h=g&~e;0!==h?d=hc(h):(f&=g,0!==f&&(d=hc(f)))}else g=c&~e,0!==g?d=hc(g):0!==f&&(d=hc(f));if(0===d)return 0;if(0!==b&&b!==d&&0===(b&e)&& +(e=d&-d,f=b&-b,e>=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0c;c++)b.push(a); +return b}function ic(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-ta(b);a[b]=c}function vj(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=$g(c)}}function bh(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?bh(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function ch(){for(var a=window,b=Qc();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break; +b=Qc(a.document)}return b}function Ie(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function Uj(a){var b=ch(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&bh(c.ownerDocument.documentElement,c)){if(null!==d&&Ie(c))if(b=d.start,a=d.end,void 0===a&&(a=b),"selectionStart"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length); +else if(a=(b=c.ownerDocument||document)&&b.defaultView||window,a.getSelection){a=a.getSelection();var e=c.textContent.length,f=Math.min(d.start,e);d=void 0===d.end?f:Math.min(d.end,e);!a.extend&&f>d&&(e=d,d=f,f=e);e=ah(c,f);var g=ah(c,d);e&&g&&(1!==a.rangeCount||a.anchorNode!==e.node||a.anchorOffset!==e.offset||a.focusNode!==g.node||a.focusOffset!==g.offset)&&(b=b.createRange(),b.setStart(e.node,e.offset),a.removeAllRanges(),f>d?(a.addRange(b),a.extend(g.node,g.offset)):(b.setEnd(g.node,g.offset), +a.addRange(b)))}b=[];for(a=c;a=a.parentNode;)1===a.nodeType&&b.push({element:a,left:a.scrollLeft,top:a.scrollTop});"function"===typeof c.focus&&c.focus();for(c=0;cMb||(a.current=Se[Mb],Se[Mb]=null,Mb--)} +function y(a,b,c){Mb++;Se[Mb]=a.current;a.current=b}function Nb(a,b){var c=a.type.contextTypes;if(!c)return cb;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function ea(a){a=a.childContextTypes;return null!==a&&void 0!==a}function th(a,b,c){if(J.current!==cb)throw Error(n(168)); +y(J,b);y(S,c)}function uh(a,b,c){var d=a.stateNode;b=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(n(108,hj(a)||"Unknown",e));return E({},c,d)}function ld(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||cb;qb=J.current;y(J,a);y(S,S.current);return!0}function vh(a,b,c){var d=a.stateNode;if(!d)throw Error(n(169));c?(a=uh(a,b,qb),d.__reactInternalMemoizedMergedChildContext=a,w(S),w(J),y(J,a)):w(S); +y(S,c)}function wh(a){null===La?La=[a]:La.push(a)}function kk(a){md=!0;wh(a)}function db(){if(!Te&&null!==La){Te=!0;var a=0,b=z;try{var c=La;for(z=1;a>=g;e-=g;Ma=1<<32-ta(b)+e|c<q?(v=l,l=null):v=l.sibling;var A=r(e,l,h[q],k);if(null===A){null===l&&(l=v);break}a&&l&&null===A.alternate&&b(e,l);g=f(A,g,q);null===m?n=A:m.sibling=A;m=A;l=v}if(q===h.length)return c(e,l),D&&rb(e,q),n;if(null===l){for(;qv?(A=q,q=null):A=q.sibling;var x=r(e,q,t.value,k);if(null===x){null===q&&(q=A);break}a&&q&&null===x.alternate&&b(e,q);g=f(x,g,v);null===l?m=x:l.sibling=x;l=x;q=A}if(t.done)return c(e,q),D&&rb(e,v),m; +if(null===q){for(;!t.done;v++,t=h.next())t=u(e,t.value,k),null!==t&&(g=f(t,g,v),null===l?m=t:l.sibling=t,l=t);D&&rb(e,v);return m}for(q=d(e,q);!t.done;v++,t=h.next())t=p(q,e,v,t.value,k),null!==t&&(a&&null!==t.alternate&&q.delete(null===t.key?v:t.key),g=f(t,g,v),null===l?m=t:l.sibling=t,l=t);a&&q.forEach(function(a){return b(e,a)});D&&rb(e,v);return m}function w(a,d,f,h){"object"===typeof f&&null!==f&&f.type===Bb&&null===f.key&&(f=f.props.children);if("object"===typeof f&&null!==f){switch(f.$$typeof){case xd:a:{for(var k= +f.key,m=d;null!==m;){if(m.key===k){k=f.type;if(k===Bb){if(7===m.tag){c(a,m.sibling);d=e(m,f.props.children);d.return=a;a=d;break a}}else if(m.elementType===k||"object"===typeof k&&null!==k&&k.$$typeof===Ta&&Kh(k)===m.type){c(a,m.sibling);d=e(m,f.props);d.ref=vc(a,m,f);d.return=a;a=d;break a}c(a,m);break}else b(a,m);m=m.sibling}f.type===Bb?(d=ub(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=wd(f.type,f.key,f.props,null,a.mode,h),h.ref=vc(a,d,f),h.return=a,a=h)}return g(a);case Cb:a:{for(m=f.key;null!== +d;){if(d.key===m)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=hf(f,a.mode,h);d.return=a;a=d}return g(a);case Ta:return m=f._init,w(a,d,m(f._payload),h)}if(cc(f))return x(a,d,f,h);if(ac(f))return F(a,d,f,h);vd(a,f)}return"string"===typeof f&&""!==f||"number"===typeof f?(f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d): +(c(a,d),d=gf(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return w}function vb(a){if(a===wc)throw Error(n(174));return a}function jf(a,b){y(xc,b);y(yc,a);y(Ea,wc);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:oe(null,"");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=oe(b,a)}w(Ea);y(Ea,b)}function Tb(a){w(Ea);w(yc);w(xc)}function Mh(a){vb(xc.current);var b=vb(Ea.current);var c=oe(b,a.type);b!==c&&(y(yc,a),y(Ea,c))}function kf(a){yc.current===a&& +(w(Ea),w(yc))}function yd(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||"$?"===c.data||"$!"===c.data))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.flags&128))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function lf(){for(var a=0;ac?c:4;a(!0);var d=uf.transition;uf.transition={};try{a(!1),b()}finally{z=c,uf.transition=d}}function di(){return sa().memoizedState}function rk(a,b, +c){var d=hb(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ei(a))fi(b,c);else if(c=Ch(a,b,c,d),null!==c){var e=Z();ya(c,a,d,e);gi(c,b,d)}}function pk(a,b,c){var d=hb(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ei(a))fi(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState,h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(ua(h,g)){var k=b.interleaved;null===k?(e.next=e,cf(b)): +(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(m){}finally{}c=Ch(a,b,e,d);null!==c&&(e=Z(),ya(c,a,d,e),gi(c,b,d))}}function ei(a){var b=a.alternate;return a===C||null!==b&&b===C}function fi(a,b){zc=Bd=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function gi(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Ub(a,b){try{var c="",d=b;do c+=gj(d),d=d.return;while(d);var e=c}catch(f){e="\nError generating stack: "+f.message+ +"\n"+f.stack}return{value:a,source:b,stack:e,digest:null}}function vf(a,b,c){return{value:a,source:null,stack:null!=c?c:null,digest:null!=b?b:null}}function wf(a,b){try{console.error(b.value)}catch(c){setTimeout(function(){throw c;})}}function hi(a,b,c){c=Pa(-1,c);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){Ed||(Ed=!0,xf=d);wf(a,b)};return c}function ii(a,b,c){c=Pa(-1,c);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){return d(e)}; +c.callback=function(){wf(a,b)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){wf(a,b);"function"!==typeof d&&(null===ib?ib=new Set([this]):ib.add(this));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ji(a,b,c){var d=a.pingCache;if(null===d){d=a.pingCache=new sk;var e=new Set;d.set(b,e)}else e=d.get(b),void 0===e&&(e=new Set,d.set(b,e));e.has(c)||(e.add(c),a=tk.bind(null,a,b,c),b.then(a,a))}function ki(a){do{var b; +if(b=13===a.tag)b=a.memoizedState,b=null!==b?null!==b.dehydrated?!0:!1:!0;if(b)return a;a=a.return}while(null!==a);return null}function li(a,b,c,d,e){if(0===(a.mode&1))return a===b?a.flags|=65536:(a.flags|=128,c.flags|=131072,c.flags&=-52805,1===c.tag&&(null===c.alternate?c.tag=17:(b=Pa(-1,1),b.tag=2,eb(c,b,1))),c.lanes|=1),a;a.flags|=65536;a.lanes=e;return a}function aa(a,b,c,d){b.child=null===a?mi(b,null,c,d):Vb(b,a.child,c,d)}function ni(a,b,c,d,e){c=c.render;var f=b.ref;Sb(b,e);d=of(a,b,c,d,f, +e);c=pf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&c&&Ue(b);b.flags|=1;aa(a,b,d,e);return b.child}function oi(a,b,c,d,e){if(null===a){var f=c.type;if("function"===typeof f&&!yf(f)&&void 0===f.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=f,pi(a,b,f,d,e);a=wd(c.type,null,d,b,b.mode,e);a.ref=b.ref;a.return=b;return b.child=a}f=a.child;if(0===(a.lanes&e)){var g=f.memoizedProps;c=c.compare;c=null!==c?c:qc;if(c(g,d)&&a.ref=== +b.ref)return Qa(a,b,e)}b.flags|=1;a=gb(f,d);a.ref=b.ref;a.return=b;return b.child=a}function pi(a,b,c,d,e){if(null!==a){var f=a.memoizedProps;if(qc(f,d)&&a.ref===b.ref)if(ha=!1,b.pendingProps=d=f,0!==(a.lanes&e))0!==(a.flags&131072)&&(ha=!0);else return b.lanes=a.lanes,Qa(a,b,e)}return zf(a,b,c,d,e)}function qi(a,b,c){var d=b.pendingProps,e=d.children,f=null!==a?a.memoizedState:null;if("hidden"===d.mode)if(0===(b.mode&1))b.memoizedState={baseLanes:0,cachePool:null,transitions:null},y(Ga,ba),ba|=c; +else{if(0===(c&1073741824))return a=null!==f?f.baseLanes|c:c,b.lanes=b.childLanes=1073741824,b.memoizedState={baseLanes:a,cachePool:null,transitions:null},b.updateQueue=null,y(Ga,ba),ba|=a,null;b.memoizedState={baseLanes:0,cachePool:null,transitions:null};d=null!==f?f.baseLanes:c;y(Ga,ba);ba|=d}else null!==f?(d=f.baseLanes|c,b.memoizedState=null):d=c,y(Ga,ba),ba|=d;aa(a,b,e,c);return b.child}function ri(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.flags|=512,b.flags|=2097152}function zf(a, +b,c,d,e){var f=ea(c)?qb:J.current;f=Nb(b,f);Sb(b,e);c=of(a,b,c,d,f,e);d=pf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&d&&Ue(b);b.flags|=1;aa(a,b,c,e);return b.child}function si(a,b,c,d,e){if(ea(c)){var f=!0;ld(b)}else f=!1;Sb(b,e);if(null===b.stateNode)Fd(a,b),Hh(b,c,d),ff(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var k=g.context,m=c.contextType;"object"===typeof m&&null!==m?m=qa(m):(m=ea(c)?qb:J.current,m=Nb(b, +m));var l=c.getDerivedStateFromProps,n="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate;n||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||k!==m)&&Ih(b,g,d,m);fb=!1;var r=b.memoizedState;g.state=r;td(b,d,g,e);k=b.memoizedState;h!==d||r!==k||S.current||fb?("function"===typeof l&&(ef(b,c,l,d),k=b.memoizedState),(h=fb||Gh(b,c,h,d,r,k,m))?(n||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount|| +("function"===typeof g.componentWillMount&&g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.flags|=4194308)):("function"===typeof g.componentDidMount&&(b.flags|=4194308),b.memoizedProps=d,b.memoizedState=k),g.props=d,g.state=k,g.context=m,d=h):("function"===typeof g.componentDidMount&&(b.flags|=4194308),d=!1)}else{g=b.stateNode;Dh(a,b);h=b.memoizedProps;m=b.type===b.elementType?h:xa(b.type,h);g.props= +m;n=b.pendingProps;r=g.context;k=c.contextType;"object"===typeof k&&null!==k?k=qa(k):(k=ea(c)?qb:J.current,k=Nb(b,k));var p=c.getDerivedStateFromProps;(l="function"===typeof p||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==n||r!==k)&&Ih(b,g,d,k);fb=!1;r=b.memoizedState;g.state=r;td(b,d,g,e);var x=b.memoizedState;h!==n||r!==x||S.current||fb?("function"===typeof p&&(ef(b,c,p,d),x=b.memoizedState), +(m=fb||Gh(b,c,m,d,r,x,k)||!1)?(l||"function"!==typeof g.UNSAFE_componentWillUpdate&&"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,x,k),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,x,k)),"function"===typeof g.componentDidUpdate&&(b.flags|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.flags|=1024)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|= +4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),b.memoizedProps=d,b.memoizedState=x),g.props=d,g.state=x,g.context=k,d=m):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),d=!1)}return Af(a,b,c,d,f,e)}function Af(a,b,c,d,e,f){ri(a,b);var g=0!==(b.flags&128);if(!d&&!g)return e&&vh(b,c,!1), +Qa(a,b,f);d=b.stateNode;uk.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?null:d.render();b.flags|=1;null!==a&&g?(b.child=Vb(b,a.child,null,f),b.child=Vb(b,null,h,f)):aa(a,b,h,f);b.memoizedState=d.state;e&&vh(b,c,!0);return b.child}function ti(a){var b=a.stateNode;b.pendingContext?th(a,b.pendingContext,b.pendingContext!==b.context):b.context&&th(a,b.context,!1);jf(a,b.containerInfo)}function ui(a,b,c,d,e){Qb();Ye(e);b.flags|=256;aa(a,b,c,d);return b.child}function Bf(a){return{baseLanes:a, +cachePool:null,transitions:null}}function vi(a,b,c){var d=b.pendingProps,e=G.current,f=!1,g=0!==(b.flags&128),h;(h=g)||(h=null!==a&&null===a.memoizedState?!1:0!==(e&2));if(h)f=!0,b.flags&=-129;else if(null===a||null!==a.memoizedState)e|=1;y(G,e&1);if(null===a){Xe(b);a=b.memoizedState;if(null!==a&&(a=a.dehydrated,null!==a))return 0===(b.mode&1)?b.lanes=1:"$!"===a.data?b.lanes=8:b.lanes=1073741824,null;g=d.children;a=d.fallback;return f?(d=b.mode,f=b.child,g={mode:"hidden",children:g},0===(d&1)&&null!== +f?(f.childLanes=0,f.pendingProps=g):f=Gd(g,d,0,null),a=ub(a,d,c,null),f.return=b,a.return=b,f.sibling=a,b.child=f,b.child.memoizedState=Bf(c),b.memoizedState=Cf,a):Df(b,g)}e=a.memoizedState;if(null!==e&&(h=e.dehydrated,null!==h))return vk(a,b,g,d,h,e,c);if(f){f=d.fallback;g=b.mode;e=a.child;h=e.sibling;var k={mode:"hidden",children:d.children};0===(g&1)&&b.child!==e?(d=b.child,d.childLanes=0,d.pendingProps=k,b.deletions=null):(d=gb(e,k),d.subtreeFlags=e.subtreeFlags&14680064);null!==h?f=gb(h,f):(f= +ub(f,g,c,null),f.flags|=2);f.return=b;d.return=b;d.sibling=f;b.child=d;d=f;f=b.child;g=a.child.memoizedState;g=null===g?Bf(c):{baseLanes:g.baseLanes|c,cachePool:null,transitions:g.transitions};f.memoizedState=g;f.childLanes=a.childLanes&~c;b.memoizedState=Cf;return d}f=a.child;a=f.sibling;d=gb(f,{mode:"visible",children:d.children});0===(b.mode&1)&&(d.lanes=c);d.return=b;d.sibling=null;null!==a&&(c=b.deletions,null===c?(b.deletions=[a],b.flags|=16):c.push(a));b.child=d;b.memoizedState=null;return d} +function Df(a,b,c){b=Gd({mode:"visible",children:b},a.mode,0,null);b.return=a;return a.child=b}function Hd(a,b,c,d){null!==d&&Ye(d);Vb(b,a.child,null,c);a=Df(b,b.pendingProps.children);a.flags|=2;b.memoizedState=null;return a}function vk(a,b,c,d,e,f,g){if(c){if(b.flags&256)return b.flags&=-257,d=vf(Error(n(422))),Hd(a,b,g,d);if(null!==b.memoizedState)return b.child=a.child,b.flags|=128,null;f=d.fallback;e=b.mode;d=Gd({mode:"visible",children:d.children},e,0,null);f=ub(f,e,g,null);f.flags|=2;d.return= +b;f.return=b;d.sibling=f;b.child=d;0!==(b.mode&1)&&Vb(b,a.child,null,g);b.child.memoizedState=Bf(g);b.memoizedState=Cf;return f}if(0===(b.mode&1))return Hd(a,b,g,null);if("$!"===e.data){d=e.nextSibling&&e.nextSibling.dataset;if(d)var h=d.dgst;d=h;f=Error(n(419));d=vf(f,d,void 0);return Hd(a,b,g,d)}h=0!==(g&a.childLanes);if(ha||h){d=O;if(null!==d){switch(g&-g){case 4:e=2;break;case 16:e=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:e= +32;break;case 536870912:e=268435456;break;default:e=0}e=0!==(e&(d.suspendedLanes|g))?0:e;0!==e&&e!==f.retryLane&&(f.retryLane=e,Oa(a,e),ya(d,a,e,-1))}Ef();d=vf(Error(n(421)));return Hd(a,b,g,d)}if("$?"===e.data)return b.flags|=128,b.child=a.child,b=wk.bind(null,a),e._reactRetry=b,null;a=f.treeContext;fa=Ka(e.nextSibling);la=b;D=!0;wa=null;null!==a&&(na[oa++]=Ma,na[oa++]=Na,na[oa++]=sb,Ma=a.id,Na=a.overflow,sb=b);b=Df(b,d.children);b.flags|=4096;return b}function wi(a,b,c){a.lanes|=b;var d=a.alternate; +null!==d&&(d.lanes|=b);bf(a.return,b,c)}function Ff(a,b,c,d,e){var f=a.memoizedState;null===f?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailMode:e}:(f.isBackwards=b,f.rendering=null,f.renderingStartTime=0,f.last=d,f.tail=c,f.tailMode=e)}function xi(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;aa(a,b,d.children,c);d=G.current;if(0!==(d&2))d=d&1|2,b.flags|=128;else{if(null!==a&&0!==(a.flags&128))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&& +wi(a,c,b);else if(19===a.tag)wi(a,c,b);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(G,d);if(0===(b.mode&1))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===yd(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ff(b,!1,e,c,f);break;case "backwards":c= +null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===yd(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ff(b,!0,c,null,f);break;case "together":Ff(b,!1,null,null,void 0);break;default:b.memoizedState=null}return b.child}function Fd(a,b){0===(b.mode&1)&&null!==a&&(a.alternate=null,b.alternate=null,b.flags|=2)}function Qa(a,b,c){null!==a&&(b.dependencies=a.dependencies);ra|=b.lanes;if(0===(c&b.childLanes))return null;if(null!==a&&b.child!==a.child)throw Error(n(153));if(null!== +b.child){a=b.child;c=gb(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=gb(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}function xk(a,b,c){switch(b.tag){case 3:ti(b);Qb();break;case 5:Mh(b);break;case 1:ea(b.type)&&ld(b);break;case 4:jf(b,b.stateNode.containerInfo);break;case 10:var d=b.type._context,e=b.memoizedProps.value;y(rd,d._currentValue);d._currentValue=e;break;case 13:d=b.memoizedState;if(null!==d){if(null!==d.dehydrated)return y(G,G.current& +1),b.flags|=128,null;if(0!==(c&b.child.childLanes))return vi(a,b,c);y(G,G.current&1);a=Qa(a,b,c);return null!==a?a.sibling:null}y(G,G.current&1);break;case 19:d=0!==(c&b.childLanes);if(0!==(a.flags&128)){if(d)return xi(a,b,c);b.flags|=128}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null,e.lastEffect=null);y(G,G.current);if(d)break;else return null;case 22:case 23:return b.lanes=0,qi(a,b,c)}return Qa(a,b,c)}function Dc(a,b){if(!D)switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!== +b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function W(a){var b=null!==a.alternate&&a.alternate.child===a.child,c=0,d=0;if(b)for(var e=a.child;null!==e;)c|=e.lanes|e.childLanes,d|=e.subtreeFlags&14680064,d|=e.flags&14680064,e.return=a,e=e.sibling;else for(e=a.child;null!==e;)c|=e.lanes|e.childLanes, +d|=e.subtreeFlags,d|=e.flags,e.return=a,e=e.sibling;a.subtreeFlags|=d;a.childLanes=c;return b}function yk(a,b,c){var d=b.pendingProps;Ve(b);switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return W(b),null;case 1:return ea(b.type)&&(w(S),w(J)),W(b),null;case 3:d=b.stateNode;Tb();w(S);w(J);lf();d.pendingContext&&(d.context=d.pendingContext,d.pendingContext=null);if(null===a||null===a.child)pd(b)?b.flags|=4:null===a||a.memoizedState.isDehydrated&&0===(b.flags& +256)||(b.flags|=1024,null!==wa&&(Gf(wa),wa=null));yi(a,b);W(b);return null;case 5:kf(b);var e=vb(xc.current);c=b.type;if(null!==a&&null!=b.stateNode)zk(a,b,c,d,e),a.ref!==b.ref&&(b.flags|=512,b.flags|=2097152);else{if(!d){if(null===b.stateNode)throw Error(n(166));W(b);return null}a=vb(Ea.current);if(pd(b)){d=b.stateNode;c=b.type;var f=b.memoizedProps;d[Da]=b;d[uc]=f;a=0!==(b.mode&1);switch(c){case "dialog":B("cancel",d);B("close",d);break;case "iframe":case "object":case "embed":B("load",d);break; +case "video":case "audio":for(e=0;e\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(c,{is:d.is}):(a=g.createElement(c),"select"===c&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,c);a[Da]=b;a[uc]=d;Ak(a,b,!1,!1);b.stateNode=a;a:{g=qe(c,d);switch(c){case "dialog":B("cancel",a);B("close",a);e=d;break;case "iframe":case "object":case "embed":B("load",a);e=d;break; +case "video":case "audio":for(e=0;eHf&&(b.flags|=128,d=!0,Dc(f,!1),b.lanes=4194304)}else{if(!d)if(a=yd(g),null!==a){if(b.flags|=128,d=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.flags|=4),Dc(f,!0),null===f.tail&&"hidden"===f.tailMode&&!g.alternate&&!D)return W(b),null}else 2*P()-f.renderingStartTime>Hf&&1073741824!==c&&(b.flags|= +128,d=!0,Dc(f,!1),b.lanes=4194304);f.isBackwards?(g.sibling=b.child,b.child=g):(c=f.last,null!==c?c.sibling=g:b.child=g,f.last=g)}if(null!==f.tail)return b=f.tail,f.rendering=b,f.tail=b.sibling,f.renderingStartTime=P(),b.sibling=null,c=G.current,y(G,d?c&1|2:c&1),b;W(b);return null;case 22:case 23:return ba=Ga.current,w(Ga),d=null!==b.memoizedState,null!==a&&null!==a.memoizedState!==d&&(b.flags|=8192),d&&0!==(b.mode&1)?0!==(ba&1073741824)&&(W(b),b.subtreeFlags&6&&(b.flags|=8192)):W(b),null;case 24:return null; +case 25:return null}throw Error(n(156,b.tag));}function Ck(a,b,c){Ve(b);switch(b.tag){case 1:return ea(b.type)&&(w(S),w(J)),a=b.flags,a&65536?(b.flags=a&-65537|128,b):null;case 3:return Tb(),w(S),w(J),lf(),a=b.flags,0!==(a&65536)&&0===(a&128)?(b.flags=a&-65537|128,b):null;case 5:return kf(b),null;case 13:w(G);a=b.memoizedState;if(null!==a&&null!==a.dehydrated){if(null===b.alternate)throw Error(n(340));Qb()}a=b.flags;return a&65536?(b.flags=a&-65537|128,b):null;case 19:return w(G),null;case 4:return Tb(), +null;case 10:return af(b.type._context),null;case 22:case 23:return ba=Ga.current,w(Ga),null;case 24:return null;default:return null}}function Wb(a,b){var c=a.ref;if(null!==c)if("function"===typeof c)try{c(null)}catch(d){H(a,b,d)}else c.current=null}function If(a,b,c){try{c()}catch(d){H(a,b,d)}}function Dk(a,b){Jf=Zc;a=ch();if(Ie(a)){if("selectionStart"in a)var c={start:a.selectionStart,end:a.selectionEnd};else a:{c=(c=a.ownerDocument)&&c.defaultView||window;var d=c.getSelection&&c.getSelection(); +if(d&&0!==d.rangeCount){c=d.anchorNode;var e=d.anchorOffset,f=d.focusNode;d=d.focusOffset;try{c.nodeType,f.nodeType}catch(M){c=null;break a}var g=0,h=-1,k=-1,m=0,t=0,u=a,r=null;b:for(;;){for(var p;;){u!==c||0!==e&&3!==u.nodeType||(h=g+e);u!==f||0!==d&&3!==u.nodeType||(k=g+d);3===u.nodeType&&(g+=u.nodeValue.length);if(null===(p=u.firstChild))break;r=u;u=p}for(;;){if(u===a)break b;r===c&&++m===e&&(h=g);r===f&&++t===d&&(k=g);if(null!==(p=u.nextSibling))break;u=r;r=u.parentNode}u=p}c=-1===h||-1===k?null: +{start:h,end:k}}else c=null}c=c||{start:0,end:0}}else c=null;Kf={focusedElem:a,selectionRange:c};Zc=!1;for(l=b;null!==l;)if(b=l,a=b.child,0!==(b.subtreeFlags&1028)&&null!==a)a.return=b,l=a;else for(;null!==l;){b=l;try{var x=b.alternate;if(0!==(b.flags&1024))switch(b.tag){case 0:case 11:case 15:break;case 1:if(null!==x){var w=x.memoizedProps,z=x.memoizedState,A=b.stateNode,v=A.getSnapshotBeforeUpdate(b.elementType===b.type?w:xa(b.type,w),z);A.__reactInternalSnapshotBeforeUpdate=v}break;case 3:var q= +b.stateNode.containerInfo;1===q.nodeType?q.textContent="":9===q.nodeType&&q.documentElement&&q.removeChild(q.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(n(163));}}catch(M){H(b,b.return,M)}a=b.sibling;if(null!==a){a.return=b.return;l=a;break}l=b.return}x=Ai;Ai=!1;return x}function Gc(a,b,c){var d=b.updateQueue;d=null!==d?d.lastEffect:null;if(null!==d){var e=d=d.next;do{if((e.tag&a)===a){var f=e.destroy;e.destroy=void 0;void 0!==f&&If(b,c,f)}e=e.next}while(e!==d)}} +function Id(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function Lf(a){var b=a.ref;if(null!==b){var c=a.stateNode;switch(a.tag){case 5:a=c;break;default:a=c}"function"===typeof b?b(a):b.current=a}}function Bi(a){var b=a.alternate;null!==b&&(a.alternate=null,Bi(b));a.child=null;a.deletions=null;a.sibling=null;5===a.tag&&(b=a.stateNode,null!==b&&(delete b[Da],delete b[uc],delete b[Me],delete b[Ek], +delete b[Fk]));a.stateNode=null;a.return=null;a.dependencies=null;a.memoizedProps=null;a.memoizedState=null;a.pendingProps=null;a.stateNode=null;a.updateQueue=null}function Ci(a){return 5===a.tag||3===a.tag||4===a.tag}function Di(a){a:for(;;){for(;null===a.sibling;){if(null===a.return||Ci(a.return))return null;a=a.return}a.sibling.return=a.return;for(a=a.sibling;5!==a.tag&&6!==a.tag&&18!==a.tag;){if(a.flags&2)continue a;if(null===a.child||4===a.tag)continue a;else a.child.return=a,a=a.child}if(!(a.flags& +2))return a.stateNode}}function Mf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=kd));else if(4!==d&&(a=a.child,null!==a))for(Mf(a,b,c),a=a.sibling;null!==a;)Mf(a,b,c),a=a.sibling}function Nf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?c.insertBefore(a,b):c.appendChild(a); +else if(4!==d&&(a=a.child,null!==a))for(Nf(a,b,c),a=a.sibling;null!==a;)Nf(a,b,c),a=a.sibling}function jb(a,b,c){for(c=c.child;null!==c;)Ei(a,b,c),c=c.sibling}function Ei(a,b,c){if(Ca&&"function"===typeof Ca.onCommitFiberUnmount)try{Ca.onCommitFiberUnmount(Uc,c)}catch(h){}switch(c.tag){case 5:X||Wb(c,b);case 6:var d=T,e=za;T=null;jb(a,b,c);T=d;za=e;null!==T&&(za?(a=T,c=c.stateNode,8===a.nodeType?a.parentNode.removeChild(c):a.removeChild(c)):T.removeChild(c.stateNode));break;case 18:null!==T&&(za? +(a=T,c=c.stateNode,8===a.nodeType?Re(a.parentNode,c):1===a.nodeType&&Re(a,c),nc(a)):Re(T,c.stateNode));break;case 4:d=T;e=za;T=c.stateNode.containerInfo;za=!0;jb(a,b,c);T=d;za=e;break;case 0:case 11:case 14:case 15:if(!X&&(d=c.updateQueue,null!==d&&(d=d.lastEffect,null!==d))){e=d=d.next;do{var f=e,g=f.destroy;f=f.tag;void 0!==g&&(0!==(f&2)?If(c,b,g):0!==(f&4)&&If(c,b,g));e=e.next}while(e!==d)}jb(a,b,c);break;case 1:if(!X&&(Wb(c,b),d=c.stateNode,"function"===typeof d.componentWillUnmount))try{d.props= +c.memoizedProps,d.state=c.memoizedState,d.componentWillUnmount()}catch(h){H(c,b,h)}jb(a,b,c);break;case 21:jb(a,b,c);break;case 22:c.mode&1?(X=(d=X)||null!==c.memoizedState,jb(a,b,c),X=d):jb(a,b,c);break;default:jb(a,b,c)}}function Fi(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=new Gk);b.forEach(function(b){var d=Hk.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Aa(a,b,c){c=b.deletions;if(null!==c)for(var d=0;de&&(e=g);d&=~f}d=e;d=P()-d;d=(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*Nk(d/1960))-d;if(10a?16:a;if(null===lb)var d=!1;else{a=lb;lb=null;Qd=0;if(0!==(p&6))throw Error(n(331));var e=p;p|=4;for(l=a.current;null!==l;){var f=l,g=f.child;if(0!==(l.flags&16)){var h=f.deletions;if(null!==h){for(var k=0;kP()-Of?xb(a,0):Sf|=c);ia(a,b)}function Ui(a,b){0===b&&(0===(a.mode&1)?b=1:(b=Rd,Rd<<=1,0===(Rd&130023424)&&(Rd=4194304)));var c=Z();a=Oa(a,b);null!==a&&(ic(a,b,c),ia(a,c))}function wk(a){var b=a.memoizedState,c=0;null!==b&&(c=b.retryLane);Ui(a,c)}function Hk(a,b){var c=0;switch(a.tag){case 13:var d=a.stateNode;var e=a.memoizedState;null!==e&&(c=e.retryLane); +break;case 19:d=a.stateNode;break;default:throw Error(n(314));}null!==d&&d.delete(b);Ui(a,c)}function Ni(a,b){return xh(a,b)}function Uk(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.subtreeFlags=this.flags=0;this.deletions=null;this.childLanes=this.lanes=0;this.alternate=null}function yf(a){a= +a.prototype;return!(!a||!a.isReactComponent)}function Vk(a){if("function"===typeof a)return yf(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===ie)return 11;if(a===je)return 14}return 2}function gb(a,b){var c=a.alternate;null===c?(c=pa(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.type=a.type,c.flags=0,c.subtreeFlags=0,c.deletions=null);c.flags=a.flags&14680064;c.childLanes=a.childLanes;c.lanes=a.lanes;c.child= +a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{lanes:b.lanes,firstContext:b.firstContext};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function wd(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)yf(a)&&(g=1);else if("string"===typeof a)g=5;else a:switch(a){case Bb:return ub(c.children,e,f,b);case fe:g=8;e|=8;break;case ee:return a=pa(12,c,b,e|2),a.elementType=ee,a.lanes=f,a;case ge:return a= +pa(13,c,b,e),a.elementType=ge,a.lanes=f,a;case he:return a=pa(19,c,b,e),a.elementType=he,a.lanes=f,a;case Vi:return Gd(c,e,f,b);default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case hg:g=10;break a;case gg:g=9;break a;case ie:g=11;break a;case je:g=14;break a;case Ta:g=16;d=null;break a}throw Error(n(130,null==a?a:typeof a,""));}b=pa(g,c,b,e);b.elementType=a;b.type=d;b.lanes=f;return b}function ub(a,b,c,d){a=pa(7,a,d,b);a.lanes=c;return a}function Gd(a,b,c,d){a=pa(22,a,d,b);a.elementType= +Vi;a.lanes=c;a.stateNode={isHidden:!1};return a}function gf(a,b,c){a=pa(6,a,null,b);a.lanes=c;return a}function hf(a,b,c){b=pa(4,null!==a.children?a.children:[],a.key,b);b.lanes=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Wk(a,b,c,d,e){this.tag=b;this.containerInfo=a;this.finishedWork=this.pingCache=this.current=this.pendingChildren=null;this.timeoutHandle=-1;this.callbackNode=this.pendingContext=this.context=null;this.callbackPriority= +0;this.eventTimes=we(0);this.expirationTimes=we(-1);this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0;this.entanglements=we(0);this.identifierPrefix=d;this.onRecoverableError=e;this.mutableSourceEagerHydrationData=null}function Vf(a,b,c,d,e,f,g,h,k,m){a=new Wk(a,b,c,h,k);1===b?(b=1,!0===f&&(b|=8)):b=0;f=pa(3,null,null,b);a.current=f;f.stateNode=a;f.memoizedState={element:d,isDehydrated:c,cache:null,transitions:null, +pendingSuspenseBoundaries:null};df(f);return a}function Xk(a,b,c){var d=3
\n\t\t';this.videoElement.innerHTML=t},t}(TF_Video); + diff --git a/media/plg_system_nrframework/js/widgets/video/selfhostedvideo.js b/media/plg_system_nrframework/js/widgets/video/selfhostedvideo.js new file mode 100644 index 00000000..cf267e32 --- /dev/null +++ b/media/plg_system_nrframework/js/widgets/video/selfhostedvideo.js @@ -0,0 +1,2 @@ +function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),_setPrototypeOf(t.prototype.constructor=t,e)}function _setPrototypeOf(t,e){return(_setPrototypeOf=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t})(t,e)}var TF_SelfHostedVideo_Video=function(e){function t(t){t=e.call(this,t)||this;return t.player=null,t}_inheritsLoose(t,e);var o=t.prototype;return o.init=function(){this.maybeLoadVideo()},o.pause=function(){this.player&&this.player.pause()},o.maybeLoadVideo=function(){this.player=this.videoElement.querySelector("video");var t=this.player.querySelector("source");t.src=t.dataset.src,this.player.load()},t}(TF_Video); + diff --git a/media/plg_system_nrframework/js/widgets/video/vimeo.js b/media/plg_system_nrframework/js/widgets/video/vimeo.js new file mode 100644 index 00000000..3d604ccf --- /dev/null +++ b/media/plg_system_nrframework/js/widgets/video/vimeo.js @@ -0,0 +1,2 @@ +function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),_setPrototypeOf(t.prototype.constructor=t,e)}function _setPrototypeOf(t,e){return(_setPrototypeOf=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t})(t,e)}var TF_Vimeo_Video=function(e){function t(t){t=e.call(this,t)||this;return t.player=null,t}_inheritsLoose(t,e);var i=t.prototype;return i.init=function(){this.maybeLoadVimeoAPI()},i.pause=function(){this.player&&this.player.pause()},i.vimeoApiLoaded=function(){return window.Vimeo&&window.Vimeo.Player&&void 0!==window.Vimeo.Player},i.maybeLoadVimeoAPI=function(){function i(t){o.vimeoApiLoaded()?o.initVimeoVideo():setTimeout(function(){return i(t)},350)}var t,e,o=this;document.querySelector(".tf-vimeo-api-script")||this.vimeoApiLoaded()||((t=document.createElement("script")).className="tf-vimeo-api-script",t.src="https://player.vimeo.com/api/player.js",(e=document.getElementsByTagName("script")[0]).parentNode.insertBefore(t,e));return new Promise(function(t,e){i(t)})},i.initVimeoVideo=function(){var e=this,i=this,o=(this.player=new window.Vimeo.Player(this.videoElement,{id:this.dataset.videoId,loop:this.setAttributeBool(this.dataset,"videoLoop"),autoplay:this.setAttributeBool(this.dataset,"videoAutoplay"),controls:this.setAttributeBool(this.dataset,"videoControls"),title:this.setAttributeBool(this.dataset,"videoTitle"),byline:this.setAttributeBool(this.dataset,"videoByline"),portrait:this.setAttributeBool(this.dataset,"videoPortrait"),color:this.dataset.videoColor.substring(1),autopause:!1,muted:this.setAttributeBool(this.dataset,"videoMute"),dnt:this.setAttributeBool(this.dataset,"videoPrivacy"),keyboard:this.setAttributeBool(this.dataset,"videoKeyboard"),pip:this.setAttributeBool(this.dataset,"videoPip")}),this.dataset.videoStart&&this.player.setCurrentTime(this.dataset.videoStart),parseInt(this.dataset.videoStart)),s=(0=parseInt(e.dataset.videoEnd)&&(e.pause(),e.dataset.videoEnd=0)});0 \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/circle.svg b/media/plg_system_nrframework/svg/rating/circle.svg new file mode 100644 index 00000000..7e7f6784 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/flag.svg b/media/plg_system_nrframework/svg/rating/flag.svg new file mode 100644 index 00000000..e2cf1f52 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/heart.svg b/media/plg_system_nrframework/svg/rating/heart.svg new file mode 100644 index 00000000..4fbe1629 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/smiley.svg b/media/plg_system_nrframework/svg/rating/smiley.svg new file mode 100644 index 00000000..4b0a6645 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/smiley.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/square.svg b/media/plg_system_nrframework/svg/rating/square.svg new file mode 100644 index 00000000..71f9e095 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/star.svg b/media/plg_system_nrframework/svg/rating/star.svg new file mode 100644 index 00000000..97204151 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/plg_system_nrframework/svg/rating/thumbs_up.svg b/media/plg_system_nrframework/svg/rating/thumbs_up.svg new file mode 100644 index 00000000..62f946b5 --- /dev/null +++ b/media/plg_system_nrframework/svg/rating/thumbs_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/templates/site/joomla-italia-theme/css/pcrt-jit.css b/media/templates/site/joomla-italia-theme/css/pcrt-jit.css index 7a6f8c11..8749335d 100644 --- a/media/templates/site/joomla-italia-theme/css/pcrt-jit.css +++ b/media/templates/site/joomla-italia-theme/css/pcrt-jit.css @@ -754,10 +754,6 @@ article{ color:#B32D41; } -.category-desc p{ - margin-bottom: 0px; -} - /* MODULO NEWS */ .scheda-item{ background: #fff; diff --git a/media/templates/site/joomla-italia-theme/css/pcrt-main.css b/media/templates/site/joomla-italia-theme/css/pcrt-main.css index 80f66a17..fbb73f2a 100644 --- a/media/templates/site/joomla-italia-theme/css/pcrt-main.css +++ b/media/templates/site/joomla-italia-theme/css/pcrt-main.css @@ -1632,4 +1632,273 @@ fieldset legend{ #contact-form .form-check [type=checkbox]:checked+label::after { display: none; +} + +.icon-primary { + fill: #cc334a !important; +} + +/* DIRECTORY */ + +.directory{ + margin-bottom: 3rem; +} + +.directory-item a{ + color: #000; +} + +.directory-item a:hover{ + color: #C3334B; + text-decoration: none; +} + +.directory-item ul li{ + list-style: none; + position: relative; +} + +.directory-item ul li a{ + position: relative; + width: 100%; + display: block; + padding-bottom: .5rem; +} + +.directory-item ul li a::after{ + content: ""; + position: absolute; + z-index: -1; + top: 50%; + transform: translateY(-50%); + right: 0; + width: 12px; + height: 12px; + background-image: url(../images/caret-right-solid.svg); + background-repeat: no-repeat; +} + +.directory-item ul{ + margin-left: 0; + padding-left: 0; +} + +.directory-item .blogitem-header h3{ + border-bottom: 1px solid #000; + padding-bottom: .5rem; +} + +.pagination__wrapper{ + margin-top: 3rem; +} + +/* DOCENTI */ + +/* classi elenco discipline e corsi */ +.correlati-docenti { + margin-top: 3em; + } + .correlati-docenti ul { + margin-top: 0; + } + + + .item-page .article-body ul,.category-desc ul,.correlati-discipline ul,.correlati-info ul,.custom_evidenza ul { + list-style-type: none; +} + +.item-page .article-body ul li,.category-desc ul li,.correlati-discipline ul li,.correlati-info ul li,.custom_evidenza ul li { + text-indent: -1.2em; +} + +.correlati-discipline ul li a,.correlati-info ul li a { + text-decoration: initial; + font-size: 110%; +} + +.item-page .article-body ul li::before,.category-desc ul li::before,.correlati-discipline ul li::before,.correlati-info ul li::before,.custom_evidenza ul li::before { + content: "\e010"; + width: 1.2em; +} + +.correlati-docenti ul { + padding-left: 0; + display: flex; + flex-wrap: wrap; +} + +.correlati-docenti li{ + list-style: none; +} + +.offerta-formativa .list li a, .correlati-docenti li a { + padding: .5em 1.5em .5em .5em; + font-size: 1rem; + color: #000; + font-weight: 500; + position: relative; +} + +.correlati-docenti li a:hover { + color: #C3334B; +} + +.correlati-docenti li a::after { + content: ""; + position: absolute; + z-index: -1; + top: 50%; + transform: translateY(-50%); + right: 0; + width: 12px; + height: 12px; + background-image: url(../images/caret-right-solid.svg); + background-repeat: no-repeat; +} + +.offerta-formativa .list li a, .correlati-docenti li a, .elenco-scuole h3.item-title a { + border: 1px solid #f1f1f1 !important; + margin: .5em 0; + padding: .5rem; + display: block; +} + +.scuole .list li, .offerta-formativa .list li, .correlati-docenti li { + text-indent: 0 !important; + border: 0 !important; + width: 50%; + display: inline-block; + padding: 0 .5rem; +} + +@media(max-width:576px){ + .scuole .list li, .offerta-formativa .list li, .correlati-docenti li { + width: 100%; + } +} + + /* classe filtro docenti */ + + .jlcontentfieldsfilter_filtro{ + display: flex; + flex-wrap: wrap; + } + + .moduletable._filtro { + min-height: 20px; + padding: 1rem; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 5px; + } + .moduletable._filtro .module-title{ + background: none !important; + font-weight: 600 !important; + color: #444 !important; + border-bottom: 1px solid #ccc !important; + font-size: 120% !important; + margin: 0 0 .5rem 0 !important; + } + .moduletable._filtro h4{ + margin: 0 0 .3em !important; + } + .moduletable._filtro .jlmf-section{ + padding-right: .5rem; + padding-left: .5rem; + margin-top: 0; + width: 33.333333%; + } + + .moduletable._filtro .jlmf-section .jlmf-label{ + font-size: .8rem; + font-weight: 600; + } + + .moduletable._filtro .jlmf-section select, + .moduletable._filtro .jlmf-section input{ + width: 100% !important; + border: 1px solid #e3e3e3!important; + border-radius: 10px; + font-size: .9rem; + height: 40px; + } + .moduletable._filtro .jlmf-section input{ + padding: 5px 10px; + } + + .moduletable._filtro .jlmf-button{ + margin-top: 15px; + background-color: #CC334B; + font-weight: 600; + padding: 2px 10px; + text-transform: none; + border: 1px solid #CC334B; + border-radius: 10px; + line-height: 2; + } + + .moduletable._filtro .jlmf-button:hover{ + background: #000; + border: 1px solid #000; + color: #fff; + } + + .moduletable._filtro .jlmf-link { + margin-top: 15px; + border: 1px solid #CC334B; + color: #CC334B; + border-radius: 10px; + font-weight: 600; + padding: 2px 10px; + text-transform: none; + font-size: .8rem; + margin-left: 1rem; + line-height: 2; + } + + .moduletable._filtro .jlmf-link:hover { + background-color: #CC334B; + color: #fff; + } + + .moduletable._filtro .jlmf-link:hover { + text-decoration: initial; + } + + .moduletable._filtro .btn-jlmf{ + display: flex; + width: 100%; + } + + + @media only screen and (max-width: 545px) { + .moduletable._filtro .jlmf-section { + width: 100%; + margin-right: 0; + margin-top: 8px; + overflow: hidden; + } + .moduletable._filtro .jlmf-section input{ + width: 100% !important; + } + } + + +.moduletable._erasmus { + background: #f9f9f9; + margin-bottom: 1em; + margin-top: 2em; + border-left: 5px solid #C8334B; + border-bottom: 1px solid #C8334B; + padding-bottom: 5px; +} + +.moduletable._erasmus .module-title { + background: #C8334B; + border-left: 0px; + padding: .4em 0 .4em .5em; + font-size: 110%; + border-radius: 0; + color: #fff; } \ No newline at end of file diff --git a/media/templates/site/joomla-italia-theme/css/pcrt-menu.css b/media/templates/site/joomla-italia-theme/css/pcrt-menu.css index 16eda995..7c5b795c 100644 --- a/media/templates/site/joomla-italia-theme/css/pcrt-menu.css +++ b/media/templates/site/joomla-italia-theme/css/pcrt-menu.css @@ -1,3 +1,8 @@ +.link-list-wrapper .link-list li.nav-item a svg{ + display: none; +} + + @media (min-width: 992px){ .navbar .navbar-collapsable .navbar-nav li a.nav-link, .navbar .navbar-collapsable .navbar-nav li a.list-item { font-size: .85em; diff --git a/modules/mod_highlights/index.html b/modules/mod_highlights/index.html new file mode 100644 index 00000000..2efb97f3 --- /dev/null +++ b/modules/mod_highlights/index.html @@ -0,0 +1 @@ + diff --git a/modules/mod_highlights/mod_highlights.php b/modules/mod_highlights/mod_highlights.php new file mode 100644 index 00000000..dbfdb2c0 --- /dev/null +++ b/modules/mod_highlights/mod_highlights.php @@ -0,0 +1,23 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Helper\ModuleHelper; +use Pcrt\Module\Highlights\Site\Helper\HighlightsHelper; + +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wr = $wa->getRegistry(); +$wr->addRegistryFile('media/mod_highlights/joomla.asset.json'); +$wa->useStyle('mod_highlights.style') + ->useScript('mod_highlights.script'); + +require ModuleHelper::getLayoutPath('mod_highlights', $params->get('content_type', 'blank')); diff --git a/modules/mod_highlights/mod_highlights.xml b/modules/mod_highlights/mod_highlights.xml new file mode 100644 index 00000000..91f81adf --- /dev/null +++ b/modules/mod_highlights/mod_highlights.xml @@ -0,0 +1,193 @@ + + + MOD_HIGHLIGHTS_NAME + 2024-12-30 + 2024 Eddy Prosperi + GNU General Public License versione 2 o successiva; vedi LICENSE.txt + Eddy Prosperi + eddy.prosperi@protocollicreativi.it + http:// + CVS: 1.0.0 + MOD_HIGHLIGHTS_DESCRIPTION + Pcrt\Module\Highlights + + + mod_highlights.php + index.html + src + tmpl + + + css + js + joomla.asset.json + + + + en-GB/mod_highlights.ini + en-GB/mod_highlights.sys.ini + it-IT/mod_highlights.ini + it-IT/mod_highlights.sys.ini + + + +
+ + + + + + + + + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + +
+ +
+
+ + https://www.component-creator.com/index.php?task=builder.preupdatecheckhook&option=com_combuilder&component=NzY0NzgtMjEzOTAw + +
diff --git a/modules/mod_highlights/src/Helper/HighlightsHelper.php b/modules/mod_highlights/src/Helper/HighlightsHelper.php new file mode 100644 index 00000000..d9c97fbd --- /dev/null +++ b/modules/mod_highlights/src/Helper/HighlightsHelper.php @@ -0,0 +1,267 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ + +namespace Pcrt\Module\Highlights\Site\Helper; + +\defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Language\Language; +use \Joomla\CMS\User\UserFactoryInterface; + +/** + * Helper for mod_highlights + * + * @package com_highlights + * @subpackage mod_highlights + * @since 1.0.0 + */ +Class HighlightsHelper +{ + /** + * Retrieve component items + * + * @param Joomla\Registry\Registry &$params module parameters + * + * @return array Array with all the elements + * + * @throws Exception + */ + public static function getList(&$params) + { + $app = Factory::getApplication(); + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + $tableField = explode(':', $params->get('field')); + $table_name = !empty($tableField[0]) ? $tableField[0] : ''; + $filtroEtichetta = $params->get('filtro_etichetta', []); + $ordering = $params->get('ordering', []); + $ordering_direction = $params->get('ordering_direction', []); + $lingua = \Joomla\CMS\Factory::getLanguage()->getTag(); + + + /* @var $params Joomla\Registry\Registry */ + $query + ->select('*') + ->from($table_name) + ->where('state = 1'); + + if (!empty($filtroEtichetta)) { + $query->where( + $db->quoteName('etichetta') . ' IN (' . implode(',', array_map([$db, 'quote'], $filtroEtichetta)) . ')' + ); + } + + if (!empty($lingua)) { + // Se la lingua è valida, filtra anche per lingua + $query->where( + $db->quoteName('lingua') . ' LIKE ' . $db->quote($lingua) + ); + } + + if (!empty($ordering)) { + $orderingDirection = strtoupper($ordering_direction ?? 'ASC'); // Default ASC + $query->order($db->quoteName($ordering) . ' ' . $orderingDirection); + } + + $db->setQuery($query, $app->input->getInt('offset', (int) $params->get('offset')), $app->input->getInt('limit', (int) $params->get('limit'))); + $rows = $db->loadObjectList(); + + return $rows; + } + + /** + * Retrieve component items + * + * @param Joomla\Registry\Registry &$params module parameters + * + * @return mixed stdClass object if the item was found, null otherwise + */ + public static function getItem(&$params) + { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + /* @var $params Joomla\Registry\Registry */ + $query + ->select('*') + ->from($params->get('item_table')) + ->where('id = ' . intval($params->get('item_id'))); + + $db->setQuery($query); + $element = $db->loadObject(); + + return $element; + } + + /** + * Render element + * + * @param Joomla\Registry\Registry $table_name Table name + * @param string $field_name Field name + * @param string $field_value Field value + * + * @return string + */ + public static function renderElement($table_name, $field_name, $field_value) + { + $result = ''; + + if(strpos($field_name, ':')) + { + $tableField = explode(':', $field_name); + $table_name = !empty($tableField[0]) ? $tableField[0] : ''; + $field_name = !empty($tableField[1]) ? $tableField[1] : ''; + } + + switch ($table_name) + { + + case '#__highlights_': + switch($field_name){ + case 'id': + $result = $field_value; + break; + case 'created_by': + $container = \Joomla\CMS\Factory::getContainer(); + $userFactory = $container->get(UserFactoryInterface::class); + $user = $userFactory->loadUserById($field_value); + $result = $user->name; + break; + case 'modified_by': + $container = \Joomla\CMS\Factory::getContainer(); + $userFactory = $container->get(UserFactoryInterface::class); + $user = $userFactory->loadUserById($field_value); + $result = $user->name; + break; + case 'etichetta': + $result = self::loadValueFromExternalTable('#__highlights_etichetta', 'nome', 'nome', $field_value); + break; + case 'titolo': + $result = $field_value; + break; + case 'sottotitolo': + $result = $field_value; + break; + case 'descrizione': + $result = $field_value; + break; + case 'lingua': + $result = Language::getInstance($field_value)->getName(); + break; + case 'link_pulsante': + $result = $field_value; + break; + case 'testo_pulsante': + $result = $field_value; + break; + case 'data': + $result = $field_value; + break; + case 'immagine_main': + $result = $field_value; + break; + case 'immagine_secondaria': + $result = $field_value; + break; + case 'data_inizio_pubblicazione': + $result = $field_value; + break; + case 'data_fine_pubblicazione': + $result = $field_value; + break; + } + break; + case '#__highlights_etichetta': + switch($field_name){ + case 'id': + $result = $field_value; + break; + case 'created_by': + $container = \Joomla\CMS\Factory::getContainer(); + $userFactory = $container->get(UserFactoryInterface::class); + $user = $userFactory->loadUserById($field_value); + $result = $user->name; + break; + case 'modified_by': + $container = \Joomla\CMS\Factory::getContainer(); + $userFactory = $container->get(UserFactoryInterface::class); + $user = $userFactory->loadUserById($field_value); + $result = $user->name; + break; + case 'nome': + $result = $field_value; + break; + case 'lingua': + $result = Language::getInstance($field_value)->getName(); + break; + } + break; + } + + return $result; + } + + /** + * Returns the translatable name of the element + * + * @param string .................. $table_name table name + * @param string $field Field name + * + * @return string Translatable name. + */ + public static function renderTranslatableHeader($table_name, $field) + { + return Text::_( + 'MOD_HIGHLIGHTS_HEADER_FIELD_' . str_replace('#__', '', strtoupper($table_name)) . '_' . strtoupper($field) + ); + } + + /** + * Checks if an element should appear in the table/item view + * + * @param string $field name of the field + * + * @return boolean True if it should appear, false otherwise + */ + public static function shouldAppear($field) + { + $noHeaderFields = array('checked_out_time', 'checked_out', 'ordering', 'state'); + + return !in_array($field, $noHeaderFields); + } + + + + /** + * Method to get a value from a external table + * @param string $source_table Source table name + * @param string $key_field Source key field + * @param string $value_field Source value field + * @param mixed $key_value Value for the key field + * @return mixed The value in the external table or null if it wasn't found + */ + private static function loadValueFromExternalTable($source_table, $key_field, $value_field, $key_value) { + $db = Factory::getContainer()->get('DatabaseDriver'); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName($value_field)) + ->from($source_table) + ->where($db->quoteName($key_field) . ' = ' . $db->quote($key_value)); + + + $db->setQuery($query); + return $db->loadResult(); + } +} diff --git a/modules/mod_highlights/tmpl/blank.php b/modules/mod_highlights/tmpl/blank.php new file mode 100644 index 00000000..5034e8b8 --- /dev/null +++ b/modules/mod_highlights/tmpl/blank.php @@ -0,0 +1,25 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use \Joomla\CMS\Filter\InputFilter; + +$safe_htmltags = array( + 'a', 'address', 'em', 'strong', 'b', 'i', + 'big', 'small', 'sub', 'sup', 'cite', 'code', + 'img', 'ul', 'ol', 'li', 'dl', 'lh', 'dt', 'dd', + 'br', 'p', 'table', 'th', 'td', 'tr', 'pre', + 'blockquote', 'nowiki', 'h1', 'h2', 'h3', + 'h4', 'h5', 'h6', 'hr'); + +/* @var $params Joomla\Registry\Registry */ +$filter = InputFilter::getInstance($safe_htmltags); +echo $filter->clean($params->get('html_content')); diff --git a/modules/mod_highlights/tmpl/countdown.php b/modules/mod_highlights/tmpl/countdown.php new file mode 100644 index 00000000..9faa2504 --- /dev/null +++ b/modules/mod_highlights/tmpl/countdown.php @@ -0,0 +1,56 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use Pcrt\Module\Highlights\Site\Helper\HighlightsHelper; + +$elements = HighlightsHelper::getList($params); + +$tableField = explode(':', $params->get('field')); +$table_name = !empty($tableField[0]) ? $tableField[0] : ''; +$field_name = !empty($tableField[1]) ? $tableField[1] : ''; + +//Come accedere alle variabili generali: +$opacita = $params->get('opacita', []); +$sfondo = $params->get('sfondo', []); + +?> + + +
+
+
+ +
+ +
+ +
+ +
+
+
+
+ \ No newline at end of file diff --git a/modules/mod_highlights/tmpl/didattica.php b/modules/mod_highlights/tmpl/didattica.php new file mode 100644 index 00000000..9faa2504 --- /dev/null +++ b/modules/mod_highlights/tmpl/didattica.php @@ -0,0 +1,56 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use Pcrt\Module\Highlights\Site\Helper\HighlightsHelper; + +$elements = HighlightsHelper::getList($params); + +$tableField = explode(':', $params->get('field')); +$table_name = !empty($tableField[0]) ? $tableField[0] : ''; +$field_name = !empty($tableField[1]) ? $tableField[1] : ''; + +//Come accedere alle variabili generali: +$opacita = $params->get('opacita', []); +$sfondo = $params->get('sfondo', []); + +?> + + +
+
+
+ +
+ +
+ +
+ +
+
+
+
+ \ No newline at end of file diff --git a/modules/mod_highlights/tmpl/index.html b/modules/mod_highlights/tmpl/index.html new file mode 100644 index 00000000..2efb97f3 --- /dev/null +++ b/modules/mod_highlights/tmpl/index.html @@ -0,0 +1 @@ + diff --git a/modules/mod_highlights/tmpl/item.php b/modules/mod_highlights/tmpl/item.php new file mode 100644 index 00000000..8bc1ebb0 --- /dev/null +++ b/modules/mod_highlights/tmpl/item.php @@ -0,0 +1,32 @@ + + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use Pcrt\Module\Highlights\Site\Helper\HighlightsHelper; + +$element = HighlightsHelper::getItem($params); +?> + + +
+ + $field_value) : ?> + +
+
+ get('item_table'), $field_name); ?> +
+
get('item_table'), $field_name, $field_value); ?>
+
+ + +
+ + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use Pcrt\Module\Highlights\Site\Helper\HighlightsHelper; + +$elements = HighlightsHelper::getList($params); + +$tableField = explode(':', $params->get('field')); +$table_name = !empty($tableField[0]) ? $tableField[0] : ''; +$field_name = !empty($tableField[1]) ? $tableField[1] : ''; +?> + + + + + + + + + +
get('field'), $element->{$field_name} + ); ?>
+ + * @copyright 2024 Eddy Prosperi + * @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt + */ +defined('_JEXEC') or die; + +use Pcrt\Module\Highlights\Site\Helper\HighlightsHelper; + +$elements = HighlightsHelper::getList($params); + +$tableField = explode(':', $params->get('field')); +$table_name = !empty($tableField[0]) ? $tableField[0] : ''; +$field_name = !empty($tableField[1]) ? $tableField[1] : ''; + +//Come accedere alle variabili generali: +$opacita = $params->get('opacita', []); +$sfondo = $params->get('sfondo', []); + +?> + + +
+
+
+ +
+ +
+ +
+ +
+
+
+
+ \ No newline at end of file diff --git a/plugins/fields/acfaddress/acfaddress.php b/plugins/fields/acfaddress/acfaddress.php new file mode 100644 index 00000000..ad4596f8 --- /dev/null +++ b/plugins/fields/acfaddress/acfaddress.php @@ -0,0 +1,64 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFAddress extends ACF_Field +{ + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + $data = $option->getData(); + $data = is_string($data->value) && json_decode($data->value, true) ? json_decode($data->value, true) : false; + + if (!$data) + { + continue; + } + + $address = isset($data['address']['address']) ? $data['address']['address'] : ''; + + if (!$address) + { + continue; + } + + $option->setLabel($address); + } + + return $options; + } +} diff --git a/plugins/fields/acfaddress/acfaddress.xml b/plugins/fields/acfaddress/acfaddress.xml new file mode 100644 index 00000000..f517f332 --- /dev/null +++ b/plugins/fields/acfaddress/acfaddress.xml @@ -0,0 +1,25 @@ + + + ACF_ADDRESS + ACF_ADDRESS_DESC + Tassos Marinos + December 2022 + Copyright (C) 2022 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfaddress.php + script.install.helper.php + version.php + language + params + tmpl + fields + + + img + + diff --git a/plugins/fields/acfaddress/fields/acfaddress.php b/plugins/fields/acfaddress/fields/acfaddress.php new file mode 100644 index 00000000..831c5623 --- /dev/null +++ b/plugins/fields/acfaddress/fields/acfaddress.php @@ -0,0 +1,63 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Form\Field\TextField; + +class JFormFieldACFAddress extends TextField +{ + /** + * Method to get the field input markup. + * + * @return string The field input markup. + */ + public function getInput() + { + // Setup properties + $zoom = $this->element['zoom'] ? (int) $this->element['zoom'] : 4; + $marker_image = !empty($this->element['marker_image']) ? (string) $this->element['marker_image'] : null; + $value = is_string($this->value) ? (json_decode($this->value) ? json_decode($this->value, true) : null) : json_decode(json_encode($this->value), true); + $address = isset($value['address']) ? $value['address'] : []; + $show_map = (string) $this->element['show_map']; + $autocomplete = (string) $this->element['autocomplete']; + $coordinates = ''; + + if (isset($address['latitude']) && !empty($address['latitude']) && isset($address['longitude']) && !empty($address['longitude'])) + { + $coordinates = $address['latitude'] . ',' . $address['longitude']; + } + + $payload = [ + 'width' => 600, + 'height' => 400, + 'required' => $this->required, + 'name' => $this->name, + 'id' => $this->id, + 'zoom' => $zoom, + 'markerImage' => $marker_image, + 'show_map' => $show_map === '0' ? false : $show_map, + 'autocomplete' => $autocomplete === '1', + 'value' => $coordinates, + 'address' => $address + ]; + + $showAddressDetails = isset($this->element['address_details_info']) ? (string) $this->element['address_details_info'] : false; + if ($showAddressDetails) + { + // Make it an array + $showAddressDetails = array_map('trim', explode(',', $showAddressDetails)); + // Set all values of each array key to true + $showAddressDetails = array_fill_keys($showAddressDetails, true); + $payload['_showAddressDetails'] = $showAddressDetails; + } + + return \NRFramework\Widgets\Helper::render('MapAddressEditor', $payload); + } +} \ No newline at end of file diff --git a/plugins/fields/acfaddress/language/de-DE/de-DE.plg_fields_acfaddress.ini b/plugins/fields/acfaddress/language/de-DE/de-DE.plg_fields_acfaddress.ini new file mode 100644 index 00000000..1605edbd --- /dev/null +++ b/plugins/fields/acfaddress/language/de-DE/de-DE.plg_fields_acfaddress.ini @@ -0,0 +1,35 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFADDRESS_LABEL="ACF - Adressen Autovervollständigung" +ACF_ADDRESS="Fields - ACF Adressen Autovervollständigung" +ACF_ADDRESS_DESC="Zeigt eine Karte mit den Standortdetails wie Adresse, Stadt, Bundesland, Land usw. an." +ACF_ADDRESS_VALUE_DESC="Wählen Sie einen Ort auf der Karte, geben Sie eine Adresse oder Koordinaten in das Suchfeld ein." +ACF_ADDRESS_ZOOM="Zoom" +ACF_ADDRESS_ZOOM_DESC="Zoom-Stufe einstellen" +ACF_ADDRESS_WIDTH="Breite" +ACF_ADDRESS_WIDTH_DESC="" +ACF_ADDRESS_HEIGHT="Höhe" +ACF_ADDRESS_HEIGHT_DESC="" +ACF_ADDRESS_ADDRESS="Adresse" +ACF_ADDRESS_ADDRESS_DESC="Eine Adresse oder Koordinaten für die Suche eingeben" +ACF_ADDRESS_SHOW_MAP="Zeige Karte" +ACF_ADDRESS_SHOW_MAP_DESC="Festlegen, ob und wo die Karte angezeigt werden soll.

Backend: Zeigt Sie die Karte nur an, wenn das Feld in einem Artikel, Kontakt usw. bearbeiten wurde...
Frontend: Zeigt die Karte nur an, wenn das Feld im Frontend Ihrer Website anzeigt wird, z. B. in einem Artikel, Kontakt usw....
Beide: Zeigt die Karte sowohl bei der Bearbeitung des benutzerdefinierten Feldes in einem Artikel, Kontakt usw. als auch bei der Anzeige des Feldes im Frontend Ihrer Website an, z. B. beim Anzeigen eines Artikels." +ACF_ADDRESS_MARKER_IMAGE="Marker Bild" +ACF_ADDRESS_MARKER_IMAGE_DESC="Ersetzt Sie den Standard-Marker durch ein Bild Ihrer Wahl." +ACF_ADDRESS_DETAILS_LAYOUT="Layout" +ACF_ADDRESS_DETAILS_LAYOUT_DESC="Wählen Sie das zu verwendende Layout, Standard oder benutzerdefiniert.

Standard: Die Karte und darunter alle ausgewählten Adressdetails anzeigen.
Benutzerdefiniert: Verwenden Sie Smart Tags, um ein benutzerdefiniertes Layout anzuzeigen. Unterstützt auch HTML-Code." +ACF_ADDRESS_MAP_LOCATION="Standort" +ACF_ADDRESS_MAP_LOCATION_DESC="Legt fest, ob die Karte über oder unter den Adressdetails angezeigt werden soll." +ACF_ADDRESS_ABOVE="über der Adresse" +ACF_ADDRESS_BELOW="unter der Adresse" +ACF_ADDRESS_DETAILS_CUSTOM="Benutzerdefiniertes Layout" +ACF_ADDRESS_DETAILS_CUSTOM_DESC="Legen Sie das benutzerdefinierte Layout für die Adressdetails fest. Unterstützt auch HTML-Code.

Erlaubte Smart Tags:
{address.map}
{address.address} - {address.address.label}
{address.latitude} - {address.latitude.label}
{address.longitude} - {address.longitude.label}
{address.country} - {address.country.label}
{address.country_code} - {address.country_code.label}
{address.city} - {address.city.label}
{address.county} - {address.county.label}
{address.postal_code} - {address.postal_code.label}
{address.state} - {address.state.label}
{address.municipality} - {address.municipality.label}
{address.town} - {address.town.label}
{address.road} - {address.road.label}" +ACF_ADDRESS_DETAILS_INFO="Adressdetails" +ACF_ADDRESS_DETAILS_INFO_DESC="Legen Sie fest, welche Adressdetails angezeigt werden sollen." +ACF_ADDRESS_AUTOCOMPLETE="Adress-Autovervollständigung" +ACF_ADDRESS_AUTOCOMPLETE_DESC="Legen Sie fest, ob die automatische Vervollständigung von Adressen aktiviert werden soll und ob bei der Eingabe einer Adresse automatisch Orte vorgeschlagen werden sollen." diff --git a/plugins/fields/acfaddress/language/de-DE/de-DE.plg_fields_acfaddress.sys.ini b/plugins/fields/acfaddress/language/de-DE/de-DE.plg_fields_acfaddress.sys.ini new file mode 100644 index 00000000..7945c643 --- /dev/null +++ b/plugins/fields/acfaddress/language/de-DE/de-DE.plg_fields_acfaddress.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_ADDRESS="Felder – Automatische Vervollständigung der ACF-Adresse" +ACF_ADDRESS_DESC="Zeigen Sie eine Karte an mit Standortdetails wie Adresse, Stadt, Bundesland, Land, etc.." diff --git a/plugins/fields/acfaddress/language/el-GR/el-GR.plg_fields_acfaddress.ini b/plugins/fields/acfaddress/language/el-GR/el-GR.plg_fields_acfaddress.ini new file mode 100644 index 00000000..5329911c --- /dev/null +++ b/plugins/fields/acfaddress/language/el-GR/el-GR.plg_fields_acfaddress.ini @@ -0,0 +1,35 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +; PLG_FIELDS_ACFADDRESS_LABEL="ACF - Address Autocomplete" +; ACF_ADDRESS="Fields - ACF Address Autocomplete" +; ACF_ADDRESS_DESC="Display a map with the location details such as address, city, state, country, etc..." +; ACF_ADDRESS_VALUE_DESC="Pick a location from the map, enter an address or coordinates in the search field." +; ACF_ADDRESS_ZOOM="Zoom" +; ACF_ADDRESS_ZOOM_DESC="Set the map zoom." +ACF_ADDRESS_WIDTH="Μήκος" +ACF_ADDRESS_WIDTH_DESC="" +ACF_ADDRESS_HEIGHT="Ύψος" +ACF_ADDRESS_HEIGHT_DESC="" +ACF_ADDRESS_ADDRESS="Διέθυνση" +; ACF_ADDRESS_ADDRESS_DESC="Enter an address or coordinates to search" +ACF_ADDRESS_SHOW_MAP="Εμφάνιση Χάρτη" +; ACF_ADDRESS_SHOW_MAP_DESC="Set whether and where to show the map.

Back-end: Show the map only when editing the field in an article, contact, etc...
Front-end: Show the map only when viewing the field on the front-end of your site such as in an article, contact, etc...
Both: Show the map both when editing the custom field in an article, contact, etc... as well as when viewing the field on the front-end of your site such as when viewing an article." +; ACF_ADDRESS_MARKER_IMAGE="Marker Image" +; ACF_ADDRESS_MARKER_IMAGE_DESC="Replace the default marker image by uploading an image of your choice." +; ACF_ADDRESS_DETAILS_LAYOUT="Layout" +; ACF_ADDRESS_DETAILS_LAYOUT_DESC="Select which layout to use, default or custom.

Default: Show a map and below all selected address details.
Custom: Use Smart Tags to display a custom layout. Also supports HTML code." +; ACF_ADDRESS_MAP_LOCATION="Map Location" +; ACF_ADDRESS_MAP_LOCATION_DESC="Set whether to display the map above or below the address details." +; ACF_ADDRESS_ABOVE="Above Address" +; ACF_ADDRESS_BELOW="Below Address" +; ACF_ADDRESS_DETAILS_CUSTOM="Custom Layout" +; ACF_ADDRESS_DETAILS_CUSTOM_DESC="Set the Custom Layout for the address details. Also supports HTML code.

Allowed Smart Tags:
{address.map}
{address.address} - {address.address.label}
{address.latitude} - {address.latitude.label}
{address.longitude} - {address.longitude.label}
{address.country} - {address.country.label}
{address.country_code} - {address.country_code.label}
{address.city} - {address.city.label}
{address.county} - {address.county.label}
{address.postal_code} - {address.postal_code.label}
{address.state} - {address.state.label}
{address.municipality} - {address.municipality.label}
{address.town} - {address.town.label}
{address.road} - {address.road.label}" +; ACF_ADDRESS_DETAILS_INFO="Address Details" +; ACF_ADDRESS_DETAILS_INFO_DESC="Set which address details to show." +; ACF_ADDRESS_AUTOCOMPLETE="Address Autocomplete" +; ACF_ADDRESS_AUTOCOMPLETE_DESC="Set whether to enable address autocomplete and automatically suggest places as you type an address." diff --git a/plugins/fields/acfaddress/language/en-GB/en-GB.plg_fields_acfaddress.ini b/plugins/fields/acfaddress/language/en-GB/en-GB.plg_fields_acfaddress.ini new file mode 100644 index 00000000..aee39569 --- /dev/null +++ b/plugins/fields/acfaddress/language/en-GB/en-GB.plg_fields_acfaddress.ini @@ -0,0 +1,35 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFADDRESS_LABEL="ACF - Address Autocomplete" +ACF_ADDRESS="Fields - ACF Address Autocomplete" +ACF_ADDRESS_DESC="Display a map with the location details such as address, city, state, country, etc..." +ACF_ADDRESS_VALUE_DESC="Pick a location from the map, enter an address or coordinates in the search field." +ACF_ADDRESS_ZOOM="Zoom" +ACF_ADDRESS_ZOOM_DESC="Set the map zoom." +ACF_ADDRESS_WIDTH="Width" +ACF_ADDRESS_WIDTH_DESC="" +ACF_ADDRESS_HEIGHT="Height" +ACF_ADDRESS_HEIGHT_DESC="" +ACF_ADDRESS_ADDRESS="Address" +ACF_ADDRESS_ADDRESS_DESC="Enter an address or coordinates to search" +ACF_ADDRESS_SHOW_MAP="Show Map" +ACF_ADDRESS_SHOW_MAP_DESC="Set whether and where to show the map.

Back-end: Show the map only when editing the field in an article, contact, etc...
Front-end: Show the map only when viewing the field on the front-end of your site such as in an article, contact, etc...
Both: Show the map both when editing the custom field in an article, contact, etc... as well as when viewing the field on the front-end of your site such as when viewing an article." +ACF_ADDRESS_MARKER_IMAGE="Marker Image" +ACF_ADDRESS_MARKER_IMAGE_DESC="Replace the default marker image by uploading an image of your choice." +ACF_ADDRESS_DETAILS_LAYOUT="Layout" +ACF_ADDRESS_DETAILS_LAYOUT_DESC="Select which layout to use, default or custom.

Default: Show a map and below all selected address details.
Custom: Use Smart Tags to display a custom layout. Also supports HTML code." +ACF_ADDRESS_MAP_LOCATION="Map Location" +ACF_ADDRESS_MAP_LOCATION_DESC="Set whether to display the map above or below the address details." +ACF_ADDRESS_ABOVE="Above Address" +ACF_ADDRESS_BELOW="Below Address" +ACF_ADDRESS_DETAILS_CUSTOM="Custom Layout" +ACF_ADDRESS_DETAILS_CUSTOM_DESC="Set the Custom Layout for the address details. Also supports HTML code.

Allowed Smart Tags:
{address.map}
{address.address} - {address.address.label}
{address.latitude} - {address.latitude.label}
{address.longitude} - {address.longitude.label}
{address.country} - {address.country.label}
{address.country_code} - {address.country_code.label}
{address.city} - {address.city.label}
{address.county} - {address.county.label}
{address.postal_code} - {address.postal_code.label}
{address.state} - {address.state.label}
{address.municipality} - {address.municipality.label}
{address.town} - {address.town.label}
{address.road} - {address.road.label}" +ACF_ADDRESS_DETAILS_INFO="Address Details" +ACF_ADDRESS_DETAILS_INFO_DESC="Set which address details to show." +ACF_ADDRESS_AUTOCOMPLETE="Enable Autocomplete" +ACF_ADDRESS_AUTOCOMPLETE_DESC="Set whether to enable address autocomplete and automatically suggest places as you type an address." \ No newline at end of file diff --git a/plugins/fields/acfaddress/language/en-GB/en-GB.plg_fields_acfaddress.sys.ini b/plugins/fields/acfaddress/language/en-GB/en-GB.plg_fields_acfaddress.sys.ini new file mode 100644 index 00000000..d7757e06 --- /dev/null +++ b/plugins/fields/acfaddress/language/en-GB/en-GB.plg_fields_acfaddress.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_ADDRESS="Fields - ACF Address Autocomplete" +ACF_ADDRESS_DESC="Display a map with the location details such as address, city, state, country, etc..." \ No newline at end of file diff --git a/plugins/fields/acfaddress/language/es-ES/es-ES.plg_fields_acfaddress.ini b/plugins/fields/acfaddress/language/es-ES/es-ES.plg_fields_acfaddress.ini new file mode 100644 index 00000000..a784fa27 --- /dev/null +++ b/plugins/fields/acfaddress/language/es-ES/es-ES.plg_fields_acfaddress.ini @@ -0,0 +1,35 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFADDRESS_LABEL="ACF - Dirección Autocompletar" +ACF_ADDRESS="Campos - ACF Dirección Autocompletar" +ACF_ADDRESS_DESC="Muestra un mapa con los detalles de la ubicación, como dirección, ciudad, estado, país, etc." +ACF_ADDRESS_VALUE_DESC="Elija una ubicación del mapa, ingrese una dirección o coordenadas en el campo de búsqueda." +ACF_ADDRESS_ZOOM="Zoom" +ACF_ADDRESS_ZOOM_DESC="Establezca el zoom del mapa." +ACF_ADDRESS_WIDTH="Ancho" +ACF_ADDRESS_WIDTH_DESC="" +ACF_ADDRESS_HEIGHT="Altura" +ACF_ADDRESS_HEIGHT_DESC="" +ACF_ADDRESS_ADDRESS="Dirección" +ACF_ADDRESS_ADDRESS_DESC="Introduzca una dirección o coordenadas para buscar" +ACF_ADDRESS_SHOW_MAP="Mostrar Mapa" +ACF_ADDRESS_SHOW_MAP_DESC="Establezca si mostrar el mapa y dónde.

Back-end:Mostrar el mapa solo al editar el campo en un artículo, contacto, etc...
Front-end:Muestre el mapa solo cuando vea el campo en el front-end de su sitio, como en un artículo, contacto, etc.
Both: Muestre el mapa tanto cuando edite el campo personalizado en un artículo, contacto, etc... como cuando vea el campo en el front-end de su sitio, como cuando vea un artículo." +ACF_ADDRESS_MARKER_IMAGE="Imagen de Marcador" +ACF_ADDRESS_MARKER_IMAGE_DESC="Reemplace la imagen del marcador predeterminado cargando una imagen de su elección." +ACF_ADDRESS_DETAILS_LAYOUT="Disposición" +ACF_ADDRESS_DETAILS_LAYOUT_DESC="Seleccione qué diseño usar, predeterminado o personalizado.

Defecto: Muestra un mapa y debajo todos los detalles de la dirección seleccionada.
Personalizado: Use etiquetas inteligentes para mostrar un diseño personalizado. También admite código HTML." +ACF_ADDRESS_MAP_LOCATION="Ubicación del Mapa" +ACF_ADDRESS_MAP_LOCATION_DESC="Establezca si mostrar el mapa encima o debajo de los detalles de la dirección." +ACF_ADDRESS_ABOVE="Sobre Dirección" +ACF_ADDRESS_BELOW="Bajo Dirección" +ACF_ADDRESS_DETAILS_CUSTOM="Diseño personalizado" +ACF_ADDRESS_DETAILS_CUSTOM_DESC="Configure el diseño personalizado para los detalles de la dirección. También admite código HTML.

Etiquetas inteligentes permitidas:
{address.map}
{address.address} - {address.address.label}
{address.latitude} - {address.latitude.label}
{address.longitude} - {address.longitude.label}
{address.country} - {address.country.label}
{address.country_code} - {address.country_code.label}
{address.city} - {address.city.label}
{address.county} - {address.county.label}
{address.postal_code} - {address.postal_code.label}
{address.state} - {address.state.label}
{address.municipality} - {address.municipality.label}
{address.town} - {address.town.label}
{address.road} - {address.road.label}" +ACF_ADDRESS_DETAILS_INFO="Detalles de Dirección" +ACF_ADDRESS_DETAILS_INFO_DESC="Establezca qué detalles de la dirección mostrar." +ACF_ADDRESS_AUTOCOMPLETE="Autocompletar Dirección" +ACF_ADDRESS_AUTOCOMPLETE_DESC="Establezca si desea habilitar el autocompletado de direcciones y sugerir lugares automáticamente a medida que escribe una dirección." diff --git a/plugins/fields/acfaddress/language/es-ES/es-ES.plg_fields_acfaddress.sys.ini b/plugins/fields/acfaddress/language/es-ES/es-ES.plg_fields_acfaddress.sys.ini new file mode 100644 index 00000000..09893051 --- /dev/null +++ b/plugins/fields/acfaddress/language/es-ES/es-ES.plg_fields_acfaddress.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_ADDRESS="Campos - ACF Dirección Autocompletar" +ACF_ADDRESS_DESC="Muestra un mapa con los detalles de la ubicación, como dirección, ciudad, estado, país, etc." diff --git a/plugins/fields/acfaddress/params/acfaddress.xml b/plugins/fields/acfaddress/params/acfaddress.xml new file mode 100644 index 00000000..b9933ce3 --- /dev/null +++ b/plugins/fields/acfaddress/params/acfaddress.xml @@ -0,0 +1,101 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfaddress/script.install.helper.php b/plugins/fields/acfaddress/script.install.helper.php new file mode 100644 index 00000000..2311cacc --- /dev/null +++ b/plugins/fields/acfaddress/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfaddressInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfaddress/script.install.php b/plugins/fields/acfaddress/script.install.php new file mode 100644 index 00000000..cc51370d --- /dev/null +++ b/plugins/fields/acfaddress/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFAddressInstallerScript extends PlgFieldsACFAddressInstallerScriptHelper +{ + public $alias = 'acfaddress'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfaddress/tmpl/acfaddress.php b/plugins/fields/acfaddress/tmpl/acfaddress.php new file mode 100644 index 00000000..d13f01e0 --- /dev/null +++ b/plugins/fields/acfaddress/tmpl/acfaddress.php @@ -0,0 +1,33 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +$value = $field->value; + +if (is_string($value) && !$value = json_decode($value, true)) +{ + return; +} + +$payload = $value + $fieldParams->flatten(); +$payload['value'] = !empty($payload['address']['latitude']) && !empty($payload['address']['longitude']) ? $payload['address']['latitude'] . ',' . $payload['address']['longitude'] : null; +$payload['show_map'] = $payload['show_map'] === '0' ? false : $payload['show_map']; +$payload['showAddressDetails'] = $fieldParams->get('address_details_info', []); + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('MapAddress', $payload); \ No newline at end of file diff --git a/plugins/fields/acfaddress/version.php b/plugins/fields/acfaddress/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfaddress/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfarticles/acfarticles.php b/plugins/fields/acfarticles/acfarticles.php new file mode 100644 index 00000000..60405195 --- /dev/null +++ b/plugins/fields/acfarticles/acfarticles.php @@ -0,0 +1,461 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Conditions\Component\ContentBase; +use Joomla\CMS\Language\Text; +use Joomla\Registry\Registry; +use Joomla\CMS\Factory; +use NRFramework\Cache; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFArticles extends ACF_Field +{ + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + $contentAssignment = new ContentBase(); + + foreach ($options as $option) + { + if (!$article = $contentAssignment->getItem($option->getLabel())) + { + continue; + } + + $option->setLabel($article->title); + } + + return $options; + } + + + public function onContentBeforeSave($context, $item, $isNew, $data = []) + { + if (!is_array($data)) + { + return true; + } + + if (!isset($data['com_fields'])) + { + return true; + } + + // Create correct context for category + if ($context == 'com_categories.category') + { + $context = $item->get('extension') . '.categories'; + } + + // Load Fields Component Helper class + JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + // Check the context + $parts = FieldsHelper::extract($context, $item); + + if (!$parts) + { + return true; + } + + // Compile the right context for the fields + $context = $parts[0] . '.' . $parts[1]; + + // Loading the fields + $fields = FieldsHelper::getFields($context, $item); + + if (!$fields) + { + return true; + } + + // Get the fields data + $fieldsData = !empty($data['com_fields']) ? $data['com_fields'] : []; + + // Whether we should clean up the temp folder at the end of this process + $should_clean = false; + + // Get the Fields Model + if (!defined('nrJ4')) + { + $model = JModelLegacy::getInstance('Field', 'FieldsModel', ['ignore_request' => true]); + } + else + { + $model = Factory::getApplication()->bootComponent('com_fields')->getMVCFactory()->createModel('Field', 'Administrator', ['ignore_request' => true]); + } + + // Cache subform fields + $subform_fields = []; + + $error = false; + + // Loop over the fields + foreach ($fields as $field) + { + $field_type = $field->type; + + /** + * Check whether a Gallery field is used within the Subform field. + */ + if ($field_type === 'subform') + { + // Ensure it has a value + if (!$subform_value = json_decode($field->value, true)) + { + continue; + } + + foreach ($subform_value as $key => $value) + { + if (!is_array($value)) + { + continue; + } + + foreach ($value as $_key => $_value) + { + // Get Field ID + $field_id = str_replace('field', '', $_key); + + // Get Field by ID + $subform_field = isset($subform_fields[$field_id]) ? $subform_fields[$field_id] : $model->getItem($field_id); + + // Only proceed for this field type + if ($subform_field->type !== $this->_name) + { + continue; + } + + // Cache field + if (!isset($subform_fields[$field_id])) + { + $subform_fields[$field_id] = $subform_field; + } + + $check_value = isset($fieldsData[$field->name][$key][$_key]) ? $fieldsData[$field->name][$key][$_key] : false; + + if (!$check_value) + { + break; + } + + $fieldParams = new Registry($subform_field->fieldparams); + + $check_value = is_array($check_value) ? $check_value : [$check_value]; + + if ($min_articles = (int) $fieldParams->get('min_articles', 0)) + { + if (count($check_value) < $min_articles) + { + Factory::getApplication()->enqueueMessage(sprintf(Text::_('ACF_ARTICLES_MIN_ITEMS_REQUIRED'), $subform_field->title, $min_articles), 'error'); + $error = true; + break; + } + } + + if ($max_articles = (int) $fieldParams->get('max_articles', 0)) + { + if (count($check_value) > $max_articles) + { + Factory::getApplication()->enqueueMessage(sprintf(Text::_('ACF_ARTICLES_MAX_ITEMS_REQUIRED'), $subform_field->title, $max_articles), 'error'); + $error = true; + break; + } + } + } + } + } + else + { + // Only proceed for this field type + if ($field_type !== $this->_name) + { + continue; + } + + // Determine the value if it is available from the data + $value = array_key_exists($field->name, $fieldsData) ? $fieldsData[$field->name] : null; + + if (!$value) + { + continue; + } + + $value = is_array($value) ? $value : [$value]; + + if ($min_articles = (int) $field->fieldparams->get('min_articles', 0)) + { + if (count($value) < $min_articles) + { + Factory::getApplication()->enqueueMessage(sprintf(Text::_('ACF_ARTICLES_MIN_ITEMS_REQUIRED'), $field->title, $min_articles), 'error'); + $error = true; + break; + } + } + + if ($max_articles = (int) $field->fieldparams->get('max_articles', 0)) + { + if (count($value) > $max_articles) + { + Factory::getApplication()->enqueueMessage(sprintf(Text::_('ACF_ARTICLES_MAX_ITEMS_REQUIRED'), $field->title, $max_articles), 'error'); + $error = true; + break; + } + } + } + } + + return !$error; + } + + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $field_type = $field->fieldparams->get('articles_type', 'default'); + + $fieldNode->setAttribute('multiple', true); + + + $max_articles = (int) $field->fieldparams->get('max_articles', 0); + if ($max_articles === 1) + { + $fieldNode->setAttribute('multiple', false); + } + + // Linked + if ($field_type === 'linked') + { + $fieldNode->setAttribute('multiple', false); + $fieldNode->setAttribute('type', 'NRToggle'); + // Default state + $fieldNode->setAttribute('default', 'false'); + + // Checked if Default Value = 1/true + if (isset($field->value) && in_array($field->value, ['1', 'true'])) + { + $fieldNode->setAttribute('checked', 'true'); + } + } + + + return $fieldNode; + } + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $value = array_filter((array) $field->value); + + + $type = $field->fieldparams->get('articles_type', 'default'); + + // Linked articles + if ($type === 'linked') + { + // Abort if no ACF Articles fields are selected + if (!$acf_articles = array_filter($field->fieldparams->get('articles_fields', []))) + { + $field->value = []; + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + // Proceed only if Field Value = 1/true + if (!in_array($field->value, ['1', 'true'])) + { + $field->value = []; + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $cache_key = 'ACFArticles_' . $item->id . '_' . md5(implode(',', $acf_articles)); + + if (Cache::has($cache_key)) + { + $field->value = Cache::get($cache_key); + } + else + { + // Grab all linked articles + $db = Factory::getDbo(); + $query = $db->getQuery(true); + $query->select('a.*') + ->from($db->quoteName('#__fields_values', 'fv')) + ->join('LEFT', $db->quoteName('#__content', 'a') . ' ON a.id = fv.item_id') + ->where($db->quoteName('fv.field_id') . ' IN (' . implode(',', $acf_articles) . ')') + ->where($db->quoteName('fv.value') . ' = ' . $db->q($item->id)); + + $this->prepareArticles($context, $query, $field, $value, true); + + Cache::set($cache_key, $field->value); + } + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + + if (!$value) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $cache_key = 'ACFArticles_Auto_' . $item->id . '_' . md5(implode(',', $value)); + + if (Cache::has($cache_key)) + { + $field->value = Cache::get($cache_key); + } + else + { + // Default articles + $db = Factory::getDbo(); + $query = $db->getQuery(true); + $query->select('a.*') + ->from($db->quoteName('#__content', 'a')) + ->where($db->quoteName('a.id') . ' IN (' . implode(',', array_map('intval', $value)) . ')'); + + $this->prepareArticles($context, $query, $field, $value); + + Cache::set($cache_key, $field->value); + } + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + /** + * Prepares the articles. + * + * @param string $context + * @param object $query + * @param object $field + * @param array $articles + * @param bool $all_filters + * + * @return void + */ + private function prepareArticles($context, $query, &$field, $articles, $all_filters = true) + { + $db = Factory::getDbo(); + + // Filter results + require_once 'fields/acfarticlesfilters.php'; + $payload = $all_filters ? $field->fieldparams : ['order' => $field->fieldparams->get('order')]; + $filters = new ACFArticlesFilters($query, $payload); + $query = $filters->apply(); + + // Set query + $db->setQuery($query); + + // Get articles + if (!$articles = $db->loadAssocList()) + { + $field->value = []; + return; + } + + + // Get and set the articles values + foreach ($articles as &$article) + { + $article['custom_fields'] = $this->fetchCustomFields($article); + } + + + $field->value = $articles; + } + + + /** + * Returns an article's custom fields. + * + * @return array + */ + private function fetchCustomFields($article = null) + { + if (!$article) + { + return []; + } + + $callback = function() use ($article) + { + \JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + if (!$fields = \FieldsHelper::getFields('com_content.article', $article, false)) + { + return []; + } + + $fieldsAssoc = []; + + foreach ($fields as $field) + { + $fieldsAssoc[$field->name] = $field->value; + } + + return $fieldsAssoc; + }; + + return Cache::memo('ACFArticlesFetchCustomFields' . $article['id'], $callback); + } + +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/acfarticles.xml b/plugins/fields/acfarticles/acfarticles.xml new file mode 100644 index 00000000..77483b39 --- /dev/null +++ b/plugins/fields/acfarticles/acfarticles.xml @@ -0,0 +1,26 @@ + + + ACF_ARTICLES + ACF_ARTICLES_DESC + Tassos Marinos + Februray 2023 + Copyright (C) 2023 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfarticles.php + script.install.helper.php + version.php + fields + language + params + tmpl + + + css + img + + diff --git a/plugins/fields/acfarticles/fields/acfarticles.php b/plugins/fields/acfarticles/fields/acfarticles.php new file mode 100644 index 00000000..d48969ff --- /dev/null +++ b/plugins/fields/acfarticles/fields/acfarticles.php @@ -0,0 +1,125 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once JPATH_PLUGINS . '/system/nrframework/helpers/fieldlist.php'; + +use Joomla\Registry\Registry; +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; + +class JFormFieldACFArticles extends NRFormFieldList +{ + public function getInput() + { + $this->hint = $this->getFieldHint(); + + return parent::getInput(); + } + + private function getFieldHint() + { + $hint = (string) $this->element['hint']; + return !empty($hint) ? $hint : Text::_('ACF_ARTICLES_SELECT_ARTICLES'); + } + + /** + * Method to get a list of options for a list input. + * + * @return array + */ + protected function getOptions() + { + require_once 'acfarticlesfilters.php'; + + // The layout param in the field XML overrides $this->layout and thus we need to set it again. + $this->layout = 'joomla.form.field.list-fancy-select'; + + $query = $this->db->getQuery(true) + ->select('a.*, c.lft as category_lft, c.title as category_title') + ->from($this->db->quoteName('#__content', 'a')) + ->join('LEFT', $this->db->quoteName('#__categories', 'c') . ' ON c.id = a.catid'); + + // Get current item id and exclude it from the list + if ($current_item_id = Factory::getApplication()->input->getInt('id')) + { + $query->where($this->db->quoteName('a.id') . ' != ' . (int) $current_item_id); + } + + $field_attributes = (array) $this->element->attributes(); + + $payload = new Registry($field_attributes['@attributes']); + + // Apply filters + $filters = new ACFArticlesFilters($query, $payload); + $query = $filters->apply(); + + $this->db->setQuery($query); + + // Get all articles + if (!$items = $this->db->loadObjectList()) + { + return; + } + + // Get all dropdown choices + $options = []; + + // Add hint to single value field + if ($payload->get('max_articles') == '1') + { + $options[] = HTMLHelper::_('select.option', '', $this->getFieldHint()); + } + + foreach ($items as $item) + { + $options[] = HTMLHelper::_('select.option', $item->id, $item->title); + } + + return $options; + } + + /** + * Return all categories child ids. + * + * @param array $categories + * + * @return array + */ + private function getCategoriesChildIds($categories = []) + { + $query = $this->db->getQuery(true) + ->select('a.id') + ->from($this->db->quoteName('#__categories', 'a')) + ->where('a.extension = ' . $this->db->quote('com_content')) + ->where('a.published = 1'); + + $children = []; + + while (!empty($categories)) + { + $query + ->clear('where') + ->where($this->db->quoteName('a.parent_id') . ' IN (' . implode(',', $categories) . ')'); + + $this->db->setQuery($query); + + $categories = $this->db->loadColumn(); + + $children = array_merge($children, $categories); + } + + return $children; + } +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/fields/acfarticlesfields.php b/plugins/fields/acfarticles/fields/acfarticlesfields.php new file mode 100644 index 00000000..45bb25f6 --- /dev/null +++ b/plugins/fields/acfarticles/fields/acfarticlesfields.php @@ -0,0 +1,70 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once JPATH_PLUGINS . '/system/nrframework/helpers/fieldlist.php'; + +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; + +class JFormFieldACFArticlesFields extends NRFormFieldList +{ + public function getInput() + { + $this->hint = Text::_('ACF_ARTICLES_SELECT_ACF_ARTICLES'); + + return parent::getInput(); + } + + /** + * Method to get a list of options for a list input. + * + * @return array + */ + protected function getOptions() + { + // The layout param in the field XML overrides $this->layout and thus we need to set it again. + $this->layout = 'joomla.form.field.list-fancy-select'; + + $query = $this->db->getQuery(true) + ->select('id, label') + ->from($this->db->quoteName('#__fields')) + ->where('type = ' . $this->db->quote('acfarticles')) + ->where('state = 1'); + + // Get current item id and exclude it from the list + if ($current_item_id = Factory::getApplication()->input->getInt('id')) + { + $query->where($this->db->quoteName('id') . ' != ' . (int) $current_item_id); + } + + $this->db->setQuery($query); + + // Get all fields + if (!$items = $this->db->loadObjectList()) + { + return; + } + + // Get all dropdown choices + $options = []; + + foreach ($items as $item) + { + $options[] = HTMLHelper::_('select.option', $item->id, $item->label); + } + + return $options; + } +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/fields/acfarticlesfilters.php b/plugins/fields/acfarticles/fields/acfarticlesfilters.php new file mode 100644 index 00000000..cffbfee8 --- /dev/null +++ b/plugins/fields/acfarticles/fields/acfarticlesfilters.php @@ -0,0 +1,223 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Factory; + +class ACFArticlesFilters +{ + private $db; + + private $query; + + private $filters; + + public function __construct($query, $filters) + { + $this->db = Factory::getDbo(); + $this->query = $query; + $this->filters = $filters; + } + + public function apply() + { + $this->applyCategories(); + + $this->applyTags(); + $this->applyAuthor(); + + $this->applyStatus(); + $this->applyOrdering(); + + $this->applyLimit(); + + + return $this->query; + } + + public function applyCategories() + { + if (!isset($this->filters['filters_category_enabled'])) + { + return; + } + + if (!isset($this->filters['filters_category_value'])) + { + return; + } + + $categories = $this->filters['filters_category_value']; + + $inc_children = isset($this->filters['filters_category_inc_children']) ? (string) $this->filters['filters_category_inc_children'] : false; + + if (in_array($inc_children, ['1', '2'])) + { + $categories = is_string($categories) ? array_map('trim', explode(',', $categories)) : $categories; + + $children_categories = $this->getCategoriesChildIds($categories); + + // Also include children categories + if ($inc_children === '1') + { + $categories = array_unique(array_filter(array_merge($categories, $children_categories))); + } + // Use only children categories + else + { + $categories = $children_categories; + } + + $categories = implode(',', $categories); + } + + $this->query->where($this->db->quoteName('a.catid') . ' IN (' . (is_string($categories) ? $categories : implode(',', $categories)) . ')'); + } + + + public function applyTags() + { + if (!isset($this->filters['filters_tag_enabled'])) + { + return; + } + + if (!isset($this->filters['filters_tag_value'])) + { + return; + } + + if ($filtersTagEnabled = (string) $this->filters['filters_tag_enabled'] === '1' && $tags = $this->filters['filters_tag_value']) + { + $this->query + ->join('INNER', $this->db->quoteName('#__contentitem_tag_map', 't') . ' ON t.content_item_id = a.id') + ->where($this->db->quoteName('t.tag_id') . ' IN (' . (is_string($tags) ? $tags : implode(',', $tags)) . ')') + ->group($this->db->quoteName('a.id')); + } + } + + public function applyAuthor() + { + if (!isset($this->filters['filters_author_enabled'])) + { + return; + } + + if (!isset($this->filters['filters_author_value'])) + { + return; + } + + if ($filtersAuthorEnabled = (string) $this->filters['filters_author_enabled'] === '1' && $authors = $this->filters['filters_author_value']) + { + $this->query->where($this->db->quoteName('a.created_by') . ' IN (' . (is_string($authors) ? $authors : implode(',', $authors)) . ')'); + } + + } + + + public function applyStatus() + { + // Default status is to show published articles + $status = [1]; + + + if (isset($this->filters['filters_status_value']) && is_array($this->filters['filters_status_value']) && count($this->filters['filters_status_value'])) + { + $status = $this->filters['filters_status_value']; + } + + + // Set articles status + $this->query->where($this->db->quoteName('a.state') . ' IN (' . (is_string($status) ? $status : implode(',', $status)) . ')'); + + } + + private function applyOrdering() + { + // Apply ordering + if (!$this->filters['order']) + { + return; + } + + $order = is_string($this->filters['order']) ? explode(',', $this->filters['order']) : $this->filters['order']; + + $order = array_filter(array_map('trim', $order)); + $orders = []; + + foreach ($order as $item) + { + $lastUnderscorePos = strrpos($item, '_'); + $part1 = substr($item, 0, $lastUnderscorePos); + $part2 = substr($item, $lastUnderscorePos + 1); + + if (!$part1 || !$part2) + { + break; + } + + $orders[] = $part1 . ' ' . $part2; + } + + if ($orders) + { + $this->query->order($this->db->escape(implode(',', $orders))); + } + } + + private function applyLimit() + { + // Apply limit + $limit = isset($this->filters['limit']) ? (int) $this->filters['limit'] : false; + if (!$limit) + { + return; + } + + $this->query->setLimit($limit); + } + + /** + * Return all categories child ids. + * + * @param array $categories + * + * @return array + */ + private function getCategoriesChildIds($categories = []) + { + $query = $this->db->getQuery(true) + ->select('a.id') + ->from($this->db->quoteName('#__categories', 'a')) + ->where('a.extension = ' . $this->db->quote('com_content')) + ->where('a.published = 1'); + + $children = []; + + while (!empty($categories)) + { + $query + ->clear('where') + ->where($this->db->quoteName('a.parent_id') . ' IN (' . implode(',', $categories) . ')'); + + $this->db->setQuery($query); + + $categories = $this->db->loadColumn(); + + $children = array_merge($children, $categories); + } + + return $children; + } +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/language/en-GB/en-GB.plg_fields_acfarticles.ini b/plugins/fields/acfarticles/language/en-GB/en-GB.plg_fields_acfarticles.ini new file mode 100644 index 00000000..00a54ff6 --- /dev/null +++ b/plugins/fields/acfarticles/language/en-GB/en-GB.plg_fields_acfarticles.ini @@ -0,0 +1,78 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2023 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFARTICLES_LABEL="ACF - Articles" +ACF_ARTICLES="Fields - ACF Articles" +ACF_ARTICLES_DESC="Select and connect articles to each other." +ACF_ARTICLES_VALUE_DESC="Select articles to connect to this item." +ACF_ARTICLES_MIN_ARTICLES="Minimum Articles" +ACF_ARTICLES_MIN_ARTICLES_DESC="Sets a limit on the minimum articles that can be selected." +ACF_ARTICLES_MAX_ARTICLES="Maximum Articles" +ACF_ARTICLES_MAX_ARTICLES_DESC="Sets a limit on the maximum articles that can be selected." +ACF_ARTICLES_FILTER_BY_CATEGORY="By Category" +ACF_ARTICLES_FILTER_CATEGORY_DESC="Set whether to filter the articles by categories." +ACF_ARTICLES_FILTER_CATEGORY_SELECTION_DESC="Select categories to filter the articles." +ACF_ARTICLES_FILTER_BY_TAG="By Tag" +ACF_ARTICLES_FILTER_TAG_DESC="Set whether to filter the articles by tags." +ACF_ARTICLES_FILTER_TAG_SELECTION_DESC="Select tags to filter the articles." +ACF_ARTICLES_FILTER_BY_AUTHOR="By Author" +ACF_ARTICLES_FILTER_AUTHOR_DESC="Set whether to filter the articles by authors." +ACF_ARTICLES_FILTER_AUTHOR_SELECTION_DESC="Select authors to filter the articles." +ACF_ARTICLES_LAYOUT_DESC="Select the layout to display the articles." +ACF_ARTICLES_LAYOUT_CUSTOM="Custom Layout" +ACF_ARTICLES_LAYOUT_CUSTOM_DESC="Enter HTML code for each article item. Smart Tags are also supported.

To access an article's data, use the Smart Tag {acf.article.KEY} where KEY is the article object key.

Helpful Smart Tags:
{acf.article.link}: The Article URL
{acf.article.introtext}: The Article intro text
{acf.article.images.image_intro}: The article intro image.
{acf.articles.index}: The article index.
{acf.articles.total}: The number of total articles to show.

For a complete list of Smart Tags, please read the documentation page." +ACF_ARTICLES_ORDER="Order" +ACF_ARTICLES_ORDER_DESC="Select how to order the items." +ACF_ARTICLES_ID_ASC="ID (Asc)" +ACF_ARTICLES_ID_DESC="ID (Desc)" +ACF_ARTICLES_ORDERING_ASC="Ordering (Asc)" +ACF_ARTICLES_ORDERING_DESC="Ordering (Desc)" +ACF_ARTICLES_TITLE_ASC="Title (Asc)" +ACF_ARTICLES_TITLE_DESC="Title (Desc)" +ACF_ARTICLES_ALIAS_ASC="Alias (Asc)" +ACF_ARTICLES_ALIAS_DESC="Alias (Desc)" +ACF_ARTICLES_HITS_ASC="Hits (Asc)" +ACF_ARTICLES_HITS_DESC="Hits (Desc)" +ACF_ARTICLES_CREATED_ASC="Created Date (Asc)" +ACF_ARTICLES_CREATED_DESC="Created Date (Desc)" +ACF_ARTICLES_MODIFIED_ASC="Modified Date (Asc)" +ACF_ARTICLES_MODIFIED_DESC="Modified Date (Desc)" +ACF_ARTICLES_PUBLISH_UP_ASC="Published Date (Asc)" +ACF_ARTICLES_PUBLISH_UP_DESC="Published Date (Desc)" +ACF_ARTICLES_FEATURED_ASC="Featured (Asc)" +ACF_ARTICLES_FEATURED_DESC="Featured (Desc)" +ACF_ARTICLES_CATEGORY_LFT_ASC="Category Order (Asc)" +ACF_ARTICLES_CATEGORY_LFT_DESC="Category Order (Desc)" +ACF_ARTICLES_CATEGORY_TITLE_ASC="Category Title (Asc)" +ACF_ARTICLES_CATEGORY_TITLE_DESC="Category Title (Desc)" +ACF_ARTICLES_SELECT_ARTICLES="Select articles" +ACF_ARTICLES_SELECT_ARTICLE="Select an article" +ACF_ARTICLES_COLUMNS="Columns" +ACF_ARTICLES_COLUMNS_DESC="Select the columns of the layout." +ACF_ARTICLES_GAP="Gap" +ACF_ARTICLES_GAP_DESC="Select the gap between the items." +ACF_ARTICLES_INPUT_FILTERS="Input Filters" +ACF_ARTICLES_INPUT_FILTERS_DESC="These filters are applied to the articles that are displayed on the back-end." +ACF_ARTICLES_INPUT_OPTIONS="Input Options" +ACF_ARTICLES_MIN_ITEMS_REQUIRED="%s: You must select a minimum of %s articles." +ACF_ARTICLES_MAX_ITEMS_REQUIRED="%s: You must select a maximum of %s articles." +ACF_ARTICLES_FILTER_BY_STATUS="By Status" +ACF_ARTICLES_FILTER_STATUS_DESC="Set whether to filter the articles by their status." +ACF_ARTICLES_FILTER_STATUS_SELECTION_DESC="Select statuses to filter the articles." +ACF_ARTICLES_TYPE="Connection Options" +ACF_ARTICLES_TYPE_DESC="Select how the articles will be displayed.

Manual Selection: Choose specific articles manually.

Automatically: Automatically identify related articles by checking if the current article is linked to other ACF Articles fields that use the Manual Selection option.
Note: By default, you must enable this custom field on each article. To automatically make all articles display the linked articles, set Default Value to 1/true." +ACF_ARTICLES_ARTICLES_FIELD="ACF - Articles fields" +ACF_ARTICLES_ARTICLES_FIELD_DESC="In order to automatically find articles, you must select at least one ACF - Articles field, and the current article must be linked to the selected ACF - Articles fields." +ACF_ARTICLES_ARTICLES_LINKED="Linked Articles" +ACF_ARTICLES_ARTICLES_LINKED_DESC="Make ACF - Articles automatically retrive articles that link to this field." +ACF_ARTICLES_MANUAL="Manual Selection" +ACF_ARTICLES_AUTO="Automatic Discovery" +ACF_ARTICLES_SELECT_ACF_ARTICLES="Select ACF - Articles fields" +ACF_ARTICLES_OUTPUT_FILTERS="Output Filters" +ACF_ARTICLES_OUTPUT_FILTERS_DESC="These filters are applied to the articles that are displayed on the front-end." +ACF_ARTICLES_LIMIT_ARTICLES="Limit Articles" +ACF_ARTICLES_LIMIT_ARTICLES_DESC="Define how many linked articles to display. Enter 0 for no limit." \ No newline at end of file diff --git a/plugins/fields/acfarticles/language/en-GB/en-GB.plg_fields_acfarticles.sys.ini b/plugins/fields/acfarticles/language/en-GB/en-GB.plg_fields_acfarticles.sys.ini new file mode 100644 index 00000000..0e0a4807 --- /dev/null +++ b/plugins/fields/acfarticles/language/en-GB/en-GB.plg_fields_acfarticles.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2023 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_ARTICLES="Fields - ACF Articles" +ACF_ARTICLES_DESC="Select and connect articles to each other." \ No newline at end of file diff --git a/plugins/fields/acfarticles/language/es-ES/es-ES.plg_fields_acfarticles.ini b/plugins/fields/acfarticles/language/es-ES/es-ES.plg_fields_acfarticles.ini new file mode 100644 index 00000000..ec6f2e25 --- /dev/null +++ b/plugins/fields/acfarticles/language/es-ES/es-ES.plg_fields_acfarticles.ini @@ -0,0 +1,78 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2023 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFARTICLES_LABEL="ACF - Artículos" +ACF_ARTICLES="Campos - ACF Artículos" +ACF_ARTICLES_DESC="Seleccionar y conectar artículos entre sí." +ACF_ARTICLES_VALUE_DESC="Seleccione artículos para conectarse a este elemento." +ACF_ARTICLES_MIN_ARTICLES="Artículos Mínimos" +ACF_ARTICLES_MIN_ARTICLES_DESC="Establece un límite en los artículos mínimos que se pueden seleccionar." +ACF_ARTICLES_MAX_ARTICLES="Artículos Máximos" +ACF_ARTICLES_MAX_ARTICLES_DESC="Establece un límite en los artículos máximos que se pueden seleccionar." +ACF_ARTICLES_FILTER_BY_CATEGORY="Por Categoría" +ACF_ARTICLES_FILTER_CATEGORY_DESC="Establezca si desea filtrar los artículos por categorías." +ACF_ARTICLES_FILTER_CATEGORY_SELECTION_DESC="Seleccione categorías para filtrar los artículos." +ACF_ARTICLES_FILTER_BY_TAG="Por Etiqueta" +ACF_ARTICLES_FILTER_TAG_DESC="Establezca si desea filtrar los artículos por etiquetas." +ACF_ARTICLES_FILTER_TAG_SELECTION_DESC="Seleccione etiquetas para filtrar los artículos." +ACF_ARTICLES_FILTER_BY_AUTHOR="Por Autor" +ACF_ARTICLES_FILTER_AUTHOR_DESC="Establezca si desea filtrar los artículos por autor." +ACF_ARTICLES_FILTER_AUTHOR_SELECTION_DESC="Seleccione autores para filtrar los artículos." +ACF_ARTICLES_LAYOUT_DESC="Seleccione el diseño para mostrar los artículos." +ACF_ARTICLES_LAYOUT_CUSTOM="Diseño Personalizado" +ACF_ARTICLES_LAYOUT_CUSTOM_DESC="Ingrese el código HTML para cada elemento del artículo. También se admiten etiquetas inteligentes.

Para acceder a los datos de un artículo, utilice la etiqueta inteligente {acf.article.KEY} donde KEY es la clave del objeto del artículo.

Etiquetas inteligentes útiles:
{acf.article.link}: la URL del artículo
{acf.article .introtext}: el texto de introducción del artículo
{acf.article.images.image_intro}: la imagen de introducción del artículo.
{acf.articles.index}: el índice del artículo.
{acf.articles.total}: el número total de artículos que se mostrarán.

Para obtener una lista completa de etiquetas inteligentes, lea la página de documentación." +ACF_ARTICLES_ORDER="Orden" +ACF_ARTICLES_ORDER_DESC="Seleccione cómo ordenar los artículos." +ACF_ARTICLES_ID_ASC="ID (Asc)" +ACF_ARTICLES_ID_DESC="ID (Desc)" +ACF_ARTICLES_ORDERING_ASC="Ordenar (Asc)" +ACF_ARTICLES_ORDERING_DESC="Ordenar (Desc)" +ACF_ARTICLES_TITLE_ASC="Título (Asc)" +ACF_ARTICLES_TITLE_DESC="Título (Desc)" +ACF_ARTICLES_ALIAS_ASC="Alias (Asc)" +ACF_ARTICLES_ALIAS_DESC="Alias (Desc)" +ACF_ARTICLES_HITS_ASC="Golpes (Asc)" +ACF_ARTICLES_HITS_DESC="Golpes (Desc)" +ACF_ARTICLES_CREATED_ASC="Fecha de Creación (Asc)" +ACF_ARTICLES_CREATED_DESC="Fecha de Creación (Desc)" +ACF_ARTICLES_MODIFIED_ASC="Fecha de Modificación (Asc)" +ACF_ARTICLES_MODIFIED_DESC="Fecha de Modificación (Desc)" +ACF_ARTICLES_PUBLISH_UP_ASC="Fecha de Publicación (Asc)" +ACF_ARTICLES_PUBLISH_UP_DESC="Fecha de Publicación (Desc)" +ACF_ARTICLES_FEATURED_ASC="Características (Asc)" +ACF_ARTICLES_FEATURED_DESC="Características (Desc)" +ACF_ARTICLES_CATEGORY_LFT_ASC="Orden de Categoría (Asc)" +ACF_ARTICLES_CATEGORY_LFT_DESC="Orden de Categoría (Desc)" +ACF_ARTICLES_CATEGORY_TITLE_ASC="Título de Categoría (Asc)" +ACF_ARTICLES_CATEGORY_TITLE_DESC="Título de Categoría (Desc)" +ACF_ARTICLES_SELECT_ARTICLES="Seleccionar artículos" +ACF_ARTICLES_SELECT_ARTICLE="Seleccionar un artículo" +ACF_ARTICLES_COLUMNS="Columnas" +ACF_ARTICLES_COLUMNS_DESC="Seleccione las columnas del diseño." +ACF_ARTICLES_GAP="Espaciado" +ACF_ARTICLES_GAP_DESC="Seleccione el espacio entre los elementos." +ACF_ARTICLES_INPUT_FILTERS="Filtros de Entrada" +ACF_ARTICLES_INPUT_FILTERS_DESC="Estos filtros se aplican a los artículos que se muestran en el back-end." +ACF_ARTICLES_INPUT_OPTIONS="Opciones de Entrada" +ACF_ARTICLES_MIN_ITEMS_REQUIRED="%s: Debe seleccionar un mínimo de %s artículos." +ACF_ARTICLES_MAX_ITEMS_REQUIRED="%s: Debe seleccionar un máximo de %s artículos." +ACF_ARTICLES_FILTER_BY_STATUS="Por Estado" +ACF_ARTICLES_FILTER_STATUS_DESC="Establezca si desea filtrar los artículos por estados." +ACF_ARTICLES_FILTER_STATUS_SELECTION_DESC="Seleccione estados para filtrar los artículos." +ACF_ARTICLES_TYPE="Opciones de conexión" +ACF_ARTICLES_TYPE_DESC="Seleccione cómo se mostrarán los artículos.

Selección manual: Elija artículos específicos manualmente.

Automáticamente: Identifique automáticamente los artículos relacionados comprobando si el artículo actual está vinculado a otros campos de artículos ACF que utilizan la opción de selección manual.
Nota: De forma predeterminada, debe habilitar este campo personalizado en cada artículo. Para hacer que todos los artículos muestren automáticamente los artículos vinculados, establezca el Valor predeterminado en 1/true." +ACF_ARTICLES_ARTICLES_FIELD="ACF - Campos de artículos" +ACF_ARTICLES_ARTICLES_FIELD_DESC="Para encontrar automáticamente artículos, debes seleccionar al menos un ACF - Campo de Artículos, y el artículo actual debe estar vinculado a ACF - Campos de Artículos." +ACF_ARTICLES_ARTICLES_LINKED="Artículos vinculados" +ACF_ARTICLES_ARTICLES_LINKED_DESC="Hacer que AC - Artículos recupere automáticamente los artículos que estan vinculados con este campo." +ACF_ARTICLES_MANUAL="Selección Manual" +ACF_ARTICLES_AUTO="Descubrimiento automático" +ACF_ARTICLES_SELECT_ACF_ARTICLES="Seleccionar ACF - Campos de artículos" +ACF_ARTICLES_OUTPUT_FILTERS="Filtros de salida" +ACF_ARTICLES_OUTPUT_FILTERS_DESC="Estos filtros se aplican a los artículos que se muestran en el front-end." +ACF_ARTICLES_LIMIT_ARTICLES="Limitar Artículos" +ACF_ARTICLES_LIMIT_ARTICLES_DESC="Define cuántos artículos vinculados mostrar. Introduce 0 para no tener límite." diff --git a/plugins/fields/acfarticles/language/es-ES/es-ES.plg_fields_acfarticles.sys.ini b/plugins/fields/acfarticles/language/es-ES/es-ES.plg_fields_acfarticles.sys.ini new file mode 100644 index 00000000..d32cf8b6 --- /dev/null +++ b/plugins/fields/acfarticles/language/es-ES/es-ES.plg_fields_acfarticles.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2023 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_ARTICLES="Campos - ACF Articulos" +ACF_ARTICLES_DESC="Seleccionar y conectar artículos entre sí." diff --git a/plugins/fields/acfarticles/params/acfarticles.xml b/plugins/fields/acfarticles/params/acfarticles.xml new file mode 100644 index 00000000..34dfe4de --- /dev/null +++ b/plugins/fields/acfarticles/params/acfarticles.xml @@ -0,0 +1,213 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfarticles/script.install.helper.php b/plugins/fields/acfarticles/script.install.helper.php new file mode 100644 index 00000000..e8c75ae1 --- /dev/null +++ b/plugins/fields/acfarticles/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfarticlesInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfarticles/script.install.php b/plugins/fields/acfarticles/script.install.php new file mode 100644 index 00000000..e0686e97 --- /dev/null +++ b/plugins/fields/acfarticles/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFArticlesInstallerScript extends PlgFieldsACFArticlesInstallerScriptHelper +{ + public $alias = 'acfarticles'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfarticles/tmpl/acfarticles.php b/plugins/fields/acfarticles/tmpl/acfarticles.php new file mode 100644 index 00000000..10e5c26a --- /dev/null +++ b/plugins/fields/acfarticles/tmpl/acfarticles.php @@ -0,0 +1,86 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +if (!$articles = $field->value) +{ + return; +} + +$routerHelper = defined('nrJ4') ? 'Joomla\Component\Content\Site\Helper\RouteHelper' : 'ContentHelperRoute'; + +// Get the layout +$layout = $fieldParams->get('layout', 'media/plg_fields_acfarticles/img/alist.svg'); +$layout = str_replace(['media/plg_fields_acfarticles/img/', '.svg'], '', $layout); +$layout = ltrim($layout, 'a'); +$customLayout = $fieldParams->get('custom_layout', ''); + +if ($layout === 'custom' && !$customLayout) +{ + return; +} + +$id = 'acf_articles_' . $item->id . '_' . $field->id; + +// Set the wrapper classes +$classes = []; +$classes[] = $id; +$classes[] = 'layout-' . $layout; + +if (in_array($layout, ['stylea', 'styleb'])) +{ + $classes[] = 'layout-grid'; +} + +// Set columns and gap +if (in_array($layout, ['stylea', 'styleb'])) +{ + // Get columns and gap + $columns = $fieldParams->get('devices_columns.columns', []); + $gap = $fieldParams->get('devices_gap.gap', []); + + Factory::getDocument()->addStyleDeclaration(' + .acfarticles-field-wrapper.' . $id . ' { + --columns: ' . $columns['desktop'] . '; + --gap: ' . $gap['desktop'] . 'px; + } + + @media only screen and (max-width: 991px) { + .acfarticles-field-wrapper.' . $id . ' { + --columns: ' . $columns['tablet'] . '; + --gap: ' . $gap['tablet'] . 'px; + } + } + + @media only screen and (max-width: 575px) { + .acfarticles-field-wrapper.' . $id . ' { + --columns: ' . $columns['mobile'] . '; + --gap: ' . $gap['mobile'] . 'px; + } + } + '); +} + +$html = '
'; + +$path = __DIR__ . '/layouts/' . $layout . '.php'; +if (file_exists($path)) +{ + require $path; +} + +$html .= '
'; + +echo $html; \ No newline at end of file diff --git a/plugins/fields/acfarticles/tmpl/layouts/custom.php b/plugins/fields/acfarticles/tmpl/layouts/custom.php new file mode 100644 index 00000000..39a084e9 --- /dev/null +++ b/plugins/fields/acfarticles/tmpl/layouts/custom.php @@ -0,0 +1,39 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\HTML\HTMLHelper; + +$customLayout = HTMLHelper::_('content.prepare', $customLayout); + +$st = new \NRFramework\SmartTags(); + +// Add total articles +$st->add(['total' => count($articles)], 'acf.articles.'); + +// Set the pattern +$pattern = '/\{(acf\.)?article\.([^}]+)\}/'; + +foreach ($articles as $index => $article) +{ + // Add article index + $st->add(['index' => $index + 1], 'acf.articles.'); + + // Set the replacement + $replacement = '{article.$2 --id=' . $article['id'] . ' --prepareCustomFields=false}'; + + // Get updated layout + $tmpCustomLayout = preg_replace($pattern, $replacement, $customLayout); + + $html .= $st->replace($tmpCustomLayout); +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/tmpl/layouts/index.php b/plugins/fields/acfarticles/tmpl/layouts/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/fields/acfarticles/tmpl/layouts/list.php b/plugins/fields/acfarticles/tmpl/layouts/list.php new file mode 100644 index 00000000..08be6860 --- /dev/null +++ b/plugins/fields/acfarticles/tmpl/layouts/list.php @@ -0,0 +1,24 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Router\Route; + +$html .= ''; \ No newline at end of file diff --git a/plugins/fields/acfarticles/tmpl/layouts/stylea.php b/plugins/fields/acfarticles/tmpl/layouts/stylea.php new file mode 100644 index 00000000..cd94842f --- /dev/null +++ b/plugins/fields/acfarticles/tmpl/layouts/stylea.php @@ -0,0 +1,47 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Router\Route; +use Joomla\CMS\HTML\HTMLHelper; + +if (defined('nrJ4')) +{ + $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); + $wa->registerAndUseStyle('acf_articles_style', 'plg_fields_acfarticles/style.css'); +} +else +{ + HTMLHelper::stylesheet('plg_fields_acfarticles/style.css', ['relative' => true, 'version' => 'auto']); +} + +foreach ($articles as $article) +{ + $image = ''; + if (isset($article['images']) && $image = json_decode($article['images'])) + { + $image = $image->image_intro ?: $image->image_fulltext; + } + + $html .= '
'; + + if ($image) + { + $html .= '' . $article['title'] . ''; + } + + $html .= '' . $article['title'] . ''; + + $html .= '
'; +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/tmpl/layouts/styleb.php b/plugins/fields/acfarticles/tmpl/layouts/styleb.php new file mode 100644 index 00000000..39cdc93d --- /dev/null +++ b/plugins/fields/acfarticles/tmpl/layouts/styleb.php @@ -0,0 +1,56 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Router\Route; +use Joomla\CMS\HTML\HTMLHelper; + +if (defined('nrJ4')) +{ + $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); + $wa->registerAndUseStyle('acf_articles_style', 'plg_fields_acfarticles/style.css'); +} +else +{ + HTMLHelper::stylesheet('plg_fields_acfarticles/style.css', ['relative' => true, 'version' => 'auto']); +} + +foreach ($articles as $article) +{ + $image = ''; + if (isset($article['images']) && $image = json_decode($article['images'])) + { + $image = $image->image_intro ?: $image->image_fulltext; + } + + $html .= '
'; + + if ($image) + { + $html .= '' . $article['title'] . ''; + } + + $html .= '
'; + + $html .= '' . $article['title'] . ''; + + // Get the article excerpt + if ($excerpt = substr(strip_tags($article['introtext']), 0, 200)) + { + // Add the excerpt + $html .= '
' . $excerpt . '...
'; + } + + $html .= '
'; +} \ No newline at end of file diff --git a/plugins/fields/acfarticles/version.php b/plugins/fields/acfarticles/version.php new file mode 100644 index 00000000..43cd7412 --- /dev/null +++ b/plugins/fields/acfarticles/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfchainedfields/acfchainedfields.php b/plugins/fields/acfchainedfields/acfchainedfields.php new file mode 100644 index 00000000..f347b339 --- /dev/null +++ b/plugins/fields/acfchainedfields/acfchainedfields.php @@ -0,0 +1,81 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFChainedFields extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'NRChainedFields'; + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $data_source = $field->fieldparams->get('data_source', 'custom'); + + $dataset = ''; + switch ($data_source) + { + case 'custom': + $dataset = $field->fieldparams->get('data_source_custom', ''); + break; + + case 'csv_file': + $csv_file = $field->fieldparams->get('data_source_csv', ''); + + if (!file_exists($csv_file)) + { + return $parent; + } + + $dataset = $csv_file; + break; + } + + if (!$choices = \NRFramework\Helpers\ChainedFields::loadCSV($dataset, $data_source, $field->fieldparams->get('separator'), $field->name . '_', $field->name)) + { + return $parent; + } + + $field->choices = $choices; + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } +} diff --git a/plugins/fields/acfchainedfields/acfchainedfields.xml b/plugins/fields/acfchainedfields/acfchainedfields.xml new file mode 100644 index 00000000..c7ba1a35 --- /dev/null +++ b/plugins/fields/acfchainedfields/acfchainedfields.xml @@ -0,0 +1,21 @@ + + + ACF_CHAINEDFIELDS + ACF_CHAINEDFIELDS_DESC + Tassos Marinos + November 2022 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfchainedfields.php + script.install.helper.php + version.php + language + params + tmpl + + diff --git a/plugins/fields/acfchainedfields/language/en-GB/en-GB.plg_fields_acfchainedfields.ini b/plugins/fields/acfchainedfields/language/en-GB/en-GB.plg_fields_acfchainedfields.ini new file mode 100644 index 00000000..b96f8544 --- /dev/null +++ b/plugins/fields/acfchainedfields/language/en-GB/en-GB.plg_fields_acfchainedfields.ini @@ -0,0 +1,24 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCHAINEDFIELDS_LABEL="ACF - Chained Fields" +ACF_CHAINEDFIELDS_VALUE_DESC="Select a value from a dropdown to reveal the next available options." +ACF_CHAINEDFIELDS="Fields - ACF Chained Fields" +ACF_CHAINEDFIELDS_DESC="Create select fields that dynamically change based on the previous selection." +ACF_CHAINEDFIELDS_DATA_SOURCE="Data Source" +ACF_CHAINEDFIELDS_DATA_SOURCE_DESC="Select the data source. You can either enter the expected format via text or upload a CSV file." +ACF_CHAINEDFIELDS_FROM_CSV_FILE="From CSV File" +ACF_CHAINEDFIELDS_DATA_SOURCE_CUSTOM="Custom Data Source" +ACF_CHAINEDFIELDS_DATA_SOURCE_CUSTOM_DESC="Enter the data source source manually. Format:
Year,Make,Model
2015,Audi,A3
2016,BMW,X3
2017,FIAT,500" +ACF_CHAINEDFIELDS_CSV="CSV Data Source" +ACF_CHAINEDFIELDS_CSV_DESC="Upload a CSV file to import your choices." +ACF_CHAINEDFIELDS_CSV_SEPARATOR="CSV Separator" +ACF_CHAINEDFIELDS_CSV_SEPARATOR_DESC="Enter the separator used in the CSV file." +ACF_CHAINEDFIELDS_LAYOUT="Layout" +ACF_CHAINEDFIELDS_LAYOUT_DESC="Select the layout that will be used to display the field." +ACF_CHAINEDFIELDS_CUSTOM_LAYOUT="Custom Layout" +ACF_CHAINEDFIELDS_CUSTOM_LAYOUT_DESC="Enter the custom layout.
You can use the following Smart Tags for each dropdown fields to retrieve their values: {field.1.value} where 1 is the first dropdown field which will return its value.
Similarly, you can use the Smart Tag {field.1.label} to retrieve each dropdown label.

Replace 1 with 2 for the second dropdown field, etc..." \ No newline at end of file diff --git a/plugins/fields/acfchainedfields/language/en-GB/en-GB.plg_fields_acfchainedfields.sys.ini b/plugins/fields/acfchainedfields/language/en-GB/en-GB.plg_fields_acfchainedfields.sys.ini new file mode 100644 index 00000000..6a107d3f --- /dev/null +++ b/plugins/fields/acfchainedfields/language/en-GB/en-GB.plg_fields_acfchainedfields.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_CHAINEDFIELDS="Fields - ACF Chained Fields" +ACF_CHAINEDFIELDS_DESC="Create select fields that dynamically change based on the previous selection." \ No newline at end of file diff --git a/plugins/fields/acfchainedfields/language/es-ES/es-ES.plg_fields_acfchainedfields.ini b/plugins/fields/acfchainedfields/language/es-ES/es-ES.plg_fields_acfchainedfields.ini new file mode 100644 index 00000000..758a119b --- /dev/null +++ b/plugins/fields/acfchainedfields/language/es-ES/es-ES.plg_fields_acfchainedfields.ini @@ -0,0 +1,24 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCHAINEDFIELDS_LABEL="ACF - Campos Encadenados" +ACF_CHAINEDFIELDS_VALUE_DESC="Seleccione un valor del menú desplegable para revelar las siguientes opciones disponibles." +ACF_CHAINEDFIELDS="Campos - ACF Campos Encadenados" +ACF_CHAINEDFIELDS_DESC="Cree campos de selección que cambien dinámicamente en función de la selección anterior." +ACF_CHAINEDFIELDS_DATA_SOURCE="Fuente de datos" +ACF_CHAINEDFIELDS_DATA_SOURCE_DESC="Seleccione la fuente de datos. Puede ingresar el formato esperado a través de texto o cargar un archivo CSV." +ACF_CHAINEDFIELDS_FROM_CSV_FILE="Desde archivo CSV" +ACF_CHAINEDFIELDS_DATA_SOURCE_CUSTOM="Fuente de datos personalizada" +ACF_CHAINEDFIELDS_DATA_SOURCE_CUSTOM_DESC="Ingrese la fuente de origen de datos manualmente. Formato:
Año, Marca, Modelo
2015,Audi,A3
2016,BMW,X3
2017,FIAT,500" +ACF_CHAINEDFIELDS_CSV="Fuente de datos CSV" +ACF_CHAINEDFIELDS_CSV_DESC="Cargue un archivo CSV para importar sus opciones." +ACF_CHAINEDFIELDS_CSV_SEPARATOR="Separador CSV" +ACF_CHAINEDFIELDS_CSV_SEPARATOR_DESC="Introduzca el separador utilizado en el archivo CSV." +ACF_CHAINEDFIELDS_LAYOUT="Disposición" +ACF_CHAINEDFIELDS_LAYOUT_DESC="Seleccione el diseño que se utilizará para mostrar el campo." +ACF_CHAINEDFIELDS_CUSTOM_LAYOUT="Diseño personalizado" +ACF_CHAINEDFIELDS_CUSTOM_LAYOUT_DESC="Introduzca el diseño personalizado.
Puede usar las siguientes etiquetas inteligentes para cada campo desplegable para recuperar sus valores: {field.1.value} donde 1 es el primer campo desplegable que devolverá su valor.
De manera similar, puede usar la etiqueta inteligente {field.1.label} para recuperar cada etiqueta desplegable.

Reemplace 1 con 2 para el segundo campo desplegable, etc." diff --git a/plugins/fields/acfchainedfields/language/es-ES/es-ES.plg_fields_acfchainedfields.sys.ini b/plugins/fields/acfchainedfields/language/es-ES/es-ES.plg_fields_acfchainedfields.sys.ini new file mode 100644 index 00000000..bb963095 --- /dev/null +++ b/plugins/fields/acfchainedfields/language/es-ES/es-ES.plg_fields_acfchainedfields.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_CHAINEDFIELDS="Campos - ACF Campos Encadenados" +ACF_CHAINEDFIELDS_DESC="Cree campos de selección que cambien dinámicamente en función de la selección anterior." diff --git a/plugins/fields/acfchainedfields/params/acfchainedfields.xml b/plugins/fields/acfchainedfields/params/acfchainedfields.xml new file mode 100644 index 00000000..7b863cdf --- /dev/null +++ b/plugins/fields/acfchainedfields/params/acfchainedfields.xml @@ -0,0 +1,54 @@ + +
+ +
+ + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfchainedfields/script.install.helper.php b/plugins/fields/acfchainedfields/script.install.helper.php new file mode 100644 index 00000000..14a73a0b --- /dev/null +++ b/plugins/fields/acfchainedfields/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfchainedfieldsInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfchainedfields/script.install.php b/plugins/fields/acfchainedfields/script.install.php new file mode 100644 index 00000000..e8e13d9a --- /dev/null +++ b/plugins/fields/acfchainedfields/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFChainedFieldsInstallerScript extends PlgFieldsACFChainedFieldsInstallerScriptHelper +{ + public $alias = 'acfchainedfields'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfchainedfields/tmpl/acfchainedfields.php b/plugins/fields/acfchainedfields/tmpl/acfchainedfields.php new file mode 100644 index 00000000..a2c570a1 --- /dev/null +++ b/plugins/fields/acfchainedfields/tmpl/acfchainedfields.php @@ -0,0 +1,78 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$value = $field->value) +{ + return; +} + +$value = array_values($value); + +$layout = $fieldParams->get('layout', 'default'); + +// Default layout +if ($layout === 'default') +{ + ?>
$_value) + { + if (!$_value) + { + continue; + } + + $label = isset($field->choices['inputs'][$key]) ? $field->choices['inputs'][$key]['label'] : ''; + ?> +
+ : +
+ +
+ get('custom_layout', null)) + { + return; + } + + // Make index start from 1 + array_unshift($value, ''); + unset($value[0]); + + // Create Smart Tags instance + $st = new \NRFramework\SmartTags(); + + $values = []; + $labels = []; + + foreach ($value as $key => $_value) + { + $label_value = isset($field->choices['inputs'][$key - 1]) ? $field->choices['inputs'][$key - 1]['label'] : ''; + + $labels[$key . '.label'] = $label_value; + $values[$key . '.value'] = $_value; + } + $st->add($labels, 'field.'); + + // Add values to Smart Tags + $st->add($values, 'field.'); + + // Replace Smart Tags + echo $st->replace($layout); +} \ No newline at end of file diff --git a/plugins/fields/acfchainedfields/version.php b/plugins/fields/acfchainedfields/version.php new file mode 100644 index 00000000..8e294844 --- /dev/null +++ b/plugins/fields/acfchainedfields/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfconvertforms/acfconvertforms.php b/plugins/fields/acfconvertforms/acfconvertforms.php new file mode 100644 index 00000000..a2ada54c --- /dev/null +++ b/plugins/fields/acfconvertforms/acfconvertforms.php @@ -0,0 +1,89 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFConvertForms extends ACF_Field +{ + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + if (!class_exists('\ConvertForms\Form')) + { + return $options; + } + + foreach ($options as $option) + { + if (!$form = ConvertForms\Form::load($option->getLabel())) + { + continue; + } + + $option->setLabel($form['name']); + } + + return $options; + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return; + } + + $form->addFieldPath(JPATH_ADMINISTRATOR . '/components/com_convertforms/models/forms/fields'); + + $fieldNode->setAttribute('type', 'convertforms'); + + $option = new \DOMElement('option'); + $option->textContent = htmlspecialchars('- ' . Text::_('JSELECT') . ' -', ENT_COMPAT, 'UTF-8'); + $element = $fieldNode->appendChild($option); + + return $fieldNode; + } +} diff --git a/plugins/fields/acfconvertforms/acfconvertforms.xml b/plugins/fields/acfconvertforms/acfconvertforms.xml new file mode 100644 index 00000000..8bb27958 --- /dev/null +++ b/plugins/fields/acfconvertforms/acfconvertforms.xml @@ -0,0 +1,20 @@ + + + ACF_CONVERTFORMS + ACF_CONVERTFORMS_DESC + Tassos Marinos + Sep 2022 + Copyright (C) 2017 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfconvertforms.php + script.install.helper.php + version.php + tmpl + language + + diff --git a/plugins/fields/acfconvertforms/language/en-GB/en-GB.plg_fields_acfconvertforms.ini b/plugins/fields/acfconvertforms/language/en-GB/en-GB.plg_fields_acfconvertforms.ini new file mode 100644 index 00000000..aba9606c --- /dev/null +++ b/plugins/fields/acfconvertforms/language/en-GB/en-GB.plg_fields_acfconvertforms.ini @@ -0,0 +1,4 @@ +PLG_FIELDS_ACFCONVERTFORMS_LABEL="ACF - Convert Forms" +ACF_CONVERTFORMS="Fields - ACF Convert Forms" +ACF_CONVERTFORMS_DESC="Render a form created with Convert Forms" +ACF_CONVERTFORMS_VALUE_DESC="Select a form to render" \ No newline at end of file diff --git a/plugins/fields/acfconvertforms/language/en-GB/en-GB.plg_fields_acfconvertforms.sys.ini b/plugins/fields/acfconvertforms/language/en-GB/en-GB.plg_fields_acfconvertforms.sys.ini new file mode 100644 index 00000000..be7154da --- /dev/null +++ b/plugins/fields/acfconvertforms/language/en-GB/en-GB.plg_fields_acfconvertforms.sys.ini @@ -0,0 +1,2 @@ +ACF_CONVERTFORMS="Fields - ACF Convert Forms" +ACF_CONVERTFORMS_DESC="Render a form created with Convert Forms" \ No newline at end of file diff --git a/plugins/fields/acfconvertforms/language/es-ES/es-ES.plg_fields_acfconvertforms.ini b/plugins/fields/acfconvertforms/language/es-ES/es-ES.plg_fields_acfconvertforms.ini new file mode 100644 index 00000000..011bebc2 --- /dev/null +++ b/plugins/fields/acfconvertforms/language/es-ES/es-ES.plg_fields_acfconvertforms.ini @@ -0,0 +1,4 @@ +PLG_FIELDS_ACFCONVERTFORMS_LABEL="ACF - Convert Forms" +ACF_CONVERTFORMS="Campos - ACF Convert Forms" +ACF_CONVERTFORMS_DESC="Renderizar un formulario creado con Convert Forms" +ACF_CONVERTFORMS_VALUE_DESC="Seleccione un formulario para renderizar" diff --git a/plugins/fields/acfconvertforms/language/es-ES/es-ES.plg_fields_acfconvertforms.sys.ini b/plugins/fields/acfconvertforms/language/es-ES/es-ES.plg_fields_acfconvertforms.sys.ini new file mode 100644 index 00000000..28b62c3e --- /dev/null +++ b/plugins/fields/acfconvertforms/language/es-ES/es-ES.plg_fields_acfconvertforms.sys.ini @@ -0,0 +1,2 @@ +ACF_CONVERTFORMS="Campos - ACF Conversor de Formularios" +ACF_CONVERTFORMS_DESC="Renderizar un formulario creado con Convert Forms" diff --git a/plugins/fields/acfconvertforms/script.install.helper.php b/plugins/fields/acfconvertforms/script.install.helper.php new file mode 100644 index 00000000..e045b6da --- /dev/null +++ b/plugins/fields/acfconvertforms/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfconvertformsInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfconvertforms/script.install.php b/plugins/fields/acfconvertforms/script.install.php new file mode 100644 index 00000000..cc99c345 --- /dev/null +++ b/plugins/fields/acfconvertforms/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFConvertFormsInstallerScript extends PlgFieldsACFConvertFormsInstallerScriptHelper +{ + public $alias = 'acfconvertforms'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfconvertforms/tmpl/acfconvertforms.php b/plugins/fields/acfconvertforms/tmpl/acfconvertforms.php new file mode 100644 index 00000000..2c3dab68 --- /dev/null +++ b/plugins/fields/acfconvertforms/tmpl/acfconvertforms.php @@ -0,0 +1,18 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if ($id = $field->value) +{ + echo ConvertForms\Helper::renderFormById($id); +} diff --git a/plugins/fields/acfconvertforms/version.php b/plugins/fields/acfconvertforms/version.php new file mode 100644 index 00000000..05780d52 --- /dev/null +++ b/plugins/fields/acfconvertforms/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfcountdown/acfcountdown.php b/plugins/fields/acfcountdown/acfcountdown.php new file mode 100644 index 00000000..2e42001f --- /dev/null +++ b/plugins/fields/acfcountdown/acfcountdown.php @@ -0,0 +1,92 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFCountdown extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'Countdown'; + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + */ + public function onContentPrepareForm(Form $form, $data) + { + $data = (object) $data; + + // Make sure we are manipulating the right field. + if (isset($data->type) && $data->type != $this->_name) + { + return; + } + + /** + * Set the configuration for the presets. + * + * These are handed over to Javascript and + * whenever we click on a preset, these values + * are set to each setting on the backend. + */ + if (Factory::getApplication()->isClient('administrator')) + { + Text::script('ACF_FIELD_PREVIEWER'); + Text::script('ACF_FIELD_PREVIEWER_INFO_ICON_TITLE'); + Text::script('NR_AND_LC'); + Text::script('NR_DAYS'); + Text::script('NR_HOURS'); + Text::script('NR_MINUTES'); + Text::script('NR_SECONDS'); + + // Include presets + include 'fields/helper.php'; + + $script = 'window.ACFCountdownPresetsData = ' . json_encode($presets) . ';'; + Factory::getDocument()->addScriptDeclaration($script); + HTMLHelper::script('plg_system_nrframework/tffieldsvaluesapplier.js', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_fields_acfcountdown/countdown.js', ['relative' => true, 'version' => 'auto']); + + $script = 'window.ACFFieldsPreviewerData = ' . json_encode([ + 'fullscreenActions' => true, + 'responsiveControls' => true + ]) . ';'; + Factory::getDocument()->addScriptDeclaration($script); + HTMLHelper::script('plg_fields_acfcountdown/previewer.js', ['relative' => true, 'version' => 'auto']); + } + + + return parent::onContentPrepareForm($form, $data); + } +} diff --git a/plugins/fields/acfcountdown/acfcountdown.xml b/plugins/fields/acfcountdown/acfcountdown.xml new file mode 100644 index 00000000..8d431fd8 --- /dev/null +++ b/plugins/fields/acfcountdown/acfcountdown.xml @@ -0,0 +1,26 @@ + + + ACF_COUNTDOWN + ACF_COUNTDOWN_DESC + Tassos Marinos + December 2022 + Copyright (C) 2021 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfcountdown.php + script.install.helper.php + version.php + fields + language + params + tmpl + + + js + img + + diff --git a/plugins/fields/acfcountdown/fields/countdown.php b/plugins/fields/acfcountdown/fields/countdown.php new file mode 100644 index 00000000..185135b7 --- /dev/null +++ b/plugins/fields/acfcountdown/fields/countdown.php @@ -0,0 +1,120 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Form\Form; + +class JFormFieldCountdown extends FormField +{ + /** + * Method to attach a JForm object to the field. + * + * @param SimpleXMLElement $element The SimpleXMLElement object representing the tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. + * + * @return boolean True on success. + * + * @since 3.6 + */ + public function setup(SimpleXMLElement $element, $value, $group = null) + { + if (!parent::setup($element, $value, $group)) + { + return false; + } + + // Value must be an array + if (is_string($this->value) && $value = json_decode($this->value, true)) + { + $this->value = json_decode($this->value, true); + } + + return true; + } + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + */ + protected function getInput() + { + $countdown_type = (string) $this->element['countdown_type']; + + $static_fields = ' + '; + + $evergreen_fields = ' + + + + + + + '; + + $form_source = new SimpleXMLElement(' +
+
+ ' . ($countdown_type === 'static' ? $static_fields : $evergreen_fields) . ' +
+
+ '); + + $control = $this->name; + $formname = 'acfcountdown.' . str_replace(['jform[', '[', ']'], ['', '.', ''], $control); + + $form = Form::getInstance($formname, $form_source->asXML(), ['control' => $control]); + $form->bind($this->value); + + return '
' . $form->renderFieldset('acfcountdown') . '
'; + } +} \ No newline at end of file diff --git a/plugins/fields/acfcountdown/fields/helper.php b/plugins/fields/acfcountdown/fields/helper.php new file mode 100644 index 00000000..ba36f7ea --- /dev/null +++ b/plugins/fields/acfcountdown/fields/helper.php @@ -0,0 +1,516 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +$presets = [ + 1 => [ + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => 25, 'mobile' => ''] + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 15, 'tablet' => 13, 'mobile' => ''] + ], + 'gap' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 10, 'tablet' => 0, 'mobile' => ''] + ], + 'minutes_label' => [ + 'type' => 'text', + 'value' => Text::_('ACF_COUNTDOWN_MINS') + ], + 'seconds_label' => [ + 'type' => 'text', + 'value' => Text::_('ACF_COUNTDOWN_SECS') + ], + 'digits_wrapper_custom_width' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'digits_wrapper_min_width' => [ + 'type' => 'number', + 'value' => 50 + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 400 + ], + 'unit_label_margin_top' => [ + 'type' => 'number', + 'value' => 9 + ] + ], + 2 => [ + 'item_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => 74 + ], + '[border][style]' => [ + 'type' => 'list', + 'value' => 'solid' + ], + '[border][width]' => [ + 'type' => 'number', + 'value' => 2 + ], + '[border][color]' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'item_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 9] + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => '', 'mobile' => ''] + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 12, 'tablet' => '', 'mobile' => ''] + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ] + ], + 3 => [ + 'days_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('NR_DAYS')) + ], + 'hours_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('NR_HOURS')) + ], + 'minutes_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('ACF_COUNTDOWN_MINS')) + ], + 'seconds_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('ACF_COUNTDOWN_SECS')) + ], + 'item_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => 84 + ], + '[border][style]' => [ + 'type' => 'list', + 'value' => 'solid' + ], + '[border][width]' => [ + 'type' => 'number', + 'value' => 2 + ], + '[border][color]' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'item_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 100] + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => '', 'mobile' => ''] + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 12, 'tablet' => '', 'mobile' => ''] + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ] + ], + 4 => [ + 'item_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => 74 + ], + 'item_background_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'item_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 9] + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => '', 'mobile' => ''] + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#fff' + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 12, 'tablet' => '', 'mobile' => ''] + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#d2d5ee' + ] + ], + 5 => [ + 'days' => [ + 'type' => 'nrtoggle', + 'value' => false + ], + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'gap' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 5] + ], + 'digits_gap' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 3] + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => '', 'mobile' => ''] + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#fff' + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 15, 'tablet' => 13, 'mobile' => ''] + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 400 + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'unit_label_margin_top' => [ + 'type' => 'number', + 'value' => 8 + ], + 'digits_custom_width' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'digits_min_width' => [ + 'type' => 'number', + 'value' => 39 + ], + 'digits_padding' => [ + 'responsive' => true, + 'dimensions' => true, + 'type' => 'number', + 'value' => ['desktop' => ['top' => 9, 'right' => 5, 'bottom' => 9, 'left' => 5]] + ], + 'digit_background_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'digits_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 9] + ] + ], + 6 => [ + 'minutes_label' => [ + 'type' => 'text', + 'value' => Text::_('ACF_COUNTDOWN_MINS') + ], + 'seconds_label' => [ + 'type' => 'text', + 'value' => Text::_('ACF_COUNTDOWN_SECS') + ], + 'digits_wrapper_custom_width' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'digits_wrapper_min_width' => [ + 'type' => 'number', + 'value' => 59 + ], + 'digits_wrapper_padding' => [ + 'responsive' => true, + 'dimensions' => true, + 'type' => 'number', + 'value' => ['desktop' => 9] + ], + 'digits_wrapper_background_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#fff' + ], + 'digits_wrapper_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 5] + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => 25, 'mobile' => ''] + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 15, 'tablet' => 13, 'mobile' => ''] + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 400 + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'unit_label_margin_top' => [ + 'type' => 'number', + 'value' => 8 + ] + ], + 7 => [ + 'days_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('NR_DAYS')) + ], + 'hours_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('NR_HOURS')) + ], + 'minutes_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('ACF_COUNTDOWN_MINS')) + ], + 'seconds_label' => [ + 'type' => 'text', + 'value' => strtoupper(Text::_('ACF_COUNTDOWN_SECS')) + ], + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'item_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => 90 + ], + 'item_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 7] + ], + 'item_background_color' => [ + 'type' => 'text', + 'value' => '#f6f6f6' + ], + 'gap' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 5] + ], + 'digits_gap' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 3] + ], + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 30, 'tablet' => 25, 'mobile' => ''] + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ], + 'digit_text_color' => [ + 'type' => 'text', + 'value' => '#fff' + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 12, 'tablet' => '', 'mobile' => ''] + ], + 'label_font_weight' => [ + 'type' => 'list', + 'value' => 600 + ], + 'unit_label_text_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'unit_label_margin_top' => [ + 'type' => 'number', + 'value' => 9 + ], + 'digits_custom_width' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'digits_min_width' => [ + 'type' => 'number', + 'value' => 30 + ], + 'digits_padding' => [ + 'responsive' => true, + 'dimensions' => true, + 'type' => 'number', + 'value' => ['desktop' => 5] + ], + 'digit_background_color' => [ + 'type' => 'text', + 'value' => '#41495b' + ], + 'digits_border_radius' => [ + 'responsive' => true, + 'dimensions' => true, + 'border_radius' => true, + 'type' => 'number', + 'value' => ['desktop' => 7] + ] + ], + 8 => [ + 'digits_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 16, 'tablet' => '', 'mobile' => ''] + ], + 'label_font_size' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => ['desktop' => 16, 'tablet' => '', 'mobile' => ''] + ], + 'gap' => [ + 'responsive' => true, + 'type' => 'number', + 'value' => '' + ], + 'days_label' => [ + 'type' => 'text', + 'value' => ' ' . strtolower(Text::_('NR_DAYS')) + ], + 'hours_label' => [ + 'type' => 'text', + 'value' => ' ' . strtolower(Text::_('NR_HOURS')) + ], + 'minutes_label' => [ + 'type' => 'text', + 'value' => ' ' . strtolower(Text::_('NR_MINUTES')) + ], + 'seconds_label' => [ + 'type' => 'text', + 'value' => ' ' . strtolower(Text::_('NR_SECONDS')) + ], + 'digits_font_weight' => [ + 'type' => 'list', + 'value' => 500 + ] + ] +]; \ No newline at end of file diff --git a/plugins/fields/acfcountdown/language/en-GB/en-GB.plg_fields_acfcountdown.ini b/plugins/fields/acfcountdown/language/en-GB/en-GB.plg_fields_acfcountdown.ini new file mode 100644 index 00000000..89342790 --- /dev/null +++ b/plugins/fields/acfcountdown/language/en-GB/en-GB.plg_fields_acfcountdown.ini @@ -0,0 +1,108 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTDOWN_LABEL="ACF - Countdown" +ACF_COUNTDOWN="Fields - ACF Countdown" +ACF_COUNTDOWN_VALUE_DESC="Set the countdown date, or dynamic days/hours/minutes/seconds." +ACF_COUNTDOWN_DESC="Use a countdown timer to create anticipation and scarcity around upcoming events, creating the feeling of scarcity, anticipation, and drive more sales." +ACF_COUNTDOWN_SHOW_MESSAGE="Show message" +ACF_COUNTDOWN_SHOW_MESSAGE_DESC="Set the message that will appear once the timer ends." +ACF_COUNTDOWN_FINISH_TEXT_DESC="Set the message that will appear once the countdown ends." +ACF_COUNTDOWN_FINISH_TEXT_HINT="Countdown finished." +ACF_COUNTDOWN_CUSTOM_FORMAT="Custom Format" +ACF_COUNTDOWN_CUSTOM_FORMAT_DESC="Using the Custom Format option, you can write your own HTML to produce a countdown that fits your needs.
Available Tags:

• {days}
• {hours}
• {minutes}
• {seconds}

Example: {minutes}m:{seconds}s will produce a countdown with output: 35m:28s" +ACF_COUNTDOWN_CUSTOM_FORMAT_HINT="{days} days, {hours} hours, {minutes} minutes and {seconds} seconds" +ACF_COUNTDOWN_COUNTDOWN_TIMER="Timer" +ACF_COUNTDOWN_ACTION="On Countdown Expire" +ACF_COUNTDOWN_ACTION_DESC="Select the countdown end action.

No action: The countdown will remain once finished.
Hide timer: The countdown will disappear once it ends.
Show message: Show a message once the countdown ends.
Redirect to URL: Redirect to a URL once the timer ends." +ACF_COUNTDOWN_ACTION_NO_ACTION="No action" +ACF_COUNTDOWN_ACTION_HIDE_TIMER="Hide timer" +ACF_COUNTDOWN_ACTION_REDIRECT="Redirect to URL" +ACF_COUNTDOWN_REDIRECT_URL="Redirect URL" +ACF_COUNTDOWN_REDIRECT_URL_DESC="Set the URL to redirect the user once the countdown ends." +ACF_COUNTDOWN_REDIRECT_URL_HINT="https://" +ACF_COUNTDOWN_PRE_MADE_TEMPLATE="Pre-made Template" +ACF_COUNTDOWN_TEMPLATE="Template" +ACF_COUNTDOWN_TEMPLATE_DESC="Select the countdown template to use. Once you select a template, the settings below will be updated based on the selected template." +ACF_COUNTDOWN_UNIT_DISPLAY="Unit Display" +ACF_COUNTDOWN_DAYS_LABEL="Show Days" +ACF_COUNTDOWN_DAYS="Days" +ACF_COUNTDOWN_DAYS_DESC="Set whether to display the days." +ACF_COUNTDOWN_DAYS_LABEL="Days Label" +ACF_COUNTDOWN_DAYS_LABEL_DESC="Set the days label." +ACF_COUNTDOWN_HOURS_LABEL="Show Hours" +ACF_COUNTDOWN_HOURS="Hours" +ACF_COUNTDOWN_HOURS_DESC="Set whether to display the hours." +ACF_COUNTDOWN_HOURS_LABEL="Hours Label" +ACF_COUNTDOWN_HOURS_LABEL_DESC="Set the hours label." +ACF_COUNTDOWN_MINUTES_LABEL="Show Minutes" +ACF_COUNTDOWN_MINUTES="Minutes" +ACF_COUNTDOWN_MINUTES_DESC="Set whether to display the minutes." +ACF_COUNTDOWN_MINUTES_LABEL="Minutes Label" +ACF_COUNTDOWN_MINUTES_LABEL_DESC="Set the minutes label." +ACF_COUNTDOWN_SECONDS_LABEL="Show Seconds" +ACF_COUNTDOWN_SECONDS="Seconds" +ACF_COUNTDOWN_SECONDS_DESC="Set whether to display the seconds." +ACF_COUNTDOWN_SECONDS_LABEL="Seconds Label" +ACF_COUNTDOWN_SECONDS_LABEL_DESC="Set the seconds label." +ACF_COUNTDOWN_TYPE="Type" +ACF_COUNTDOWN_TYPE_DESC="Select the Contdown type.

Fixed: Counts down to a specific date and time. Universal deadline for all visitors.
Evergreen: Set-and-forget solution. The countdown starts when your visitor sees the offer. The countdown will also restart upon ending." +ACF_COUNTDOWN_TYPE_FIXED="Fixed Date" +ACF_COUNTDOWN_TYPE_EVERGREEN="Evergreen" +ACF_COUNTDOWN_TIMEZONE="Date Timezone" +ACF_COUNTDOWN_TIMEZONE_DESC="Select which timezone will be used." +ACF_COUNTDOWN_TIMEZONE_SERVER="Use Joomla Timezone" +ACF_COUNTDOWN_TIMEZONE_CLIENT="Use Visitor's Timezone" +ACF_COUNTDOWN_THEME_SETTINGS="Theme Settings" +ACF_COUNTDOWN_ITEM_BORDER_COLOR="Item Border Color" +ACF_COUNTDOWN_ITEM_BORDER_COLOR_DESC="Set the border color around each countdown item." +ACF_COUNTDOWN_ENABLE_SEPARATOR="Enable Separator" +ACF_COUNTDOWN_ENABLE_SEPARATOR_DESC="Set to display a colon \":\" separator." +ACF_COUNTDOWN_DOUBLE_ZEROES_FORMAT="Enable 00 Number Format" +ACF_COUNTDOWN_DOUBLE_ZEROES_FORMAT_DESC="Set whether to display the numbers in 0 or 00 format." +ACF_COUNTDOWN_UNIT_ITEM="Unit Item" +ACF_COUNTDOWN_ITEM_SIZE="Size" +ACF_COUNTDOWN_ITEM_SIZE_DESC="Sets the item's width and height in pixels. Leave empty for auto width." +ACF_COUNTDOWN_TEMPLATE_SELECTOR_DESC="Select whether to use a pre-made template or a custom template" +ACF_COUNTDOWN_TEMPLATE_SELECTOR="Template selector" +ACF_COUNTDOWN_GAP="Gap" +ACF_COUNTDOWN_GAP_DESC="Set the space between unit items." +ACF_COUNTDOWN_BACKGROUND_COLOR="Background Color" +ACF_COUNTDOWN_ITEM_BACKGROUND_COLOR_DESC="Set the item background color." +ACF_COUNTDOWN_UNIT_ITEM_BORDER_CONTROL_DESC="Set the border for the unit item (style, color and width)." +ACF_COUNTDOWN_UNIT_DIGITS_CONTAINER="Unit Digits Container" +ACF_COUNTDOWN_UNIT_DIGIT="Unit Digit" +ACF_COUNTDOWN_UNIT_LABEL="Unit Label" +ACF_COUNTDOWN_ITEM_BORDER_RADIUS_DESC="Set the item border radius." +ACF_COUNTDOWN_DIGITS_CONTAINER_PADDING_DESC="Set the digits container padding." +ACF_COUNTDOWN_DIGITS_CONTAINER_CUSTOM_WIDTH="Digits Container Custom Width" +ACF_COUNTDOWN_DIGITS_CONTAINER_CUSTOM_WIDTH_DESC="Set the digits container minimum width.

Enable to set a custom width.
Disable for auto width." +ACF_COUNTDOWN_DIGITS_CONTAINER_BACKGROUND_COLOR_DESC="Set the digits container background color." +ACF_COUNTDOWN_DIGITS_FONT_SIZE_DESC="Set the font size of the digits." +ACF_COUNTDOWN_DIGITS_FONT_WEIGHT="Font Weight" +ACF_COUNTDOWN_DIGITS_FONT_WEIGHT_DESC="Set the font weight of the digits." +ACF_COUNTDOWN_DIGITS_CUSTOM_WIDTH="Digits Custom Width" +ACF_COUNTDOWN_DIGITS_CUSTOM_WIDTH_DESC="Set the digits minimum width.

Enable to set a custom width.
Disable for auto width." +ACF_COUNTDOWN_DIGITS_PADDING_DESC="Set the padding for each digit." +ACF_COUNTDOWN_DIGITS_CONTAINER_BORDER_RADIUS_DESC="Set the border radius for the digits container." +ACF_COUNTDOWN_DIGITS_BORDER_RADIUS_DESC="Set the border radius for each digit." +ACF_COUNTDOWN_DIGITS_GAP_DESC="Set the gap between the digits." +ACF_COUNTDOWN_DIGIT_BACKGROUND_COLOR_DESC="Set the background color of each digit." +ACF_COUNTDOWN_TEXT_COLOR="Text Color" +ACF_COUNTDOWN_DIGIT_TEXT_COLOR_DESC="Set the text color of each digit." +ACF_COUNTDOWN_LABEL_FONT_SIZE_DESC="Set the font size of the labels." +ACF_COUNTDOWN_LABEL_FONT_WEIGHT="Label Weight" +ACF_COUNTDOWN_LABEL_FONT_WEIGHT_DESC="Set the font weight of the labels." +ACF_COUNTDOWN_UNIT_LABEL_COLOR_DESC="Set the text color of the labels appearing below each countdown digit." +ACF_COUNTDOWN_UNIT_LABEL_MARGIN_TOP="Space between label and digits" +ACF_COUNTDOWN_UNIT_LABEL_MARGIN_TOP_DESC="Set the spacing between the label and the countdown digits." +ACF_COUNTDOWN_MINS="Mins" +ACF_COUNTDOWN_SECS="Secs" +ACF_COUNTDOWN_SHOW_ADVANCED_SETTINGS="Show Advanced Settings" +ACF_COUNTDOWN_SHOW_ADVANCED_SETTINGS_DESC="Enable to show advanced settings." +ACF_COUNTDOWN_ITEM_PADDING="Item Padding" +ACF_COUNTDOWN_ITEM_PADDING_DESC="Set the item's padding." \ No newline at end of file diff --git a/plugins/fields/acfcountdown/language/en-GB/en-GB.plg_fields_acfcountdown.sys.ini b/plugins/fields/acfcountdown/language/en-GB/en-GB.plg_fields_acfcountdown.sys.ini new file mode 100644 index 00000000..6346d5f0 --- /dev/null +++ b/plugins/fields/acfcountdown/language/en-GB/en-GB.plg_fields_acfcountdown.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_COUNTDOWN="Fields - ACF Countdown" +ACF_COUNTDOWN_DESC="Use a countdown timer to create anticipation and scarcity around upcoming events, creating the feeling of scarcity, anticipation, and drive more sales." \ No newline at end of file diff --git a/plugins/fields/acfcountdown/language/es-ES/es-ES.plg_fields_acfcountdown.ini b/plugins/fields/acfcountdown/language/es-ES/es-ES.plg_fields_acfcountdown.ini new file mode 100644 index 00000000..e03dac29 --- /dev/null +++ b/plugins/fields/acfcountdown/language/es-ES/es-ES.plg_fields_acfcountdown.ini @@ -0,0 +1,108 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTDOWN_LABEL="ACF - Cuenta Atras" +ACF_COUNTDOWN="Campos - ACF Cuenta Atras" +ACF_COUNTDOWN_VALUE_DESC="Establezca la fecha de la cuenta regresiva o en dinámico los días/horas/minutos/segundos." +ACF_COUNTDOWN_DESC="Use un temporizador de cuenta regresiva para crear anticipación y escasez en torno a los próximos eventos, creando la sensación de escasez, anticipación e impulsando más ventas." +ACF_COUNTDOWN_SHOW_MESSAGE="Mostrar mensaje" +ACF_COUNTDOWN_SHOW_MESSAGE_DESC="Configure el mensaje que aparecerá una vez que finalice el temporizador." +ACF_COUNTDOWN_FINISH_TEXT_DESC="Configure el mensaje que aparecerá una vez finalice la cuenta atrás." +ACF_COUNTDOWN_FINISH_TEXT_HINT="Cuenta regresiva finalizada." +ACF_COUNTDOWN_CUSTOM_FORMAT="Formato Personalizado" +ACF_COUNTDOWN_CUSTOM_FORMAT_DESC="Usando la opción de Formato Personalizado, puede escribir su propio HTML para producir una cuenta regresiva que se ajuste a sus necesidades.
Etiquetas Disponibles:

• {days}
• {hours}
• {minutes}
• {seconds}

Ejemplo: {minutes}m:{seconds}s producirá una cuenta regresiva con salida: 35m:28s" +ACF_COUNTDOWN_CUSTOM_FORMAT_HINT="{days} días, {hours} horas, {minutes} minutos y {seconds} segundos" +ACF_COUNTDOWN_COUNTDOWN_TIMER="Temporizador" +ACF_COUNTDOWN_ACTION="Expirar una Cuenta Atras" +ACF_COUNTDOWN_ACTION_DESC="Seleccione la acción final de la cuenta atras.

Sin acción: La cuenta atrás permanecerá una vez finalizada.
Ocultar temporizador: La cuenta atras desaparecerá una vez que finalice.
Mostrar mensaje: Muestra un mensaje una vez que finaliza la cuenta atras.
Redirigir a una URL: Redirige a una URL una vez que finaliza el temporizador." +ACF_COUNTDOWN_ACTION_NO_ACTION="Sin acción" +ACF_COUNTDOWN_ACTION_HIDE_TIMER="Ocultar temporizador" +ACF_COUNTDOWN_ACTION_REDIRECT="Redirigir a URL" +ACF_COUNTDOWN_REDIRECT_URL="Redireccionar URL" +ACF_COUNTDOWN_REDIRECT_URL_DESC="Configure la URL para redirigir al usuario una vez que finalice la cuenta atras." +ACF_COUNTDOWN_REDIRECT_URL_HINT="https://" +ACF_COUNTDOWN_PRE_MADE_TEMPLATE="Plantilla Prefabricada" +ACF_COUNTDOWN_TEMPLATE="Plantilla" +ACF_COUNTDOWN_TEMPLATE_DESC="Seleccione la plantilla de cuenta atrasa que desea usar. Una vez que seleccione una plantilla, la configuración a continuación se actualizará en función de la plantilla seleccionada." +ACF_COUNTDOWN_UNIT_DISPLAY="Pantalla de unidad" +ACF_COUNTDOWN_DAYS_LABEL="Mostrar Días" +ACF_COUNTDOWN_DAYS="Días" +ACF_COUNTDOWN_DAYS_DESC="Establezca si mostrar los días." +ACF_COUNTDOWN_DAYS_LABEL="Mostrar Días" +ACF_COUNTDOWN_DAYS_LABEL_DESC="Establecer la etiqueta de días." +ACF_COUNTDOWN_HOURS_LABEL="Mostrar Horas" +ACF_COUNTDOWN_HOURS="Horas" +ACF_COUNTDOWN_HOURS_DESC="Establezca si mostrar las horas." +ACF_COUNTDOWN_HOURS_LABEL="Mostrar Horas" +ACF_COUNTDOWN_HOURS_LABEL_DESC="Establecer la etiqueta de horas." +ACF_COUNTDOWN_MINUTES_LABEL="Mostrar Minutos" +ACF_COUNTDOWN_MINUTES="Minutos" +ACF_COUNTDOWN_MINUTES_DESC="Establezca si mostrar minutos." +ACF_COUNTDOWN_MINUTES_LABEL="Mostrar Minutos" +ACF_COUNTDOWN_MINUTES_LABEL_DESC="Establecer la etiqueta de minutos." +ACF_COUNTDOWN_SECONDS_LABEL="Mostrar Segundos" +ACF_COUNTDOWN_SECONDS="Segundos" +ACF_COUNTDOWN_SECONDS_DESC="Establezca si mostrar segundos" +ACF_COUNTDOWN_SECONDS_LABEL="Mostrar Segundos" +ACF_COUNTDOWN_SECONDS_LABEL_DESC="Establecer la etiqueta de segundos." +ACF_COUNTDOWN_TYPE="Tipo" +ACF_COUNTDOWN_TYPE_DESC="Seleccione el tipo de Cuenta Atras.

Fijo: Cuenta atrás hasta una fecha y hora específicas. Plazo universal para todos los visitantes.
Perenne: Solución de configurar y olvidar. La cuenta regresiva comienza cuando su visitante ve la oferta. La cuenta regresiva también se reiniciará al finalizar." +ACF_COUNTDOWN_TYPE_FIXED="Fecha Fijada" +ACF_COUNTDOWN_TYPE_EVERGREEN="Perenne" +ACF_COUNTDOWN_TIMEZONE="Zona Horaria" +ACF_COUNTDOWN_TIMEZONE_DESC="Seleccione qué zona horaria se utilizará." +ACF_COUNTDOWN_TIMEZONE_SERVER="Usar la zona horaria de Joomla" +ACF_COUNTDOWN_TIMEZONE_CLIENT="Usar la zona horaria del visitante" +ACF_COUNTDOWN_THEME_SETTINGS="Ajustes del Tema" +ACF_COUNTDOWN_ITEM_BORDER_COLOR="Color del borde del artículo" +ACF_COUNTDOWN_ITEM_BORDER_COLOR_DESC="Establezca el color del borde alrededor de cada elemento de la cuenta atras." +ACF_COUNTDOWN_ENABLE_SEPARATOR="Activar Separador" +ACF_COUNTDOWN_ENABLE_SEPARATOR_DESC="Configure para mostrar un separador de dos puntos \\\":\\\"." +ACF_COUNTDOWN_DOUBLE_ZEROES_FORMAT="Habilitar Formato de Número 00" +ACF_COUNTDOWN_DOUBLE_ZEROES_FORMAT_DESC="Establezca si mostrar los números en formato 0 o 00." +ACF_COUNTDOWN_UNIT_ITEM="Unidad del Elemento" +ACF_COUNTDOWN_ITEM_SIZE="Tamaño" +ACF_COUNTDOWN_ITEM_SIZE_DESC="Establece el ancho y la altura del elemento en píxeles. Deje vacío para ancho automático." +ACF_COUNTDOWN_TEMPLATE_SELECTOR_DESC="Seleccione si desea utilizar una plantilla prefabricada o una plantilla personalizada" +ACF_COUNTDOWN_TEMPLATE_SELECTOR="Selector de Plantilla" +ACF_COUNTDOWN_GAP="Espaciado" +ACF_COUNTDOWN_GAP_DESC="Establezca el espacio entre los elementos de la unidad." +ACF_COUNTDOWN_BACKGROUND_COLOR="Color de Fondo" +ACF_COUNTDOWN_ITEM_BACKGROUND_COLOR_DESC="Establezca el color de fondo del elemento" +ACF_COUNTDOWN_UNIT_ITEM_BORDER_CONTROL_DESC="Establezca el borde para el elemento de la unidad (estilo, color y ancho)." +ACF_COUNTDOWN_UNIT_DIGITS_CONTAINER="Contenedor de Unidad de Dígitos" +ACF_COUNTDOWN_UNIT_DIGIT="Unidad de Digitos" +ACF_COUNTDOWN_UNIT_LABEL="Unidad de Etiqueta" +ACF_COUNTDOWN_ITEM_BORDER_RADIUS_DESC="Establezca el radio del borde del elemento." +ACF_COUNTDOWN_DIGITS_CONTAINER_PADDING_DESC="Establezca el relleno del contenedor de dígitos." +ACF_COUNTDOWN_DIGITS_CONTAINER_CUSTOM_WIDTH="Ancho personalizado del contenedor de dígitos" +ACF_COUNTDOWN_DIGITS_CONTAINER_CUSTOM_WIDTH_DESC="Establezca el ancho mínimo del contenedor de dígitos.

Activadopara establecer un ancho personalizado.
Desactivado Para tamaño automático." +ACF_COUNTDOWN_DIGITS_CONTAINER_BACKGROUND_COLOR_DESC="Establezca el color de fondo del contenedor de dígitos." +ACF_COUNTDOWN_DIGITS_FONT_SIZE_DESC="Establezca el tamaño de la fuente de los dígitos." +ACF_COUNTDOWN_DIGITS_FONT_WEIGHT="Peso de fuente" +ACF_COUNTDOWN_DIGITS_FONT_WEIGHT_DESC="Establezca el peso de la fuente de los dígitos." +ACF_COUNTDOWN_DIGITS_CUSTOM_WIDTH="Ancho Personalizado de Dígitos" +ACF_COUNTDOWN_DIGITS_CUSTOM_WIDTH_DESC="Configure el ancho mínimo de los dígitos.

Activado para establecer un ancho personalizado.
Desactivado para tamaño automático." +ACF_COUNTDOWN_DIGITS_PADDING_DESC="Establecer el relleno para cada dígito." +ACF_COUNTDOWN_DIGITS_CONTAINER_BORDER_RADIUS_DESC="Establezca el radio del borde para el contenedor de dígitos." +ACF_COUNTDOWN_DIGITS_BORDER_RADIUS_DESC="Establezca el radio del borde para cada dígito." +ACF_COUNTDOWN_DIGITS_GAP_DESC="Establezca el espacio entre los dígitos." +ACF_COUNTDOWN_DIGIT_BACKGROUND_COLOR_DESC="Establezca el color de fondo de cada dígito." +ACF_COUNTDOWN_TEXT_COLOR="Color del Texto" +ACF_COUNTDOWN_DIGIT_TEXT_COLOR_DESC="Establece el color del texto de cada dígito." +ACF_COUNTDOWN_LABEL_FONT_SIZE_DESC="Establezca el tamaño de fuente de las etiquetas." +ACF_COUNTDOWN_LABEL_FONT_WEIGHT="Peso de la Etiqueta" +ACF_COUNTDOWN_LABEL_FONT_WEIGHT_DESC="Establezca el peso de la fuente de las etiquetas." +ACF_COUNTDOWN_UNIT_LABEL_COLOR_DESC="Establezca el color del texto de las etiquetas que aparecen debajo de cada dígito de la cuenta regresiva." +ACF_COUNTDOWN_UNIT_LABEL_MARGIN_TOP="Espacio entre la etiqueta y los dígitos" +ACF_COUNTDOWN_UNIT_LABEL_MARGIN_TOP_DESC="Configure el espacio entre la etiqueta y los dígitos de la cuenta atras." +ACF_COUNTDOWN_MINS="Minutos" +ACF_COUNTDOWN_SECS="Segundos" +ACF_COUNTDOWN_SHOW_ADVANCED_SETTINGS="Mostrar Ajustes Avanzados" +ACF_COUNTDOWN_SHOW_ADVANCED_SETTINGS_DESC="Habilite para mostrar la configuración avanzada." +ACF_COUNTDOWN_ITEM_PADDING="Relleno de Artículos" +ACF_COUNTDOWN_ITEM_PADDING_DESC="Establezca el relleno del elemento." diff --git a/plugins/fields/acfcountdown/language/es-ES/es-ES.plg_fields_acfcountdown.sys.ini b/plugins/fields/acfcountdown/language/es-ES/es-ES.plg_fields_acfcountdown.sys.ini new file mode 100644 index 00000000..ed077bad --- /dev/null +++ b/plugins/fields/acfcountdown/language/es-ES/es-ES.plg_fields_acfcountdown.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_COUNTDOWN="Campos - ACF Cuenta Atras" +ACF_COUNTDOWN_DESC="Use un temporizador de cuenta regresiva para crear anticipación y escasez en torno a los próximos eventos, creando la sensación de escasez, anticipación e impulsando más ventas." diff --git a/plugins/fields/acfcountdown/params/acfcountdown.xml b/plugins/fields/acfcountdown/params/acfcountdown.xml new file mode 100644 index 00000000..6cb49101 --- /dev/null +++ b/plugins/fields/acfcountdown/params/acfcountdown.xml @@ -0,0 +1,417 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfcountdown/script.install.helper.php b/plugins/fields/acfcountdown/script.install.helper.php new file mode 100644 index 00000000..1914f94c --- /dev/null +++ b/plugins/fields/acfcountdown/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfcountdownInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfcountdown/script.install.php b/plugins/fields/acfcountdown/script.install.php new file mode 100644 index 00000000..0abc6877 --- /dev/null +++ b/plugins/fields/acfcountdown/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFCountdownInstallerScript extends PlgFieldsACFCountdownInstallerScriptHelper +{ + public $alias = 'acfcountdown'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfcountdown/tmpl/acfcountdown.php b/plugins/fields/acfcountdown/tmpl/acfcountdown.php new file mode 100644 index 00000000..2a77c8d3 --- /dev/null +++ b/plugins/fields/acfcountdown/tmpl/acfcountdown.php @@ -0,0 +1,110 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$value = $field->value) +{ + return; +} + +if (is_string($value) && !$value = json_decode($value, true)) +{ + return; +} + +$countdown_action = $fieldParams->get('action', 'keep'); +$countdown_type = $fieldParams->get('countdown_type', 'static'); + +// Evergreen provides extra actions, allow to override the action on item editing page +if ($countdown_type === 'evergreen') +{ + $countdown_action = 'restart'; +} + +$preset_source = $fieldParams->get('preset_source', 'preset'); +$preset = $fieldParams->get('preset', '1'); + +// Determine theme +$theme = $preset_source === 'custom' ? 'custom' : ($preset === '8' ? 'oneline' : 'default'); + +$payload = [ + // Field values + 'countdown_type' => $countdown_type, + 'value' => isset($value['value']) ? $value['value'] : null, + 'timezone' => $fieldParams->get('timezone', 'server'), + 'dynamic_days' => isset($value['dynamic_days']) ? $value['dynamic_days'] : null, + 'dynamic_hours' => isset($value['dynamic_hours']) ? $value['dynamic_hours'] : null, + 'dynamic_minutes' => isset($value['dynamic_minutes']) ? $value['dynamic_minutes'] : null, + 'dynamic_seconds' => isset($value['dynamic_seconds']) ? $value['dynamic_seconds'] : null, + + // Countdown End Action + 'finish_text' => $fieldParams->get('finish_text', ''), + 'redirect_url' => $fieldParams->get('redirect_url', ''), + 'countdown_action' => $countdown_action, + + // Preset + 'theme' => $theme, + 'format' => $fieldParams->get('format', ''), + + // Unit Display + 'days' => $fieldParams->get('days') === '1', + 'days_label' => $fieldParams->get('days_label'), + 'hours' => $fieldParams->get('hours') === '1', + 'hours_label' => $fieldParams->get('hours_label'), + 'minutes' => $fieldParams->get('minutes') === '1', + 'minutes_label' => $fieldParams->get('minutes_label'), + 'seconds' => $fieldParams->get('seconds') === '1', + 'seconds_label' => $fieldParams->get('seconds_label'), + 'separator' => $fieldParams->get('separator') === '1', + 'double_zeroes_format' => $fieldParams->get('double_zeroes_format') === '1', + + // Unit Item + 'item_size' => $fieldParams->get('item_size_responsive.item_size'), + 'item_padding' => $fieldParams->get('item_padding_control.item_padding'), + 'gap' => $fieldParams->get('item_gap.gap'), + 'item_border_style' => $fieldParams->get('border.style'), + 'item_border_width' => $fieldParams->get('border.width'), + 'item_border_color' => $fieldParams->get('border.color'), + 'item_background_color' => $fieldParams->get('item_background_color'), + 'item_border_radius' => $fieldParams->get('item_border_radius_control.item_border_radius'), + + // Unit Digits Container + 'digits_wrapper_min_width' => $fieldParams->get('digits_wrapper_custom_width') === '1' ? $fieldParams->get('digits_wrapper_min_width') : null, + 'digits_wrapper_padding' => $fieldParams->get('digits_wrapper_padding_control.digits_wrapper_padding'), + 'digits_wrapper_border_radius' => $fieldParams->get('digits_wrapper_border_radius_control.digits_wrapper_border_radius'), + 'digits_wrapper_background_color' => $fieldParams->get('digits_wrapper_background_color'), + + // Unit Digit + 'digits_font_size' => $fieldParams->get('digits_font_size_control.digits_font_size'), + 'digits_font_weight' => $fieldParams->get('digits_font_weight'), + 'digit_min_width' => $fieldParams->get('digits_custom_width') === '1' ? $fieldParams->get('digits_min_width') : null, + 'digits_padding' => $fieldParams->get('digits_padding_control.digits_padding'), + 'digit_border_radius' => $fieldParams->get('digits_border_radius_control.digits_border_radius'), + 'digits_gap' => $fieldParams->get('digits_gap_control.digits_gap'), + 'digit_background_color' => $fieldParams->get('digit_background_color'), + 'digit_text_color' => $fieldParams->get('digit_text_color'), + + // Unit Label + 'label_font_size' => $fieldParams->get('label_font_size_control.label_font_size'), + 'label_font_weight' => $fieldParams->get('label_font_weight'), + 'unit_label_margin_top' => $fieldParams->get('unit_label_margin_top'), + 'unit_label_text_color' => $fieldParams->get('unit_label_text_color'), +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('Countdown', $payload); \ No newline at end of file diff --git a/plugins/fields/acfcountdown/version.php b/plugins/fields/acfcountdown/version.php new file mode 100644 index 00000000..3dd77202 --- /dev/null +++ b/plugins/fields/acfcountdown/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfcountry/acfcountry.php b/plugins/fields/acfcountry/acfcountry.php new file mode 100644 index 00000000..ed2b0711 --- /dev/null +++ b/plugins/fields/acfcountry/acfcountry.php @@ -0,0 +1,137 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFCountry extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'NR_Geo'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + if (!$country = \NRFramework\Countries::getCountry($option->getValue())) + { + continue; + } + + $option->setLabel($country['name']); + } + + return $options; + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $fieldNode->setAttribute('layout', 'joomla.form.field.list-fancy-select'); + + $fieldNode->setAttribute('multiple', (bool) $field->fieldparams->get('multiple_selection', false)); + $fieldNode->setAttribute('detect_visitor_country', (bool) $field->fieldparams->get('detect_visitor_country', false)); + return $fieldNode; + } + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $countries = $field->value; + + if (!is_array($countries)) + { + $countries = [$countries]; + } + + if ($field->fieldparams->get('countrydisplay', 'name') == 'name') + { + if (!is_array($countries)) + { + $countries = [$countries]; + } + + $countries_temp = []; + + foreach ($countries as $c) + { + if (!$country = \NRFramework\Countries::getCountry($c)) + { + continue; + } + + $countries_temp[] = $country['name']; + } + + $countries = $countries_temp; + } + + $field->value = implode (', ', $countries); + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } +} diff --git a/plugins/fields/acfcountry/acfcountry.xml b/plugins/fields/acfcountry/acfcountry.xml new file mode 100644 index 00000000..8a7a9575 --- /dev/null +++ b/plugins/fields/acfcountry/acfcountry.xml @@ -0,0 +1,21 @@ + + + ACF_COUNTRY + ACF_COUNTRY_DESC + Tassos Marinos + April 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfcountry.php + script.install.helper.php + version.php + params + tmpl + language + + diff --git a/plugins/fields/acfcountry/language/ca-ES/ca-ES.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/ca-ES/ca-ES.plg_fields_acfcountry.ini new file mode 100644 index 00000000..09d49ee0 --- /dev/null +++ b/plugins/fields/acfcountry/language/ca-ES/ca-ES.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - País" +ACF_COUNTRY="Camps - ACF País" +ACF_COUNTRY_DESC="Mostra el nom o el codi del país" +ACF_COUNTRY_NAME="Nom del país" +ACF_COUNTRY_CODE="Codi de país" +ACF_COUNTRY_VALUE_DESC="Escull un país del llistat" diff --git a/plugins/fields/acfcountry/language/da-DK/da-DK.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/da-DK/da-DK.plg_fields_acfcountry.ini new file mode 100644 index 00000000..184269df --- /dev/null +++ b/plugins/fields/acfcountry/language/da-DK/da-DK.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Land" +ACF_COUNTRY="Felter - ACF Land" +ACF_COUNTRY_DESC="Vis landenavn eller landekodecountry name or country code" +ACF_COUNTRY_NAME="Landenavn" +ACF_COUNTRY_CODE="LandekodeCode" +ACF_COUNTRY_VALUE_DESC="Vælg et land fra listena Country from the list" diff --git a/plugins/fields/acfcountry/language/de-DE/de-DE.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/de-DE/de-DE.plg_fields_acfcountry.ini new file mode 100644 index 00000000..8b12b010 --- /dev/null +++ b/plugins/fields/acfcountry/language/de-DE/de-DE.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Land" +ACF_COUNTRY="Felder - ACF-Land" +ACF_COUNTRY_DESC="Ländernamen oder Ländercode anzeigen" +ACF_COUNTRY_NAME="Ländername" +ACF_COUNTRY_CODE="Ländercode" +ACF_COUNTRY_VALUE_DESC="Wählen Sie ein Land aus der Liste" diff --git a/plugins/fields/acfcountry/language/en-GB/en-GB.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/en-GB/en-GB.plg_fields_acfcountry.ini new file mode 100644 index 00000000..d125345f --- /dev/null +++ b/plugins/fields/acfcountry/language/en-GB/en-GB.plg_fields_acfcountry.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Country" +ACF_COUNTRY="Fields - ACF Country" +ACF_COUNTRY_DESC="Select a country from a list in the back-end and display the country name or country code in the front-end." +ACF_COUNTRY_NAME="Country Name" +ACF_COUNTRY_CODE="Country Code" +ACF_COUNTRY_VALUE_DESC="Select a Country from the list" +ACF_COUNTRY_DETECT_VISITOR_COUNTRY="Detect Visitor Country" +ACF_COUNTRY_DETECT_VISITOR_COUNTRY_DESC="If enabled, the field will try to detect and prefill the visitor's country.

It requires the TGeoIP plugin to be installed." +ACF_COUNTRY_MULTIPLE_SELECTION="Multiple Country Selection" +ACF_COUNTRY_MULTIPLE_SELECTION_DESC="Allow selection of multiple countries." \ No newline at end of file diff --git a/plugins/fields/acfcountry/language/en-GB/en-GB.plg_fields_acfcountry.sys.ini b/plugins/fields/acfcountry/language/en-GB/en-GB.plg_fields_acfcountry.sys.ini new file mode 100644 index 00000000..8e7e82ef --- /dev/null +++ b/plugins/fields/acfcountry/language/en-GB/en-GB.plg_fields_acfcountry.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_COUNTRY="Fields - ACF Country" +ACF_COUNTRY_DESC="Select a country from a list in the back-end and display the country name or country code in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfcountry/language/es-ES/es-ES.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/es-ES/es-ES.plg_fields_acfcountry.ini new file mode 100644 index 00000000..bd1c83e7 --- /dev/null +++ b/plugins/fields/acfcountry/language/es-ES/es-ES.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="País - ACF" +ACF_COUNTRY="Campos - ACF País" +ACF_COUNTRY_DESC="Mostrar el nombre del país o el código del país " +ACF_COUNTRY_NAME="Nombre del país" +ACF_COUNTRY_CODE="Código de país " +ACF_COUNTRY_VALUE_DESC="Selecciona un país de la lista" diff --git a/plugins/fields/acfcountry/language/es-ES/es-ES.plg_fields_acfcountry.sys.ini b/plugins/fields/acfcountry/language/es-ES/es-ES.plg_fields_acfcountry.sys.ini new file mode 100644 index 00000000..60c3e15e --- /dev/null +++ b/plugins/fields/acfcountry/language/es-ES/es-ES.plg_fields_acfcountry.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_COUNTRY="Campos - ACF País" +ACF_COUNTRY_DESC="Seleccione un país de una lista en el back-end y muestre el nombre del país o el código del país en el front-end." diff --git a/plugins/fields/acfcountry/language/fr-FR/fr-FR.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/fr-FR/fr-FR.plg_fields_acfcountry.ini new file mode 100644 index 00000000..b90b7f29 --- /dev/null +++ b/plugins/fields/acfcountry/language/fr-FR/fr-FR.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Pays" +ACF_COUNTRY="Champs - Pays ACF" +ACF_COUNTRY_DESC="Afficher le nom ou le code du pays" +ACF_COUNTRY_NAME="Nom du pays" +ACF_COUNTRY_CODE="Code du pays" +ACF_COUNTRY_VALUE_DESC="Sélectionnez un pays dans la liste" diff --git a/plugins/fields/acfcountry/language/it-IT/it-IT.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/it-IT/it-IT.plg_fields_acfcountry.ini new file mode 100644 index 00000000..bbf8799f --- /dev/null +++ b/plugins/fields/acfcountry/language/it-IT/it-IT.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Stato" +ACF_COUNTRY="Campi - ACF Stato" +ACF_COUNTRY_DESC="Visualizza il nome o il codice dello stato" +ACF_COUNTRY_NAME="Nome Stato" +ACF_COUNTRY_CODE="Codice Stato" +ACF_COUNTRY_VALUE_DESC="Seleziona un Paese dall'elenco" diff --git a/plugins/fields/acfcountry/language/nl-NL/nl-NL.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/nl-NL/nl-NL.plg_fields_acfcountry.ini new file mode 100644 index 00000000..58817487 --- /dev/null +++ b/plugins/fields/acfcountry/language/nl-NL/nl-NL.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Land" +ACF_COUNTRY="Velden - ACF Land" +ACF_COUNTRY_DESC="Toon de landnaam of het landcode" +ACF_COUNTRY_NAME="Naam van het land" +ACF_COUNTRY_CODE="Land Code" +ACF_COUNTRY_VALUE_DESC="Selecteer een land uit de lijst" diff --git a/plugins/fields/acfcountry/language/pt-BR/pt-BR.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/pt-BR/pt-BR.plg_fields_acfcountry.ini new file mode 100644 index 00000000..8dd9ba8d --- /dev/null +++ b/plugins/fields/acfcountry/language/pt-BR/pt-BR.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - País" +ACF_COUNTRY="Campos - ACF País" +ACF_COUNTRY_DESC="Exibe o nome do país ou código do país." +ACF_COUNTRY_NAME="Nome do País" +ACF_COUNTRY_CODE="Código do País" +; ACF_COUNTRY_VALUE_DESC="Select a Country from the list" diff --git a/plugins/fields/acfcountry/language/ru-RU/ru-RU.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/ru-RU/ru-RU.plg_fields_acfcountry.ini new file mode 100644 index 00000000..e163f23d --- /dev/null +++ b/plugins/fields/acfcountry/language/ru-RU/ru-RU.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Страна" +ACF_COUNTRY="Поля - Страна ACF" +ACF_COUNTRY_DESC="Показать название страны или код страны" +ACF_COUNTRY_NAME="Название страны" +ACF_COUNTRY_CODE="Код страны" +ACF_COUNTRY_VALUE_DESC="Выберите страну из списка" diff --git a/plugins/fields/acfcountry/language/sv-SE/sv-SE.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/sv-SE/sv-SE.plg_fields_acfcountry.ini new file mode 100644 index 00000000..a70cbe6d --- /dev/null +++ b/plugins/fields/acfcountry/language/sv-SE/sv-SE.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Land" +ACF_COUNTRY="Fält - ACF Land" +ACF_COUNTRY_DESC="Visa landsnamn eller landskod" +ACF_COUNTRY_NAME="Land namn" +ACF_COUNTRY_CODE="Landskod" +ACF_COUNTRY_VALUE_DESC="Välj ett land från listan" diff --git a/plugins/fields/acfcountry/language/tr-TR/tr-TR.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/tr-TR/tr-TR.plg_fields_acfcountry.ini new file mode 100644 index 00000000..ace4c989 --- /dev/null +++ b/plugins/fields/acfcountry/language/tr-TR/tr-TR.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +; PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - Country" +; ACF_COUNTRY="Fields - ACF Country" +ACF_COUNTRY_DESC="Ülke adını veya ülke kodunu göster" +; ACF_COUNTRY_NAME="Country Name" +; ACF_COUNTRY_CODE="Country Code" +; ACF_COUNTRY_VALUE_DESC="Select a Country from the list" diff --git a/plugins/fields/acfcountry/language/uk-UA/uk-UA.plg_fields_acfcountry.ini b/plugins/fields/acfcountry/language/uk-UA/uk-UA.plg_fields_acfcountry.ini new file mode 100644 index 00000000..55edf152 --- /dev/null +++ b/plugins/fields/acfcountry/language/uk-UA/uk-UA.plg_fields_acfcountry.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCOUNTRY_LABEL="ACF - країна" +ACF_COUNTRY="Поля - країна ACF" +ACF_COUNTRY_DESC="Відобразити назву країни або код країни" +ACF_COUNTRY_NAME="Назва країни" +ACF_COUNTRY_CODE="Код країни" +ACF_COUNTRY_VALUE_DESC="Вибрати країну зі списку" diff --git a/plugins/fields/acfcountry/params/acfcountry.xml b/plugins/fields/acfcountry/params/acfcountry.xml new file mode 100644 index 00000000..49d6cec0 --- /dev/null +++ b/plugins/fields/acfcountry/params/acfcountry.xml @@ -0,0 +1,25 @@ + +
+ +
+ + + + + + + + + +
+
+
diff --git a/plugins/fields/acfcountry/script.install.helper.php b/plugins/fields/acfcountry/script.install.helper.php new file mode 100644 index 00000000..a40e9a6d --- /dev/null +++ b/plugins/fields/acfcountry/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfcountryInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfcountry/script.install.php b/plugins/fields/acfcountry/script.install.php new file mode 100644 index 00000000..47c76eb7 --- /dev/null +++ b/plugins/fields/acfcountry/script.install.php @@ -0,0 +1,42 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\Filesystem\File; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFCountryInstallerScript extends PlgFieldsACFCountryInstallerScriptHelper +{ + public $alias = 'acfcountry'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + // Fix missing version.php + if ($this->isInstalled() && !file_exists($this->getMainFolder() . '/version.php')) + { + $systemVersionPath = JPATH_SITE . '/plugins/system/acf/version.php'; + File::copy($systemVersionPath, $this->getMainFolder() . '/version.php'); + } + + return parent::onBeforeInstall(); + } +} diff --git a/plugins/fields/acfcountry/tmpl/acfcountry.php b/plugins/fields/acfcountry/tmpl/acfcountry.php new file mode 100644 index 00000000..211e0832 --- /dev/null +++ b/plugins/fields/acfcountry/tmpl/acfcountry.php @@ -0,0 +1,15 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +echo htmlspecialchars($field->value, ENT_COMPAT, 'UTF-8'); \ No newline at end of file diff --git a/plugins/fields/acfcountry/version.php b/plugins/fields/acfcountry/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfcountry/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfcurrency/acfcurrency.php b/plugins/fields/acfcurrency/acfcurrency.php new file mode 100644 index 00000000..cc6cd9b8 --- /dev/null +++ b/plugins/fields/acfcurrency/acfcurrency.php @@ -0,0 +1,66 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFCurrency extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'NR_Currencies'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + require_once JPATH_PLUGINS . '/system/nrframework/fields/currencies.php'; + + $class = new \JFormFieldNR_Currencies(); + $currencies = $class->currencies; + + foreach ($options as $option) + { + if (!isset($currencies[$option->getValue()])) + { + continue; + } + + $option->setLabel($currencies[$option->getValue()]); + } + + return $options; + } +} diff --git a/plugins/fields/acfcurrency/acfcurrency.xml b/plugins/fields/acfcurrency/acfcurrency.xml new file mode 100644 index 00000000..47ca14bf --- /dev/null +++ b/plugins/fields/acfcurrency/acfcurrency.xml @@ -0,0 +1,20 @@ + + + ACF_CURRENCY + ACF_CURRENCY_DESC + Tassos Marinos + April 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfcurrency.php + script.install.helper.php + version.php + tmpl + language + + diff --git a/plugins/fields/acfcurrency/language/ca-ES/ca-ES.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/ca-ES/ca-ES.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..9ae1b1ab --- /dev/null +++ b/plugins/fields/acfcurrency/language/ca-ES/ca-ES.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Divisa" +ACF_CURRENCY="Camps - ACF divisa" +ACF_CURRENCY_DESC="Mostra un codi de divisa" +ACF_CURRENCY_VALUE_DESC="Escull una divisa del llistat" diff --git a/plugins/fields/acfcurrency/language/da-DK/da-DK.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/da-DK/da-DK.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..c6bf0a10 --- /dev/null +++ b/plugins/fields/acfcurrency/language/da-DK/da-DK.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Valuta" +ACF_CURRENCY="Felter - ACF Valuta" +ACF_CURRENCY_DESC="Vis en valutakodea currency code" +ACF_CURRENCY_VALUE_DESC="Vælg en valuta fra listena Currency from the list" diff --git a/plugins/fields/acfcurrency/language/de-DE/de-DE.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/de-DE/de-DE.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..2fb87a2b --- /dev/null +++ b/plugins/fields/acfcurrency/language/de-DE/de-DE.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Währung" +ACF_CURRENCY="Felder - ACF-Währung" +ACF_CURRENCY_DESC="Währungscode anzeigen" +ACF_CURRENCY_VALUE_DESC="Währung aus der Liste auswählen" diff --git a/plugins/fields/acfcurrency/language/en-GB/en-GB.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/en-GB/en-GB.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..aa567aa6 --- /dev/null +++ b/plugins/fields/acfcurrency/language/en-GB/en-GB.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Currency" +ACF_CURRENCY="Fields - ACF Currency" +ACF_CURRENCY_DESC="Select a currency from a list in the back-end and display the currency code in the front-end." +ACF_CURRENCY_VALUE_DESC="Select a Currency from the list" \ No newline at end of file diff --git a/plugins/fields/acfcurrency/language/en-GB/en-GB.plg_fields_acfcurrency.sys.ini b/plugins/fields/acfcurrency/language/en-GB/en-GB.plg_fields_acfcurrency.sys.ini new file mode 100644 index 00000000..ca0727e8 --- /dev/null +++ b/plugins/fields/acfcurrency/language/en-GB/en-GB.plg_fields_acfcurrency.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_CURRENCY="Fields - ACF Currency" +ACF_CURRENCY_DESC="Select a currency from a list in the back-end and display the currency code in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfcurrency/language/es-ES/es-ES.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/es-ES/es-ES.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..ce477645 --- /dev/null +++ b/plugins/fields/acfcurrency/language/es-ES/es-ES.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Moneda" +ACF_CURRENCY="Campos - ACF Moneda" +ACF_CURRENCY_DESC="Mostrar el código de moneda" +ACF_CURRENCY_VALUE_DESC="Seleccione una moneda de la lista" diff --git a/plugins/fields/acfcurrency/language/es-ES/es-ES.plg_fields_acfcurrency.sys.ini b/plugins/fields/acfcurrency/language/es-ES/es-ES.plg_fields_acfcurrency.sys.ini new file mode 100644 index 00000000..3a818eba --- /dev/null +++ b/plugins/fields/acfcurrency/language/es-ES/es-ES.plg_fields_acfcurrency.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_CURRENCY="Campos - ACF Moneda" +ACF_CURRENCY_DESC="Seleccione una moneda de una lista en el back-end y muestre el código de moneda en el front-end." diff --git a/plugins/fields/acfcurrency/language/fr-FR/fr-FR.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/fr-FR/fr-FR.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..0ef5b073 --- /dev/null +++ b/plugins/fields/acfcurrency/language/fr-FR/fr-FR.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Devise" +ACF_CURRENCY="Champs - Devise ACF" +ACF_CURRENCY_DESC="Afficher un code de devise" +ACF_CURRENCY_VALUE_DESC="Sélectionnez une devise dans la liste" diff --git a/plugins/fields/acfcurrency/language/it-IT/it-IT.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/it-IT/it-IT.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..fbbe6e91 --- /dev/null +++ b/plugins/fields/acfcurrency/language/it-IT/it-IT.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Valuta" +ACF_CURRENCY="Campi - ACF Valuta" +ACF_CURRENCY_DESC="Mostra il Codice Valuta" +ACF_CURRENCY_VALUE_DESC="Seleziona una valuta dall'elenco" diff --git a/plugins/fields/acfcurrency/language/nl-NL/nl-NL.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/nl-NL/nl-NL.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..5b995395 --- /dev/null +++ b/plugins/fields/acfcurrency/language/nl-NL/nl-NL.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Currency" +ACF_CURRENCY="Velden - ACF Currency" +ACF_CURRENCY_DESC="Toon een valuta code" +ACF_CURRENCY_VALUE_DESC="Selecteer een valuta uit de lijst" diff --git a/plugins/fields/acfcurrency/language/pt-BR/pt-BR.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/pt-BR/pt-BR.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..19f44742 --- /dev/null +++ b/plugins/fields/acfcurrency/language/pt-BR/pt-BR.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Moeda" +ACF_CURRENCY="Campos - ACF Moeda" +ACF_CURRENCY_DESC="Exibe um código de moeda." +; ACF_CURRENCY_VALUE_DESC="Select a Currency from the list" diff --git a/plugins/fields/acfcurrency/language/ru-RU/ru-RU.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/ru-RU/ru-RU.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..be955971 --- /dev/null +++ b/plugins/fields/acfcurrency/language/ru-RU/ru-RU.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Валюта" +ACF_CURRENCY="Поля - Валюта ACF" +ACF_CURRENCY_DESC="Показать код валюты" +ACF_CURRENCY_VALUE_DESC="Выбрать валюту из списка" diff --git a/plugins/fields/acfcurrency/language/sv-SE/sv-SE.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/sv-SE/sv-SE.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..50517979 --- /dev/null +++ b/plugins/fields/acfcurrency/language/sv-SE/sv-SE.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Valuta" +ACF_CURRENCY="Fält - ACF Valuta" +ACF_CURRENCY_DESC="Visa en valutakod" +ACF_CURRENCY_VALUE_DESC="Välj en valuta från listan" diff --git a/plugins/fields/acfcurrency/language/uk-UA/uk-UA.plg_fields_acfcurrency.ini b/plugins/fields/acfcurrency/language/uk-UA/uk-UA.plg_fields_acfcurrency.ini new file mode 100644 index 00000000..2ffa7308 --- /dev/null +++ b/plugins/fields/acfcurrency/language/uk-UA/uk-UA.plg_fields_acfcurrency.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFCURRENCY_LABEL="ACF - Валюта" +ACF_CURRENCY="Поля - валюта ACF" +ACF_CURRENCY_DESC="Показати код валюти" +ACF_CURRENCY_VALUE_DESC="Вибрати валюту зі списку" diff --git a/plugins/fields/acfcurrency/script.install.helper.php b/plugins/fields/acfcurrency/script.install.helper.php new file mode 100644 index 00000000..a8cc814b --- /dev/null +++ b/plugins/fields/acfcurrency/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfcurrencyInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfcurrency/script.install.php b/plugins/fields/acfcurrency/script.install.php new file mode 100644 index 00000000..30e6a258 --- /dev/null +++ b/plugins/fields/acfcurrency/script.install.php @@ -0,0 +1,43 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\Filesystem\File; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFCurrencyInstallerScript extends PlgFieldsACFCurrencyInstallerScriptHelper +{ + public $alias = 'acfcurrency'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + // If version.php doesn't exist, copy it from the system plugin + if ($this->isInstalled() && !file_exists($this->getMainFolder() . '/version.php')) + { + $systemVersionPath = JPATH_SITE . '/plugins/system/acf/version.php'; + + $result = File::copy($systemVersionPath, $this->getMainFolder() . '/version.php'); + } + + return parent::onBeforeInstall(); + } +} diff --git a/plugins/fields/acfcurrency/tmpl/acfcurrency.php b/plugins/fields/acfcurrency/tmpl/acfcurrency.php new file mode 100644 index 00000000..45bb40d6 --- /dev/null +++ b/plugins/fields/acfcurrency/tmpl/acfcurrency.php @@ -0,0 +1,15 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +echo htmlspecialchars($field->value, ENT_COMPAT, 'UTF-8'); diff --git a/plugins/fields/acfcurrency/version.php b/plugins/fields/acfcurrency/version.php new file mode 100644 index 00000000..8e294844 --- /dev/null +++ b/plugins/fields/acfcurrency/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfdownloadbutton/acfdownloadbutton.php b/plugins/fields/acfdownloadbutton/acfdownloadbutton.php new file mode 100644 index 00000000..2e59033f --- /dev/null +++ b/plugins/fields/acfdownloadbutton/acfdownloadbutton.php @@ -0,0 +1,54 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFDownloadButton extends ACF_Field +{ + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $fieldNode->setAttribute('type', 'filelist'); + $fieldNode->setAttribute('hide_default', true); + $fieldNode->setAttribute('directory', $field->fieldparams->get('directory', 'images')); + $fieldNode->setAttribute('filter', $field->fieldparams->get('filter', '')); + $fieldNode->setAttribute('exclude', $field->fieldparams->get('exclude', '')); + $fieldNode->setAttribute('class', ''); + + return $fieldNode; + } +} diff --git a/plugins/fields/acfdownloadbutton/acfdownloadbutton.xml b/plugins/fields/acfdownloadbutton/acfdownloadbutton.xml new file mode 100644 index 00000000..da2a66ad --- /dev/null +++ b/plugins/fields/acfdownloadbutton/acfdownloadbutton.xml @@ -0,0 +1,21 @@ + + + ACF_DOWNLOADBUTTON + ACF_DOWNLOADBUTTON_DESC + Tassos Marinos + April 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfdownloadbutton.php + script.install.helper.php + version.php + params + tmpl + language + + diff --git a/plugins/fields/acfdownloadbutton/language/ca-ES/ca-ES.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/ca-ES/ca-ES.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..e80e123b --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/ca-ES/ca-ES.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Botó de descàrrega" +ACF_DOWNLOADBUTTON="Camps - ACF botó de descàrrega" +ACF_DOWNLOADBUTTON_DESC="Crear un botó per descarregar arxius fàcilment!" +ACF_DOWNLOADBUTTON_DIR="Directori" +ACF_DOWNLOADBUTTON_DIR_DESC="La ruta del sistema d'arxius al directori que conté els arxius a llistar" +ACF_DOWNLOADBUTTON_FILTER="Incloure arxius" +ACF_DOWNLOADBUTTON_FILTER_DESC="És una cadena d'expressió regular que s'utilitza per filtrar el llistat dels arxius seleccionats per ser inclosos al llistat desplegable. Si s'omet, s'inclouran tots els arxius al directori. L'argument d'inclusió d'arxius s'aplica abans que l'argument d'exclusió.

Filtrar una extensió individual:
.pdf

Filtrar un llistat d'extensions:
.pdf|.zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Excloure arxius" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="És una cadena d'expressió regular que s'utilitza per excloure arxius del llistat. L'argument d'exclusió d'arxius s'aplica després que l'argument d'inclusió.

Filtrar una extensió individual:
.pdf

Filtrar un llistat d'extensions:
.pdf|.zip" +ACF_DOWNLOADBUTTON_TYPE="Tipus" +ACF_DOWNLOADBUTTON_TYPE_DESC="Escull si mostrar el botó de descàrrega com un element botó o com un enllaç." +ACF_DOWNLOADBUTTON_LABEL="Etiqueta del botó" +ACF_DOWNLOADBUTTON_LABEL_DESC="Opcionalment, escriu una etiqueta de botó personalitzada que s'utilitzarà a la part pública. Si s'omet, s'utilitzarà l'etiqueta 'Descàrregar'." +ACF_DOWNLOADBUTTON_DOWNLOAD="Descarregar" +ACF_DOWNLOADBUTTON_CLASS="Classe del botó" +ACF_DOWNLOADBUTTON_CLASS_DESC="Escriu una classe pel botó que s'utilitzarà a la part pública. En la majoria d'infraestructures CSS un botó s'estila amb les classes 'btn' o 'button'" diff --git a/plugins/fields/acfdownloadbutton/language/da-DK/da-DK.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/da-DK/da-DK.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..55a0a7bd --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/da-DK/da-DK.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Download knap" +ACF_DOWNLOADBUTTON="Felter - ACF Download knap" +ACF_DOWNLOADBUTTON_DESC="Opret let en fil download knap!" +ACF_DOWNLOADBUTTON_DIR="Mappe" +ACF_DOWNLOADBUTTON_DIR_DESC="Filsystemstien til mappen der indeholder filerne der skal listes" +ACF_DOWNLOADBUTTON_FILTER="Inkluder filer" +ACF_DOWNLOADBUTTON_FILTER_DESC="Er en regulær udtryksstreng som anvendes til at filtrere listen med filer valgt til inkludering i dropdown listen. Hvis sprunget over, så vil alle filer i mappen blive inkluderet. Argumentet inkluder filer bliver anvendt før argumentet ekskluder filer.

Filtrer en enkelt udvidelse:
.pdf

Filtrer en liste med udvidelser:
.pdf|.zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Ekskluder filer" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Det er en regulær udtryksstreng som anvendes til at ekskludere filer fra listen. Argumentet eksluder filer bliver anvendt efter argumentet inkluder filer.

Filtrer en enkelt udvidelse:
.pdf

Filtrer en liste over udvidelser:
.pdf|.zip" +ACF_DOWNLOADBUTTON_TYPE="Type" +ACF_DOWNLOADBUTTON_TYPE_DESC="Vælg om downloadknappen skal vises som et knapelement eller som et link." +ACF_DOWNLOADBUTTON_LABEL="Knap label" +ACF_DOWNLOADBUTTON_LABEL_DESC="Angiv eventuelt en brugerdefineret knap label, som anvendes i frontend. Hvis sprunget over, så vil labelen 'Download' blive anvendt i stedet for." +ACF_DOWNLOADBUTTON_DOWNLOAD="Download" +ACF_DOWNLOADBUTTON_CLASS="Knap klasse" +ACF_DOWNLOADBUTTON_CLASS_DESC="Angiv en knap klasse som bliver anvendt i frontend. I de fleste CSS frameworks kan en knap styles ved anvendelse af klassen 'btn' eller 'button'" diff --git a/plugins/fields/acfdownloadbutton/language/de-DE/de-DE.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/de-DE/de-DE.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..38a644e0 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/de-DE/de-DE.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Herunterladen Schaltfläche" +ACF_DOWNLOADBUTTON="Felder - ACF-Herunterladen-Schaltfläche" +ACF_DOWNLOADBUTTON_DESC="Schaltfläche zum Herunterladen von Dateien einfach erstellen!" +ACF_DOWNLOADBUTTON_DIR="Verzeichnis" +ACF_DOWNLOADBUTTON_DIR_DESC="Der Dateisystempfad zu dem Verzeichnis, in dem sich die aufzulistenden Dateien befinden." +ACF_DOWNLOADBUTTON_FILTER="Dateien einschließen" +ACF_DOWNLOADBUTTON_FILTER_DESC="Eine Zeichenfolge mit regulären Ausdrücken, mit der die Liste der Dateien gefiltert wird, die zur Aufnahme in die Dropdown-Liste ausgewählt wurden. Wenn diese Option nicht angegeben wird, werden alle Dateien im Verzeichnis eingeschlossen. Das Argument\" Include Files \"wird vor dem Argument\" Exclude Files \"angewendet.

Filtern einer einzelnen Erweiterung:
.pdf

Filtern einer Liste von Erweiterungen:
.pdf | .zip " +ACF_DOWNLOADBUTTON_EXCLUDE="Dateien ausschließen" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Ein regulärer Ausdruck, der zum Ausschließen von Dateien aus der Liste verwendet wird. Der Argumentausdruck\" Dateien ausschließen \"wird nach dem Argumentausdruck\" Dateien einschließen \"angewendet.

Filtern Sie eine einzelne Erweiterung:
.pdf

Filtern einer Liste von Erweiterungen:
.pdf | .zip " +ACF_DOWNLOADBUTTON_TYPE="Typ" +ACF_DOWNLOADBUTTON_TYPE_DESC="Wählen Sie aus, ob die Herunterladen-Schaltfläche als Schaltflächenelement oder als Link angezeigt werden soll." +ACF_DOWNLOADBUTTON_LABEL="Schaltflächenbeschriftung" +ACF_DOWNLOADBUTTON_LABEL_DESC="Optional können Sie eine benutzerdefinierte Schaltflächenbezeichnung eingeben, die im Front-End verwendet wird. Wenn Sie diese nicht angeben, wird stattdessen die Bezeichnung \" Herunterladen \" verwendet." +ACF_DOWNLOADBUTTON_DOWNLOAD="Herunterladen" +ACF_DOWNLOADBUTTON_CLASS="Schaltfläche-Klasse" +ACF_DOWNLOADBUTTON_CLASS_DESC="Geben Sie eine Schaltflächenklasse ein, die im Front-End verwendet wird. In den meisten CSS-Frameworks kann eine Schaltfläche mit der Klasse 'btn' oder 'button' formatiert werden." diff --git a/plugins/fields/acfdownloadbutton/language/en-GB/en-GB.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/en-GB/en-GB.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..e2c01c2d --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/en-GB/en-GB.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,24 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Download Button" +ACF_DOWNLOADBUTTON="Fields - ACF Download Button" +ACF_DOWNLOADBUTTON_DESC="Select a file in the back-end and display a download link or button in the front-end." +ACF_DOWNLOADBUTTON_DIR="Directory" +ACF_DOWNLOADBUTTON_DIR_DESC="The filesystem path to the directory containing the files to be listed" +ACF_DOWNLOADBUTTON_FILTER="Include Files" +ACF_DOWNLOADBUTTON_FILTER_DESC="Is a regular expression string which is used to filter the list of files selected for inclusion in the drop-down list. If omitted, all files in the directory are included. The Include Files argument is applied before the Exclude Files argument.

Filter a single extension:
.pdf

Filtering a list of extensions:
.pdf|.zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Exclude Files" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Is a regular expression string which is used to exclude files from the list. The Exclude Files argument expression is applied after the Includes Files argument expression.

Filter a single extension:
.pdf

Filtering a list of extensions:
.pdf|.zip" +ACF_DOWNLOADBUTTON_TYPE="Type" +ACF_DOWNLOADBUTTON_TYPE_DESC="Choose whether to display the download button as a button element or as a link." +ACF_DOWNLOADBUTTON_LABEL="Button Label" +ACF_DOWNLOADBUTTON_LABEL_DESC="Optionally, enter a custom button label which will be used in the front-end. If omitted the 'Download' label will be used instead." +ACF_DOWNLOADBUTTON_DOWNLOAD="Download" +ACF_DOWNLOADBUTTON_CLASS="Button Class" +ACF_DOWNLOADBUTTON_CLASS_DESC="Enter a button class that will be used in the front-end. In most CSS frameworks a button can be styled using the class 'btn' or 'button'" +ACF_DOWNLOADBUTTON_VALUE_DESC="Select a file from the list." \ No newline at end of file diff --git a/plugins/fields/acfdownloadbutton/language/en-GB/en-GB.plg_fields_acfdownloadbutton.sys.ini b/plugins/fields/acfdownloadbutton/language/en-GB/en-GB.plg_fields_acfdownloadbutton.sys.ini new file mode 100644 index 00000000..2257863c --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/en-GB/en-GB.plg_fields_acfdownloadbutton.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_DOWNLOADBUTTON="Fields - ACF Download Button" +ACF_DOWNLOADBUTTON_DESC="Select a file in the back-end and display a download link or button in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfdownloadbutton/language/es-ES/es-ES.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/es-ES/es-ES.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..d7181fcc --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/es-ES/es-ES.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Botón de descarga" +ACF_DOWNLOADBUTTON="Campos - Botón de descarga de ACF" +ACF_DOWNLOADBUTTON_DESC="¡Cree un botón de descarga de archivos fácilmente!" +ACF_DOWNLOADBUTTON_DIR="Directorio" +ACF_DOWNLOADBUTTON_DIR_DESC="La ruta del sistema de archivos al directorio que contiene los archivos que se enumerarán" +ACF_DOWNLOADBUTTON_FILTER="Incluir archivos" +ACF_DOWNLOADBUTTON_FILTER_DESC="Es una cadena de expresión regular que se utiliza para filtrar la lista de archivos seleccionados para su inclusión en la lista desplegable. Si se omite, se incluyen todos los archivos del directorio. El argumento Incluir archivos se aplica antes que el argumento Excluir archivos.

Filtrar una sola extensión:
.pdf

Filtrar una lista de extensiones:
.pdf|.zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Excluir archivos" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Es una cadena de expresión regular que se utiliza para excluir archivos de la lista. La expresión del argumento Excluir archivos se aplica después de la expresión del argumento Incluye archivos.

Filtrar una sola extensión:
.pdf

Filtrar una lista de extensiones:
.pdf|.zip" +ACF_DOWNLOADBUTTON_TYPE="Tipo" +ACF_DOWNLOADBUTTON_TYPE_DESC="Elija si desea mostrar el botón de descarga como un elemento de botón o como un enlace." +ACF_DOWNLOADBUTTON_LABEL="Etiqueta del Botón" +ACF_DOWNLOADBUTTON_LABEL_DESC="Opcionalmente, ingrese una etiqueta de botón personalizada que se usará en el front-end. Si se omite, se utilizará la etiqueta 'Descargar' en su lugar." +ACF_DOWNLOADBUTTON_DOWNLOAD="Descargar" +ACF_DOWNLOADBUTTON_CLASS="Clase de Botón" +ACF_DOWNLOADBUTTON_CLASS_DESC="Ingrese la clase de botón que se usará en el front-end. En la mayoría de los marcos CSS, se puede diseñar un botón usando la clase 'btn' o 'button'" diff --git a/plugins/fields/acfdownloadbutton/language/es-ES/es-ES.plg_fields_acfdownloadbutton.sys.ini b/plugins/fields/acfdownloadbutton/language/es-ES/es-ES.plg_fields_acfdownloadbutton.sys.ini new file mode 100644 index 00000000..d6e05c47 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/es-ES/es-ES.plg_fields_acfdownloadbutton.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_DOWNLOADBUTTON="Campos - ACF Botón de Descarga" +ACF_DOWNLOADBUTTON_DESC="Seleccione un archivo en el back-end y muestre un enlace o botón de descarga en el front-end." diff --git a/plugins/fields/acfdownloadbutton/language/fr-FR/fr-FR.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/fr-FR/fr-FR.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..ed98fee4 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/fr-FR/fr-FR.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Download Button" +ACF_DOWNLOADBUTTON="Champs - Download Button ACF" +ACF_DOWNLOADBUTTON_DESC="Créer aisément un bouton de téléchargement" +ACF_DOWNLOADBUTTON_DIR="Répertoire" +ACF_DOWNLOADBUTTON_DIR_DESC="Le chemin du répertoire contenant les fichiers à lister" +ACF_DOWNLOADBUTTON_FILTER="Fichiers inclus" +ACF_DOWNLOADBUTTON_FILTER_DESC="C'est une expression régulière qui est utilisée pour filtrer la liste des fichiers sélectionnés à insérer dans la liste déroulante. Par défaut, tous les fichiers du dossier sont inclus. La valeur Fichiers Inclus est appliquée avant celle de Fichiers Exclus.

Filtrer une seule extension :
.pdf

Filtrer une liste d'extension :
.pdfl.zip" +ACF_DOWNLOADBUTTON_EXCLUDE=" Fichiers exclus" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="C'est une expression régulière qui est utilisées pour exclure des fichiers de la liste. La valeur Fichiers Exclus est appliquée après celle de Fichiers Inclus.

Filtrer une seule extension :
.pdf

Filtrer une liste d'extension :
.pdfl.zip " +ACF_DOWNLOADBUTTON_TYPE="Type" +ACF_DOWNLOADBUTTON_TYPE_DESC="Choisissez si vous affichez le bouton de téléchargement comme un élément bouton ou inot" +ACF_DOWNLOADBUTTON_LABEL="Label du bouton" +ACF_DOWNLOADBUTTON_LABEL_DESC="Optionnellement, entrez un label personnalisé qui sera afficher pour le bouton. Par défaut, le label 'Télécharger' sera utilisé" +ACF_DOWNLOADBUTTON_DOWNLOAD="Télécharger" +ACF_DOWNLOADBUTTON_CLASS="Classe du bouton" +ACF_DOWNLOADBUTTON_CLASS_DESC="Entrez une classe qui sera ensuite utilisé en front-end. La plupart des frameworks CSS utilisent la classe \"btn\" ou \"button\"" diff --git a/plugins/fields/acfdownloadbutton/language/it-IT/it-IT.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/it-IT/it-IT.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..fa7da324 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/it-IT/it-IT.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Pulsante di scaricamento" +ACF_DOWNLOADBUTTON="Fields - ACF Pulsante di scaricamento" +ACF_DOWNLOADBUTTON_DESC="Crea facilmente il pulsante di download dei file!" +ACF_DOWNLOADBUTTON_DIR="Cartella" +ACF_DOWNLOADBUTTON_DIR_DESC="Il percorso del filesystem per la cartella contenente i file da elencare" +ACF_DOWNLOADBUTTON_FILTER="Includi file" +ACF_DOWNLOADBUTTON_FILTER_DESC="È una stringa di espressione regolare che viene utilizzata per filtrare l'elenco di file selezionati per l'inclusione nell'elenco a discesa. Se omessa, tutti i file nella directory sono inclusi. L'argomento includi file viene applicato prima dell'argomento escludi file.

Filtra una singola estensione:
.pdf

Filtro di un elenco di estensioni:
.pdf.zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Escludi file" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="È una stringa di espressione regolare che viene utilizzata per escludere file dall'elenco. L'argomento escludi file viene applicato prima dell'argomento includi file.

Filtra una singola estensione:
.pdf

Filtrare un elenco di estensioni:
.pdf.zip" +ACF_DOWNLOADBUTTON_TYPE="Tipologia" +ACF_DOWNLOADBUTTON_TYPE_DESC="Scegli se visualizzare il pulsante di download come elemento pulsante o come collegamento." +ACF_DOWNLOADBUTTON_LABEL="Etichetta del pulsante" +ACF_DOWNLOADBUTTON_LABEL_DESC="A scelta, inserisci un'etichetta personalizzata per il pulsante che verrà usata nel front-end. Se omessa l'etichetta 'Scarica' verrà usata." +ACF_DOWNLOADBUTTON_DOWNLOAD="Scarica" +ACF_DOWNLOADBUTTON_CLASS="Classe pulsante" +ACF_DOWNLOADBUTTON_CLASS_DESC="Inserisci una classe pulsante che verrà utilizzata nel front-end. Nella maggior parte dei framework CSS un pulsante può essere disegnato usando la classe 'btn' o 'button'" diff --git a/plugins/fields/acfdownloadbutton/language/nl-NL/nl-NL.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/nl-NL/nl-NL.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..6bcaa19f --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/nl-NL/nl-NL.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Download Knop" +ACF_DOWNLOADBUTTON="Velden - ACF Download Knop" +ACF_DOWNLOADBUTTON_DESC="Maak eenvoudig een downloadknop voor bestanden!" +ACF_DOWNLOADBUTTON_DIR="Directory" +ACF_DOWNLOADBUTTON_DIR_DESC="Het bestandssysteempad naar de map met de bestanden wat als lijst zal worden weergegeven" +ACF_DOWNLOADBUTTON_FILTER="Bestanden Opnemen" +ACF_DOWNLOADBUTTON_FILTER_DESC="Is een reguliere expressietekenreeks welke wordt gebruikt om de lijst met te filteren bestanden die zijn geselecteerd voor opname in de vervolgkeuzelijst. Indien weggelaten, worden alle bestanden in de directory opgenomen. Het argument Bestanden Opnemen wordt toegepast vóór het argument Bestanden Uitsluiten.


Filter een enkele extensie:

.pdf


Een lijst met extensies filteren:

.pdf | .zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Bestanden uitsluiten" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Een reguliere expressietekenreeks welke wordt gebruikt om bestanden uit de lijst uit te sluiten. De argument uitdrukking Bestanden Uitsluiten wordt toegepast na de argumentuitdrukking Bestanden opnemen.


Een enkele extensie filteren:

.pdf


Een lijst met extensies filteren:

.pdf | .zip" +ACF_DOWNLOADBUTTON_TYPE="Type" +ACF_DOWNLOADBUTTON_TYPE_DESC="Kies of je de downloadknop wilt weergeven als knopelement of als een link." +ACF_DOWNLOADBUTTON_LABEL="Knop Label" +ACF_DOWNLOADBUTTON_LABEL_DESC="Voer optioneel een aangepast knoplabel in dat aan de voorkant wordt gebruikt. Indien weggelaten, wordt in plaats daarvan het label 'Downloaden' gebruikt." +ACF_DOWNLOADBUTTON_DOWNLOAD="Download" +ACF_DOWNLOADBUTTON_CLASS="Knop Klasse" +ACF_DOWNLOADBUTTON_CLASS_DESC="Voer een knopklasse in die op de front-end zal worden gebruikt. In de meeste CSS-frameworks kan een knop worden gestyled met de klasse 'btn' of 'button'" diff --git a/plugins/fields/acfdownloadbutton/language/ru-RU/ru-RU.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/ru-RU/ru-RU.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..e94ac928 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/ru-RU/ru-RU.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - кнопка загрузки" +ACF_DOWNLOADBUTTON="Поля - кнопка загрузки ACF" +ACF_DOWNLOADBUTTON_DESC="Создать кнопку загрузки файла легко!" +ACF_DOWNLOADBUTTON_DIR="Справочник" +ACF_DOWNLOADBUTTON_DIR_DESC="Путь файловой системы к каталогу, содержащему файлы, которые будут перечислены" +ACF_DOWNLOADBUTTON_FILTER="Включить файлы" +ACF_DOWNLOADBUTTON_FILTER_DESC="Строка регулярного выражения, которая используется для фильтрации списка файлов, выбранных для включения в раскрывающийся список. Если не указан, все файлы в каталоге включены. Аргумент Include Files применяется перед аргументом Exclude Files.

Фильтровать одно расширение:
.pdf

Фильтрация списка расширений:
.pdf | .zip " +ACF_DOWNLOADBUTTON_EXCLUDE="Исключить файлы" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Строка регулярного выражения, используемая для исключения файлов из списка. Выражение аргумента« Исключить файлы »применяется после выражения аргумента« Включает файлы ».

Фильтровать одно расширение:
.pdf

Фильтрация списка расширений:
.pdf | .zip " +ACF_DOWNLOADBUTTON_TYPE="Тип" +ACF_DOWNLOADBUTTON_TYPE_DESC="Выберите, отображать ли кнопку загрузки как элемент кнопки или как ссылку." +ACF_DOWNLOADBUTTON_LABEL="Метка кнопки" +ACF_DOWNLOADBUTTON_LABEL_DESC="При желании введите метку пользовательской кнопки, которая будет использоваться в интерфейсе. Если опущено, вместо нее будет использоваться метка"_QQ_" Скачать "_QQ_"." +ACF_DOWNLOADBUTTON_DOWNLOAD="Скачать" +ACF_DOWNLOADBUTTON_CLASS="Класс кнопок" +ACF_DOWNLOADBUTTON_CLASS_DESC="Введите класс кнопки, который будет использоваться во внешнем интерфейсе. В большинстве CSS-фреймворков кнопку можно стилизовать с помощью класса« btn »или« кнопка »" diff --git a/plugins/fields/acfdownloadbutton/language/sv-SE/sv-SE.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/sv-SE/sv-SE.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..6f4bfa78 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/sv-SE/sv-SE.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - Nedladdningsknapp" +ACF_DOWNLOADBUTTON="Fält - ACF Nedladdningsknapp" +ACF_DOWNLOADBUTTON_DESC="Skapa en filnedladdningsknapp enkelt!" +ACF_DOWNLOADBUTTON_DIR="Mapp" +ACF_DOWNLOADBUTTON_DIR_DESC="Filsystemets sökväg till mappen som innehåller filerna som ska listas" +ACF_DOWNLOADBUTTON_FILTER="Inkludera filer" +ACF_DOWNLOADBUTTON_FILTER_DESC="Är en sträng med reguljärt uttryck som används för att filtrera listan över filer som valts för att tas med i rullgardinsmenyn. Om fältet lämnas tomt inkluderas alla filer i mappen. Argumentet Inkludera filer tillämpas före argumentet Exkludera filer.

Filtrera en enskild filändelse:
.pdf

Filtrera en lista med filändelser:
.pdf|.zip" +ACF_DOWNLOADBUTTON_EXCLUDE="Exkludera filer" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Är en sträng med reguljärt uttryck som används för att exkludera filer från listan. Argumentuttrycket Exkludera filer tillämpas efter argumentuttrycket Inkludera filer.

Filtrera en enskild filändelse:
.pdf

Filtrera en lista med filändelser:
.pdf|.zip" +ACF_DOWNLOADBUTTON_TYPE="Typ" +ACF_DOWNLOADBUTTON_TYPE_DESC="Välj om nedladdningsknappen ska visas som en knapp eller som en länk." +ACF_DOWNLOADBUTTON_LABEL="Knapp etikett" +ACF_DOWNLOADBUTTON_LABEL_DESC="Valfritt: du kan ange en anpassad knappetikett som kommer att användas i frontend. Om fältet lämnas tomt kommer etiketten 'Ladda ner' att användas istället." +ACF_DOWNLOADBUTTON_DOWNLOAD="Ladda ner" +ACF_DOWNLOADBUTTON_CLASS="Knapp klass" +ACF_DOWNLOADBUTTON_CLASS_DESC="Ange en knappklass som kommer att användas i frontend. I de flesta CSS-ramverk kan en knapp designas med klasserna 'btn' eller 'button'" diff --git a/plugins/fields/acfdownloadbutton/language/uk-UA/uk-UA.plg_fields_acfdownloadbutton.ini b/plugins/fields/acfdownloadbutton/language/uk-UA/uk-UA.plg_fields_acfdownloadbutton.ini new file mode 100644 index 00000000..25d38cba --- /dev/null +++ b/plugins/fields/acfdownloadbutton/language/uk-UA/uk-UA.plg_fields_acfdownloadbutton.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFDOWNLOADBUTTON_LABEL="ACF - кнопка завантаження" +ACF_DOWNLOADBUTTON="Поля - кнопка завантаження ACF" +ACF_DOWNLOADBUTTON_DESC="Створити кнопку завантаження файлів легко!" +ACF_DOWNLOADBUTTON_DIR="Каталог" +ACF_DOWNLOADBUTTON_DIR_DESC="Шлях файлової системи до каталогу, що містить файли, що підлягають переліку" +ACF_DOWNLOADBUTTON_FILTER="Включити файли" +ACF_DOWNLOADBUTTON_FILTER_DESC="Це звичайний виразний рядок, який використовується для фільтрації списку файлів, вибраних для включення до випадаючого списку. Якщо цей пункт пропущено, всі файли в каталозі включаються. Аргумент"_QQ_" Включити файли "_QQ_"застосовується перед аргументом"_QQ_" Виключити файли "_QQ_".

Фільтр одного розширення:
.pdf

Фільтрація списку розширень:
.pdf | .zip " +ACF_DOWNLOADBUTTON_EXCLUDE="Виключити файли" +ACF_DOWNLOADBUTTON_EXCLUDE_DESC="Це звичайний рядок виразів, який використовується для виключення файлів зі списку. Вираз"_QQ_" Аргумент виключення файлів "_QQ_"застосовується після виразу аргументу"_QQ_" Включає файли "_QQ_".

Фільтр одного розширення:
.pdf

Фільтрація списку розширень:
.pdf | .zip " +ACF_DOWNLOADBUTTON_TYPE="Тип" +ACF_DOWNLOADBUTTON_TYPE_DESC="Виберіть, чи відображати кнопку завантаження як елемент кнопки чи як посилання." +ACF_DOWNLOADBUTTON_LABEL="Мітка кнопки" +ACF_DOWNLOADBUTTON_LABEL_DESC="Необов’язково введіть спеціальну мітку кнопки, яка буде використовуватися в передній частині. Якщо пропущена, замість цього буде використана мітка"_QQ_" Завантажити "_QQ_"." +ACF_DOWNLOADBUTTON_DOWNLOAD="Завантажити" +ACF_DOWNLOADBUTTON_CLASS="Клас кнопки" +ACF_DOWNLOADBUTTON_CLASS_DESC="Введіть клас клавіш, який буде використовуватися в передній частині. У більшості фреймів CSS кнопку можна стилізувати, використовуючи клас"_QQ_" btn "_QQ_"або"_QQ_" button "_QQ_"" diff --git a/plugins/fields/acfdownloadbutton/params/acfdownloadbutton.xml b/plugins/fields/acfdownloadbutton/params/acfdownloadbutton.xml new file mode 100644 index 00000000..c65dd15f --- /dev/null +++ b/plugins/fields/acfdownloadbutton/params/acfdownloadbutton.xml @@ -0,0 +1,33 @@ + +
+ +
+ + + + + +
+
+
\ No newline at end of file diff --git a/plugins/fields/acfdownloadbutton/script.install.helper.php b/plugins/fields/acfdownloadbutton/script.install.helper.php new file mode 100644 index 00000000..f326a079 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfdownloadbuttonInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfdownloadbutton/script.install.php b/plugins/fields/acfdownloadbutton/script.install.php new file mode 100644 index 00000000..29b71bc1 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFDownloadButtonInstallerScript extends PlgFieldsACFDownloadButtonInstallerScriptHelper +{ + public $alias = 'acfdownloadbutton'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfdownloadbutton/tmpl/acfdownloadbutton.php b/plugins/fields/acfdownloadbutton/tmpl/acfdownloadbutton.php new file mode 100644 index 00000000..48fe25f7 --- /dev/null +++ b/plugins/fields/acfdownloadbutton/tmpl/acfdownloadbutton.php @@ -0,0 +1,35 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Language\Text; + +$file = htmlspecialchars($field->value, ENT_COMPAT, 'UTF-8'); + +if (!$file || $file == '-1') +{ + return; +} + +// Setup Variables +$class = $fieldParams->get('class'); +$label = $fieldParams->get('label', 'ACF_DOWNLOADBUTTON_DOWNLOAD'); +$directory = ltrim($fieldParams->get('directory', 'images'), '/'); +$directory = rtrim($directory, '/'); +$filepath = Uri::root() . $directory . '/' . $file; + +// Output +$buffer = '' . Text::_($label) . ''; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfdownloadbutton/version.php b/plugins/fields/acfdownloadbutton/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfdownloadbutton/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfemail/acfemail.php b/plugins/fields/acfemail/acfemail.php new file mode 100644 index 00000000..a8608270 --- /dev/null +++ b/plugins/fields/acfemail/acfemail.php @@ -0,0 +1,49 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFEmail extends ACF_Field +{ + protected $validate = 'email'; + + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'email'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'email@example.com'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; +} diff --git a/plugins/fields/acfemail/acfemail.xml b/plugins/fields/acfemail/acfemail.xml new file mode 100644 index 00000000..a618122e --- /dev/null +++ b/plugins/fields/acfemail/acfemail.xml @@ -0,0 +1,21 @@ + + + ACF_EMAIL + ACF_EMAIL_DESC + Tassos Marinos + May 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfemail.php + script.install.helper.php + version.php + language + params + tmpl + + diff --git a/plugins/fields/acfemail/language/ca-ES/ca-ES.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/ca-ES/ca-ES.plg_fields_acfemail.ini new file mode 100644 index 00000000..535236a5 --- /dev/null +++ b/plugins/fields/acfemail/language/ca-ES/ca-ES.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - Correu electrònic" +ACF_EMAIL="Camps - ACF Correu electrònic" +ACF_EMAIL_DESC="Mostrar l'adreça de correu electrònic de manera segura a la part pública d'una manera il·legible pels robots d'spam." +ACF_EMAIL_VALUE_DESC="Estableix un correu electrònic" +ACF_EMAIL_CLOAK_SUPPORT="Protegeix el correu electrònic" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Si s'activa, l'adreça de correu electrònic estarà protegida per un script d'ofuscació Anti-Spam que la farà il·legible pels spambots." +ACF_EMAIL_DISPLAY_AS="Mostrar com" +ACF_EMAIL_DISPLAY_AS_DESC="Estableix si mostrar l'adreça de correu electrònic com text plà o si convertir-la en un enllaç mailto." +ACF_EMAIL_TEXT="Text" +ACF_EMAIL_LINK="Enllaç mailto" diff --git a/plugins/fields/acfemail/language/da-DK/da-DK.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/da-DK/da-DK.plg_fields_acfemail.ini new file mode 100644 index 00000000..1028f3ce --- /dev/null +++ b/plugins/fields/acfemail/language/da-DK/da-DK.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - E-mail" +ACF_EMAIL="Felter - ACF E-mail" +ACF_EMAIL_DESC="Placer sikkert e-mailadresser i frontend som er ikke læsbare for spambotter." +ACF_EMAIL_VALUE_DESC="Angiv en e-mailadresse." +ACF_EMAIL_CLOAK_SUPPORT="Gem e-mailadresse" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Hvis aktiveret, så vil e-mailadressen være beskyttet med et anti-spam e-mail skjule script, der gør den ikke læsbar for spambotter." +ACF_EMAIL_DISPLAY_AS="Vis som" +ACF_EMAIL_DISPLAY_AS_DESC="Vælg om e-mailadressen skal vises som ren tekst eller den skal konverteres til et mailto link." +ACF_EMAIL_TEXT="Tekst" +ACF_EMAIL_LINK="Mailto link" diff --git a/plugins/fields/acfemail/language/de-DE/de-DE.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/de-DE/de-DE.plg_fields_acfemail.ini new file mode 100644 index 00000000..95dfdb2a --- /dev/null +++ b/plugins/fields/acfemail/language/de-DE/de-DE.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - E-Mail" +ACF_EMAIL="Felder - ACF-E-Mail" +ACF_EMAIL_DESC="E-Mail-Adressen, die für Spambots nicht lesbar sind, sicher im Front-End anzeigen." +ACF_EMAIL_VALUE_DESC="Legen Sie eine E-Mail-Adresse fest." +ACF_EMAIL_CLOAK_SUPPORT="E-Mail-Adresse verbergen" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Wenn diese Option aktiviert ist, wird die E-Mail-Adresse mit einem Anti-Spam-Skript zum Schutz vor E-Mails geschützt, sodass sie für Spambots nicht lesbar ist." +ACF_EMAIL_DISPLAY_AS="Anzeigen als" +ACF_EMAIL_DISPLAY_AS_DESC="Wählen Sie aus, ob die E-Mail-Adresse als einfacher Text angezeigt oder in einen Mailto-Link konvertiert werden soll." +ACF_EMAIL_TEXT="Text" +ACF_EMAIL_LINK="Mailto Link" diff --git a/plugins/fields/acfemail/language/en-GB/en-GB.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/en-GB/en-GB.plg_fields_acfemail.ini new file mode 100644 index 00000000..5ea7c69d --- /dev/null +++ b/plugins/fields/acfemail/language/en-GB/en-GB.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - Email" +ACF_EMAIL="Fields - ACF Email" +ACF_EMAIL_DESC="Safely display email addresses in the front-end that are unreadbable to spambots." +ACF_EMAIL_VALUE_DESC="Set an email address." +ACF_EMAIL_CLOAK_SUPPORT="Cloak Email Address" +ACF_EMAIL_CLOAK_SUPPORT_DESC="If enabled, the email address will be protected with an Anti-Spam Email Cloaking script making it unreadable to spambots." +ACF_EMAIL_DISPLAY_AS="Display As" +ACF_EMAIL_DISPLAY_AS_DESC="Select whether to display the email address as plain text or convert it to a mailto link." +ACF_EMAIL_TEXT="Text" +ACF_EMAIL_LINK="Mailto Link" \ No newline at end of file diff --git a/plugins/fields/acfemail/language/en-GB/en-GB.plg_fields_acfemail.sys.ini b/plugins/fields/acfemail/language/en-GB/en-GB.plg_fields_acfemail.sys.ini new file mode 100644 index 00000000..d2ea44dd --- /dev/null +++ b/plugins/fields/acfemail/language/en-GB/en-GB.plg_fields_acfemail.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_EMAIL="Fields - ACF Email" +ACF_EMAIL_DESC="Safely display email addresses in the front-end that are unreadbable to spambots." \ No newline at end of file diff --git a/plugins/fields/acfemail/language/es-ES/es-ES.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/es-ES/es-ES.plg_fields_acfemail.ini new file mode 100644 index 00000000..c07560d0 --- /dev/null +++ b/plugins/fields/acfemail/language/es-ES/es-ES.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - Email" +ACF_EMAIL="Campos - ACF Email" +ACF_EMAIL_DESC="Muestre de forma segura las direcciones de correo electrónico en el front-end que son ilegibles para los spambots." +ACF_EMAIL_VALUE_DESC="Establecer una dirección de correo electrónico." +ACF_EMAIL_CLOAK_SUPPORT="Dirección de correo electrónico oculta" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Si está habilitado, la dirección de correo electrónico se protegerá con un script de encubrimiento de correo electrónico antispam que hará que los robots de spam no puedan leerla." +ACF_EMAIL_DISPLAY_AS="Mostrar como" +ACF_EMAIL_DISPLAY_AS_DESC="Seleccione si desea mostrar la dirección de correo electrónico como texto sin formato o convertirla en un enlace mailto." +ACF_EMAIL_TEXT="Texto" +ACF_EMAIL_LINK="Enlace de correo a" diff --git a/plugins/fields/acfemail/language/es-ES/es-ES.plg_fields_acfemail.sys.ini b/plugins/fields/acfemail/language/es-ES/es-ES.plg_fields_acfemail.sys.ini new file mode 100644 index 00000000..75f1bc57 --- /dev/null +++ b/plugins/fields/acfemail/language/es-ES/es-ES.plg_fields_acfemail.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_EMAIL="Campos - ACF Email" +ACF_EMAIL_DESC="Muestre de forma segura las direcciones de correo electrónico en el front-end paraa que sean ilegibles para los spambots." diff --git a/plugins/fields/acfemail/language/it-IT/it-IT.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/it-IT/it-IT.plg_fields_acfemail.ini new file mode 100644 index 00000000..1ec20f81 --- /dev/null +++ b/plugins/fields/acfemail/language/it-IT/it-IT.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - Email" +ACF_EMAIL="Campi - Email ACF " +ACF_EMAIL_DESC="Mostra indirizzi email in sicurezza nel front-end, che siano illeggibili per gli spambot." +ACF_EMAIL_VALUE_DESC="Imposta un indirizzo email." +ACF_EMAIL_CLOAK_SUPPORT="Maschera indirizzo email" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Se abilitato l'indirizzo email sarà protetto con una mascheramento anti-spam che lo rende illeggibile per gli spambot." +ACF_EMAIL_DISPLAY_AS="Mostra come" +ACF_EMAIL_DISPLAY_AS_DESC="Scegli se mostrare l'indirizzo email come testo semplice o convertirlo a collegamento mailto." +ACF_EMAIL_TEXT="Testo" +ACF_EMAIL_LINK="Collegamento mailto" diff --git a/plugins/fields/acfemail/language/nl-NL/nl-NL.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/nl-NL/nl-NL.plg_fields_acfemail.ini new file mode 100644 index 00000000..1bd16170 --- /dev/null +++ b/plugins/fields/acfemail/language/nl-NL/nl-NL.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - Email" +ACF_EMAIL="Velden - ACF Email" +ACF_EMAIL_DESC="Toon veilig e-mailadressen aan de front-end die onleesbaar zullen zijn voor spambots." +ACF_EMAIL_VALUE_DESC="Stel een e-mailadres in." +ACF_EMAIL_CLOAK_SUPPORT="Verberg e-mailadres" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Indien ingeschakeld, wordt het e-mailadres beschermd met een Anti-Spam Email verberg-script, waardoor het onleesbaar is voor spambots." +ACF_EMAIL_DISPLAY_AS="Weergeven als" +ACF_EMAIL_DISPLAY_AS_DESC="Selecteer of het e-mailadres als platte tekst moet worden weergegeven of moet worden omgezet in een mailto-link." +ACF_EMAIL_TEXT="Tekst" +ACF_EMAIL_LINK="Mailto Link" diff --git a/plugins/fields/acfemail/language/ru-RU/ru-RU.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/ru-RU/ru-RU.plg_fields_acfemail.ini new file mode 100644 index 00000000..637c44f1 --- /dev/null +++ b/plugins/fields/acfemail/language/ru-RU/ru-RU.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - электронная почта" +ACF_EMAIL="Поля - электронная почта ACF" +ACF_EMAIL_DESC="Безопасное отображение адресов электронной почты в интерфейсе, недоступных для спам-ботов." +ACF_EMAIL_VALUE_DESC="Установить адрес электронной почты." +ACF_EMAIL_CLOAK_SUPPORT="Скрытый адрес электронной почты" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Если этот параметр включен, адрес электронной почты будет защищен сценарием защиты от спама в электронной почте, что сделает его нечитаемым для спам-ботов." +ACF_EMAIL_DISPLAY_AS="Показать как" +ACF_EMAIL_DISPLAY_AS_DESC="Выберите, отображать ли адрес электронной почты в виде простого текста или преобразовать его в ссылку mailto." +ACF_EMAIL_TEXT="Текст" +ACF_EMAIL_LINK="Ссылка на почту" diff --git a/plugins/fields/acfemail/language/sv-SE/sv-SE.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/sv-SE/sv-SE.plg_fields_acfemail.ini new file mode 100644 index 00000000..9da4fb5b --- /dev/null +++ b/plugins/fields/acfemail/language/sv-SE/sv-SE.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - E-post" +ACF_EMAIL="Fält - ACF E-post" +ACF_EMAIL_DESC="Visa e-postadresser säkert i frontend, på ett sätt som är oläsbart för spamrobotar." +ACF_EMAIL_VALUE_DESC="Ange en e-postadress." +ACF_EMAIL_CLOAK_SUPPORT="Skydda e-postadress" +ACF_EMAIL_CLOAK_SUPPORT_DESC="Om aktiverad, kommer e-postadressen att skyddas med ett antispam-e-postskydd som gör den oläsbar för spamrobotar." +ACF_EMAIL_DISPLAY_AS="Visa som" +ACF_EMAIL_DISPLAY_AS_DESC="Välj om e-postadressen ska visas som vanlig text eller om den ska konverteras till en mailto-länk." +ACF_EMAIL_TEXT="Text" +ACF_EMAIL_LINK="Mailto Länk" diff --git a/plugins/fields/acfemail/language/uk-UA/uk-UA.plg_fields_acfemail.ini b/plugins/fields/acfemail/language/uk-UA/uk-UA.plg_fields_acfemail.ini new file mode 100644 index 00000000..351b826c --- /dev/null +++ b/plugins/fields/acfemail/language/uk-UA/uk-UA.plg_fields_acfemail.ini @@ -0,0 +1,17 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFEMAIL_LABEL="ACF - Email" +ACF_EMAIL="Fields - ACF Email" +ACF_EMAIL_DESC="Safely display email addresses in the front-end that are unreadbable to spambots." +ACF_EMAIL_VALUE_DESC="Set an email address." +ACF_EMAIL_CLOAK_SUPPORT="Cloak Email Address" +ACF_EMAIL_CLOAK_SUPPORT_DESC="If enabled, the email address will be protected with an Anti-Spam Email Cloaking script making it unreadable to spambots." +ACF_EMAIL_DISPLAY_AS="Display As" +ACF_EMAIL_DISPLAY_AS_DESC="Select whether to display the email address as plain text or convert it to a mailto link." +ACF_EMAIL_TEXT="Text" +ACF_EMAIL_LINK="Mailto Link" diff --git a/plugins/fields/acfemail/params/acfemail.xml b/plugins/fields/acfemail/params/acfemail.xml new file mode 100644 index 00000000..7e1d31de --- /dev/null +++ b/plugins/fields/acfemail/params/acfemail.xml @@ -0,0 +1,20 @@ + +
+ +
+ + + + + +
+
+
diff --git a/plugins/fields/acfemail/script.install.helper.php b/plugins/fields/acfemail/script.install.helper.php new file mode 100644 index 00000000..598ce400 --- /dev/null +++ b/plugins/fields/acfemail/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfemailInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfemail/script.install.php b/plugins/fields/acfemail/script.install.php new file mode 100644 index 00000000..f13ff017 --- /dev/null +++ b/plugins/fields/acfemail/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFEmailInstallerScript extends PlgFieldsACFEmailInstallerScriptHelper +{ + public $alias = 'acfemail'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfemail/tmpl/acfemail.php b/plugins/fields/acfemail/tmpl/acfemail.php new file mode 100644 index 00000000..eb17cd27 --- /dev/null +++ b/plugins/fields/acfemail/tmpl/acfemail.php @@ -0,0 +1,49 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\HTML\HTMLHelper; + +if (!$email = $field->value) +{ + return; +} + +// Check if valid email is given +if (!filter_var($email, FILTER_VALIDATE_EMAIL)) +{ + return; +} + +// Get field params +$cloak_support = $fieldParams->get('cloak_support', true); +$display_as = $fieldParams->get('display_as', 'link'); + +// We have selected cloak support +if ($cloak_support) { + if ($display_as == 'text') { + // Cloak it but display it as text + $buffer = HTMLHelper::_('email.cloak', $email, 0); + } else { + // Cloak it but display it as a link + $buffer = HTMLHelper::_('email.cloak', $email); + } +} else { + if ($display_as == 'text') { + $buffer = $email; + } else { + $buffer = '' . $email . ''; + } +} + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfemail/version.php b/plugins/fields/acfemail/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfemail/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acffacebook/acffacebook.php b/plugins/fields/acffacebook/acffacebook.php new file mode 100644 index 00000000..b25294a0 --- /dev/null +++ b/plugins/fields/acffacebook/acffacebook.php @@ -0,0 +1,47 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFFacebook extends ACF_Field +{ + /** + * The validation rule will be used to validate the field on saving + * + * @var string + */ + protected $validate = 'url'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'ACF_FACEBOOK_HINT'; +} diff --git a/plugins/fields/acffacebook/acffacebook.xml b/plugins/fields/acffacebook/acffacebook.xml new file mode 100644 index 00000000..d65683a8 --- /dev/null +++ b/plugins/fields/acffacebook/acffacebook.xml @@ -0,0 +1,21 @@ + + + ACF_FACEBOOK + ACF_FACEBOOK_DESC + Tassos Marinos + July 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acffacebook.php + script.install.helper.php + version.php + language + params + tmpl + + diff --git a/plugins/fields/acffacebook/language/ca-ES/ca-ES.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/ca-ES/ca-ES.plg_fields_acffacebook.ini new file mode 100644 index 00000000..3c9081dc --- /dev/null +++ b/plugins/fields/acffacebook/language/ca-ES/ca-ES.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Camps - ACF Facebook" +ACF_FACEBOOK_DESC="Escriu l'URL d'una pàgina Facebook per incrustar-la i promoure-la permetent als visitants fer 'like' i compartir la pagina sense abandonar el teu lloc" +ACF_FACEBOOK_HINT="Escriu l'URL de la pàgina Facebook" +ACF_FACEBOOK_VALUE_DESC="Escriu l'URL de la pàgina Facebook" +ACF_FACEBOOK_WIDTH="amplada" +ACF_FACEBOOK_WIDTH_DESC="Estableix l'amplada de l'incrustat" +ACF_FACEBOOK_HEIGHT="Alçada" +ACF_FACEBOOK_HEIGHT_DESC="Estableix l'alçada de l'incrustat" +ACF_FACEBOOK_TABS="Pestanyes" +ACF_FACEBOOK_TABS_DESC="Pestanyes per renderitzar, com Línia de temps, esdeveniments i missatges" +ACF_FACEBOOK_TIMELINE="Línia de temps" +ACF_FACEBOOK_EVENTS="Esdeveniments" +ACF_FACEBOOK_MESSAGES="Missatges" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Amagar foto de portada" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Amagar la foto de portada a la capçalera" +ACF_FACEBOOK_SMALL_HEADER="Utilitzar capçalera petita" +ACF_FACEBOOK_SMALL_HEADER_DESC="Utilitza una versió reduida de la capçalera mostrant una foto de portada més petita" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Mostrar cares d'amics" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Mostra fotos de perfil d'amics als que els agrada auqesta pàgina" +ACF_FACEBOOK_HIDE_CTA="Amagar el botó crida a l'acció" +ACF_FACEBOOK_HIDE_CTA_DESC="Amaga el botó crida a l'acció (si està activat a l'administració de la pàgina)" +ACF_FACEBOOK_WIDGET="Giny" +ACF_FACEBOOK_WIDGET_DESC="Escull un giny a mostrar" +ACF_FACEBOOK_FBPAGE="Pàgina Facebook" diff --git a/plugins/fields/acffacebook/language/da-DK/da-DK.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/da-DK/da-DK.plg_fields_acffacebook.ini new file mode 100644 index 00000000..cff8a0b2 --- /dev/null +++ b/plugins/fields/acffacebook/language/da-DK/da-DK.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Felter - ACF Facebook" +ACF_FACEBOOK_DESC="Angiv en Facebook side URL der skal indlejres for at promovere en side ved at lade dine besøgende like og dele siden uden at forlade dit webstedr site" +ACF_FACEBOOK_HINT="Angiv en Facebook side URL" +ACF_FACEBOOK_VALUE_DESC="Angiv en Facebook side URL" +ACF_FACEBOOK_WIDTH="Bredde" +ACF_FACEBOOK_WIDTH_DESC="Sæt bredden på indlejringenthe width of the embed" +ACF_FACEBOOK_HEIGHT="Højde" +ACF_FACEBOOK_HEIGHT_DESC="Sæt højden på indlejringen" +ACF_FACEBOOK_TABS="Faner" +ACF_FACEBOOK_TABS_DESC="Faner der skal gengives såsom tidslinje, begivenheder og beskeder" +ACF_FACEBOOK_TIMELINE="Tidslinje" +ACF_FACEBOOK_EVENTS="Begivenheder" +ACF_FACEBOOK_MESSAGES="Beskeder" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Skjul coverbillede" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Skjul cover billedet i headerenphoto in the header" +ACF_FACEBOOK_SMALL_HEADER="Anvend Small Header" +ACF_FACEBOOK_SMALL_HEADER_DESC="Anvend en mindre version af headeren ved at vise et mindre coverbillede" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Vis venners ansigterFriend's Faces" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Vis profilbilleder af venner som liker denne side" +ACF_FACEBOOK_HIDE_CTA="Skjul Kald til handlings knappenCall to Action button" +ACF_FACEBOOK_HIDE_CTA_DESC="Skjul den brugerdefinerede Kald til handling knap (hvis aktiveret af Side admin)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Vælg et widget til visning display" +ACF_FACEBOOK_FBPAGE="Facebook side" diff --git a/plugins/fields/acffacebook/language/de-DE/de-DE.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/de-DE/de-DE.plg_fields_acffacebook.ini new file mode 100644 index 00000000..557b6333 --- /dev/null +++ b/plugins/fields/acffacebook/language/de-DE/de-DE.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Felder - ACF Facebook" +ACF_FACEBOOK_DESC="Geben Sie eine Facebook-Seiten-URL ein, um eine Seite einzubetten und zu bewerben, indem Sie Ihren Besuchern erlauben, die Seite zu mögen und zu teilen, ohne Ihre Website zu verlassen." +ACF_FACEBOOK_HINT="URL einer Facebook-Seite eingeben" +ACF_FACEBOOK_VALUE_DESC="URL einer Facebook-Seite eingeben" +ACF_FACEBOOK_WIDTH="Breite" +ACF_FACEBOOK_WIDTH_DESC="Breite der Einbettung festlegen" +ACF_FACEBOOK_HEIGHT="Höhe" +ACF_FACEBOOK_HEIGHT_DESC="Höhe der Einbettung festlegen" +ACF_FACEBOOK_TABS="Tabs" +ACF_FACEBOOK_TABS_DESC="Zu rendernde Registerkarten wie Zeitleiste, Ereignisse und Nachrichten" +ACF_FACEBOOK_TIMELINE="Zeitachse" +ACF_FACEBOOK_EVENTS="Ereignisse" +ACF_FACEBOOK_MESSAGES="Nachrichten" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Titelbild ausblenden" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Titelbild in der Kopfzeile ausblenden" +ACF_FACEBOOK_SMALL_HEADER="Benutze kleinen Header" +ACF_FACEBOOK_SMALL_HEADER_DESC="Verwenden Sie eine kleinere Version der Kopfzeile, indem Sie ein kleineres Titelbild anzeigen." +ACF_FACEBOOK_SHOW_FRIEND_FACES="Gesichter von Freunden anzeigen" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Profilfotos von Freunden anzeigen, denen diese Seite gefällt" +ACF_FACEBOOK_HIDE_CTA="Call-to-Action-Schaltfläche ausblenden" +ACF_FACEBOOK_HIDE_CTA_DESC="Benutzerdefinierte Aktionsaufruftaste ausblenden (falls von Seitenadministrator aktiviert)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Anzuzeigendes Widget auswählen" +ACF_FACEBOOK_FBPAGE="Facebook-Seite" diff --git a/plugins/fields/acffacebook/language/en-GB/en-GB.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/en-GB/en-GB.plg_fields_acffacebook.ini new file mode 100644 index 00000000..f67ec0b9 --- /dev/null +++ b/plugins/fields/acffacebook/language/en-GB/en-GB.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Fields - ACF Facebook" +ACF_FACEBOOK_DESC="Enter a Facebook Page URL to embed and promote a page by letting your visitors like and share the Page without leaving your site" +ACF_FACEBOOK_HINT="Enter a Facebook Page URL" +ACF_FACEBOOK_VALUE_DESC="Enter a Facebook Page URL" +ACF_FACEBOOK_WIDTH="Width" +ACF_FACEBOOK_WIDTH_DESC="Set the width of the embed" +ACF_FACEBOOK_HEIGHT="Height" +ACF_FACEBOOK_HEIGHT_DESC="Set the height of the embed" +ACF_FACEBOOK_TABS="Tabs" +ACF_FACEBOOK_TABS_DESC="Tabs to render such as Timeline, Events and Messages" +ACF_FACEBOOK_TIMELINE="Timeline" +ACF_FACEBOOK_EVENTS="Events" +ACF_FACEBOOK_MESSAGES="Messages" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Hide Cover Photo" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Hide the cover photo in the header" +ACF_FACEBOOK_SMALL_HEADER="Use Small Header" +ACF_FACEBOOK_SMALL_HEADER_DESC="Use a smaller version of the header by displaying a smaller cover photo" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Show Friend's Faces" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Show profile photos of friends who like this page" +ACF_FACEBOOK_HIDE_CTA="Hide Call to Action button" +ACF_FACEBOOK_HIDE_CTA_DESC="Hide the custom Call to Action button (if enabled by Page Admin)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Select a widget to display" +ACF_FACEBOOK_FBPAGE="Facebook Page" \ No newline at end of file diff --git a/plugins/fields/acffacebook/language/en-GB/en-GB.plg_fields_acffacebook.sys.ini b/plugins/fields/acffacebook/language/en-GB/en-GB.plg_fields_acffacebook.sys.ini new file mode 100644 index 00000000..9faa491d --- /dev/null +++ b/plugins/fields/acffacebook/language/en-GB/en-GB.plg_fields_acffacebook.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_FACEBOOK="Fields - ACF Facebook" +ACF_FACEBOOK_DESC="Enter a Facebook Page URL to embed and promote a page by letting your visitors like and share the Page without leaving your site" \ No newline at end of file diff --git a/plugins/fields/acffacebook/language/es-ES/es-ES.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/es-ES/es-ES.plg_fields_acffacebook.ini new file mode 100644 index 00000000..9b2ea5f5 --- /dev/null +++ b/plugins/fields/acffacebook/language/es-ES/es-ES.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Campos - ACF Facebook" +ACF_FACEBOOK_DESC="Ingrese la URL de una página de Facebook para incrustar y promocionar una página al permitir que a sus visitantes les guste y compartan la página sin salir de su sitio." +ACF_FACEBOOK_HINT="Introduzca la URL de la página de Facebook" +ACF_FACEBOOK_VALUE_DESC="Introduca la URL de la página de Facebook" +ACF_FACEBOOK_WIDTH="Ancho" +ACF_FACEBOOK_WIDTH_DESC="Establecer el ancho de la inserción" +ACF_FACEBOOK_HEIGHT="Alto" +ACF_FACEBOOK_HEIGHT_DESC="Establecer el alto de la inserción" +ACF_FACEBOOK_TABS="Pestañas" +ACF_FACEBOOK_TABS_DESC="Pestañas para renderizar como Cronologías, Eventos y Mensajes" +ACF_FACEBOOK_TIMELINE="Cronología" +ACF_FACEBOOK_EVENTS="Eventos" +ACF_FACEBOOK_MESSAGES="Mensajes" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Ocultar foto de portada" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Ocultar la foto de portada en el encabezado" +ACF_FACEBOOK_SMALL_HEADER="Usar encabezado pequeño" +ACF_FACEBOOK_SMALL_HEADER_DESC="Use una versión más pequeña del encabezado mostrando una foto de portada más pequeña" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Mostrar Caras de Amigos" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Mostrar fotos de perfil de amigos que les gusta esta página." +ACF_FACEBOOK_HIDE_CTA="Ocultar botón de llamada a la acción" +ACF_FACEBOOK_HIDE_CTA_DESC="Oculte el botón de llamada a la acción personalizado (si está habilitado por el administrador de la página)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Seleccione un widget para mostrar" +ACF_FACEBOOK_FBPAGE="Página de Facebook" diff --git a/plugins/fields/acffacebook/language/es-ES/es-ES.plg_fields_acffacebook.sys.ini b/plugins/fields/acffacebook/language/es-ES/es-ES.plg_fields_acffacebook.sys.ini new file mode 100644 index 00000000..99ccb453 --- /dev/null +++ b/plugins/fields/acffacebook/language/es-ES/es-ES.plg_fields_acffacebook.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_FACEBOOK="Campos - ACF Facebook" +ACF_FACEBOOK_DESC="Ingrese la URL de una página de Facebook para incrustar y promocionar una página al permitir que a sus visitantes les guste y compartan la página sin salir de su sitio." diff --git a/plugins/fields/acffacebook/language/it-IT/it-IT.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/it-IT/it-IT.plg_fields_acffacebook.ini new file mode 100644 index 00000000..16ed6f5d --- /dev/null +++ b/plugins/fields/acffacebook/language/it-IT/it-IT.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Campi - ACF Facebook" +ACF_FACEBOOK_DESC="Inserisci l'URL di una pagina Facebook per incorporare e promuovere una pagina consentendo ai tuoi visitatori di mettere mi piace e condividere la pagina senza lasciare il tuo sito" +ACF_FACEBOOK_HINT="Inserisci l'URL di una pagina Facebook" +ACF_FACEBOOK_VALUE_DESC="Inserisci l'URL di una pagina Facebook" +ACF_FACEBOOK_WIDTH="Larghezza" +ACF_FACEBOOK_WIDTH_DESC="Imposta la larghezza dell'incorporamento" +ACF_FACEBOOK_HEIGHT="Altezza" +ACF_FACEBOOK_HEIGHT_DESC="Imposta l'altezza dell'incorporamento" +ACF_FACEBOOK_TABS="Schede" +ACF_FACEBOOK_TABS_DESC="Schede per la presentazione di linee del tempo, eventi e messaggi" +ACF_FACEBOOK_TIMELINE="Linea temporale" +ACF_FACEBOOK_EVENTS="Eventi" +ACF_FACEBOOK_MESSAGES="Messaggi" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Nascondi foto di copertina" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Nascondi la foto di copertina nell'intestazione" +ACF_FACEBOOK_SMALL_HEADER="Usa intestazione piccola" +ACF_FACEBOOK_SMALL_HEADER_DESC="Utilizza una versione più piccola dell'intestazione visualizzando una foto di copertina più piccola" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Mostra i volti degli amici" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Mostra le foto del profilo degli amici a cui piace questa pagina" +ACF_FACEBOOK_HIDE_CTA="Nascondi pulsante di chiamata all'azione" +ACF_FACEBOOK_HIDE_CTA_DESC="Nascondi il pulsante di chiamata all'azione personalizzato (se abilitato dall'amministratore della pagina)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Seleziona un widget da visualizzare" +ACF_FACEBOOK_FBPAGE="Pagina Facebook" diff --git a/plugins/fields/acffacebook/language/nl-NL/nl-NL.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/nl-NL/nl-NL.plg_fields_acffacebook.ini new file mode 100644 index 00000000..189ad588 --- /dev/null +++ b/plugins/fields/acffacebook/language/nl-NL/nl-NL.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Velden - ACF Facebook" +ACF_FACEBOOK_DESC="Voer een Facebook-pagina-URL in om een pagina te integreren en te promoten door jouw bezoekers je pagina te laten liken en te delen zonder dat ze je site te verlaten" +ACF_FACEBOOK_HINT="Voer een Facebook-pagina-URL in" +ACF_FACEBOOK_VALUE_DESC="Voer een Facebook-pagina-URL in" +ACF_FACEBOOK_WIDTH="Breedte" +ACF_FACEBOOK_WIDTH_DESC="Stel de breedte van de embed in" +ACF_FACEBOOK_HEIGHT="Hoogte" +ACF_FACEBOOK_HEIGHT_DESC="Stel de hoogte van de embed in" +ACF_FACEBOOK_TABS="Tabbladen" +ACF_FACEBOOK_TABS_DESC="Weer te geven tabbladen zoals tijdlijn, gebeurtenissen en berichten" +ACF_FACEBOOK_TIMELINE="Tijdlijn" +ACF_FACEBOOK_EVENTS="Gebeurtenissen" +ACF_FACEBOOK_MESSAGES="Berichten" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Verberg de omslag foto" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Verberg de omslagfoto in de koptekt" +ACF_FACEBOOK_SMALL_HEADER="Gebruik kleine koptekst" +ACF_FACEBOOK_SMALL_HEADER_DESC="Gebruik een kleinere versie van de koptekst door een kleinere omslagfoto te tonen" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Laat de gezichten van vrienden zien" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Toon profielfoto's van vrienden die deze pagina leuk vinden" +ACF_FACEBOOK_HIDE_CTA="Verberg de Call to Action knop" +ACF_FACEBOOK_HIDE_CTA_DESC="Verberg de aangepaste call-to-action-knop (indien ingeschakeld door Page Admin)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Selecteer een widget om weer te geven" +ACF_FACEBOOK_FBPAGE="Facebookpagina" diff --git a/plugins/fields/acffacebook/language/ru-RU/ru-RU.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/ru-RU/ru-RU.plg_fields_acffacebook.ini new file mode 100644 index 00000000..f73f3ab8 --- /dev/null +++ b/plugins/fields/acffacebook/language/ru-RU/ru-RU.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Поля - ACF Facebook" +ACF_FACEBOOK_DESC="Введите URL-адрес страницы Facebook, чтобы вставлять и продвигать страницу, позволяя своим посетителям лайкать и делиться страницей, не покидая своего сайта" +ACF_FACEBOOK_HINT="Введите URL страницы Facebook" +ACF_FACEBOOK_VALUE_DESC="Введите URL страницы Facebook" +ACF_FACEBOOK_WIDTH="Ширина" +ACF_FACEBOOK_WIDTH_DESC="Установить ширину встраивания" +ACF_FACEBOOK_HEIGHT="Высота" +ACF_FACEBOOK_HEIGHT_DESC="Установить высоту встраивания" +ACF_FACEBOOK_TABS="Вкладка" +ACF_FACEBOOK_TABS_DESC="Вкладки для отображения, такие как временная шкала, события и сообщения" +ACF_FACEBOOK_TIMELINE="Временная шкала" +ACF_FACEBOOK_EVENTS="Событие" +ACF_FACEBOOK_MESSAGES="Сообщения" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Скрыть обложку" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Скрыть фотографию обложки в шапке" +ACF_FACEBOOK_SMALL_HEADER="Использовать маленький заголовок" +ACF_FACEBOOK_SMALL_HEADER_DESC="Использовать уменьшенную версию заголовка, отображая уменьшенную фотографию обложки" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Показать лица друзей" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Показать фотографии профиля друзей, которым нравится эта страница" +ACF_FACEBOOK_HIDE_CTA="Скрыть кнопку призыва к действию" +ACF_FACEBOOK_HIDE_CTA_DESC="Скрыть пользовательскую кнопку призыва к действию (если она включена администратором страницы)" +ACF_FACEBOOK_WIDGET="Виджет" +ACF_FACEBOOK_WIDGET_DESC="Выберите виджет для отображения" +ACF_FACEBOOK_FBPAGE="Страница Facebook" diff --git a/plugins/fields/acffacebook/language/sv-SE/sv-SE.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/sv-SE/sv-SE.plg_fields_acffacebook.ini new file mode 100644 index 00000000..d1bb684a --- /dev/null +++ b/plugins/fields/acffacebook/language/sv-SE/sv-SE.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Fält - ACF Facebook" +ACF_FACEBOOK_DESC="Ange en URL till Facebook-sidan för att bädda in och marknadsföra en sida genom att låta dina besökare gilla och dela sidan utan att lämna din webbplats" +ACF_FACEBOOK_HINT="Ange en URL till Facebook-sidan" +ACF_FACEBOOK_VALUE_DESC="Ange en URL till Facebook-sidan" +ACF_FACEBOOK_WIDTH="Bredd" +ACF_FACEBOOK_WIDTH_DESC="Ange bredd på inbäddningen." +ACF_FACEBOOK_HEIGHT="Höjd" +ACF_FACEBOOK_HEIGHT_DESC="Ange höjd på inbäddningen." +ACF_FACEBOOK_TABS="Flikar" +ACF_FACEBOOK_TABS_DESC="Flikar som ska visas som tidslinje, händelser och meddelanden." +ACF_FACEBOOK_TIMELINE="Tidslinje" +ACF_FACEBOOK_EVENTS="Händelser" +ACF_FACEBOOK_MESSAGES="Meddelanden" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Dölj omslagsbild" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Dölj omslagsbilden i sidhuvudet." +ACF_FACEBOOK_SMALL_HEADER="Använd litet sidhuvud" +ACF_FACEBOOK_SMALL_HEADER_DESC="Använd en mindre version av sidhuvudet genom att visa en mindre omslagsbild." +ACF_FACEBOOK_SHOW_FRIEND_FACES="Visa vänners profilbilder." +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Visa profilbilder av vänner som gillar den här sidan." +ACF_FACEBOOK_HIDE_CTA="Dölj Call to Action-knapp" +ACF_FACEBOOK_HIDE_CTA_DESC="Dölj den anpassade Callt to Action-knappen (om aktiverad av sidadministratör)" +ACF_FACEBOOK_WIDGET="Widget" +ACF_FACEBOOK_WIDGET_DESC="Välj en widget att visa" +ACF_FACEBOOK_FBPAGE="Facebook sida" diff --git a/plugins/fields/acffacebook/language/uk-UA/uk-UA.plg_fields_acffacebook.ini b/plugins/fields/acffacebook/language/uk-UA/uk-UA.plg_fields_acffacebook.ini new file mode 100644 index 00000000..74f21ca2 --- /dev/null +++ b/plugins/fields/acffacebook/language/uk-UA/uk-UA.plg_fields_acffacebook.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + + +PLG_FIELDS_ACFFACEBOOK_LABEL="ACF - Facebook" +ACF_FACEBOOK="Поля - ACF Facebook" +ACF_FACEBOOK_DESC="Введіть URL-адресу сторінки Facebook, щоб вбудувати та просувати сторінку, дозволяючи відвідувачам подобатися та ділитися Сторінкою, не виходячи з вашого сайту" +ACF_FACEBOOK_HINT="Введіть URL-адресу сторінки Facebook" +ACF_FACEBOOK_VALUE_DESC="Введіть URL-адресу сторінки Facebook" +ACF_FACEBOOK_WIDTH="Ширина" +ACF_FACEBOOK_WIDTH_DESC="Встановити ширину вставки" +ACF_FACEBOOK_HEIGHT="Висота" +ACF_FACEBOOK_HEIGHT_DESC="Встановити висоту вставки" +ACF_FACEBOOK_TABS="Вкладки" +ACF_FACEBOOK_TABS_DESC="Вкладки для візуалізації, такі як часова шкала, події та повідомлення" +ACF_FACEBOOK_TIMELINE="Хронологія" +ACF_FACEBOOK_EVENTS="Події" +ACF_FACEBOOK_MESSAGES="Повідомлення" +ACF_FACEBOOK_HIDE_COVER_PHOTO="Сховати обкладинку" +ACF_FACEBOOK_HIDE_COVER_PHOTO_DESC="Сховати обкладинку в заголовку" +ACF_FACEBOOK_SMALL_HEADER="Використовувати невеликий заголовок" +ACF_FACEBOOK_SMALL_HEADER_DESC="Використовувати меншу версію заголовка, відображаючи меншу обкладинку" +ACF_FACEBOOK_SHOW_FRIEND_FACES="Показати обличчя друга" +ACF_FACEBOOK_SHOW_FRIEND_FACES_DESC="Показати фотографії профілю друзів, яким подобається ця сторінка" +ACF_FACEBOOK_HIDE_CTA="Приховати кнопку виклику до дії" +ACF_FACEBOOK_HIDE_CTA_DESC="Приховати спеціальну кнопку"_QQ_" Заклик до дії "_QQ_"(якщо її ввімкнено Адміністратором сторінки)" +ACF_FACEBOOK_WIDGET="Віджет" +ACF_FACEBOOK_WIDGET_DESC="Вибрати віджет для відображення" +ACF_FACEBOOK_FBPAGE="Сторінка Facebook" diff --git a/plugins/fields/acffacebook/params/acffacebook.xml b/plugins/fields/acffacebook/params/acffacebook.xml new file mode 100644 index 00000000..78766b3b --- /dev/null +++ b/plugins/fields/acffacebook/params/acffacebook.xml @@ -0,0 +1,57 @@ + +
+ +
+ + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acffacebook/script.install.helper.php b/plugins/fields/acffacebook/script.install.helper.php new file mode 100644 index 00000000..767486d1 --- /dev/null +++ b/plugins/fields/acffacebook/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcffacebookInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acffacebook/script.install.php b/plugins/fields/acffacebook/script.install.php new file mode 100644 index 00000000..f48c5f02 --- /dev/null +++ b/plugins/fields/acffacebook/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFFacebookInstallerScript extends PlgFieldsACFFacebookInstallerScriptHelper +{ + public $alias = 'acffacebook'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acffacebook/tmpl/acffacebook.php b/plugins/fields/acffacebook/tmpl/acffacebook.php new file mode 100644 index 00000000..8dc42792 --- /dev/null +++ b/plugins/fields/acffacebook/tmpl/acffacebook.php @@ -0,0 +1,14 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +require __DIR__ . '/plugins/' . $fieldParams->get('widget_type', 'page') . '.php'; \ No newline at end of file diff --git a/plugins/fields/acffacebook/tmpl/plugins/page.php b/plugins/fields/acffacebook/tmpl/plugins/page.php new file mode 100644 index 00000000..3b0249b0 --- /dev/null +++ b/plugins/fields/acffacebook/tmpl/plugins/page.php @@ -0,0 +1,46 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$page_url = $field->value) +{ + return; +} + +// Setup Variables +$id = 'acf_facebook_' . $item->id . '_' . $field->id; +$width = str_replace('px', '', $fieldParams->get('page.width', 400)); +$height = str_replace('px', '', $fieldParams->get('page.height', 214)); +$tabs = $fieldParams->get('page.tabs', ''); + +// if tabs is an array, it means we have selected more than 1 tab, separate them via comma +if (is_array($tabs)) +{ + $tabs = implode(', ', $tabs); +} + +$hide_cover_photo = $fieldParams->get('page.hide_cover_photo', false); +$small_header = $fieldParams->get('page.small_header', false); +$show_friend_faces = $fieldParams->get('page.show_friend_faces', true); +$hide_cta = $fieldParams->get('page.hide_cta', false); + +// Output +echo ' + '; \ No newline at end of file diff --git a/plugins/fields/acffacebook/version.php b/plugins/fields/acffacebook/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acffacebook/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acffaq/acffaq.php b/plugins/fields/acffaq/acffaq.php new file mode 100644 index 00000000..2e8db6f3 --- /dev/null +++ b/plugins/fields/acffaq/acffaq.php @@ -0,0 +1,87 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Language\Text; +use Joomla\CMS\HTML\HTMLHelper; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFFAQ extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'FAQ'; + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param JForm $form The form + * @param stdClass $data The data + * + * @return void + */ + public function onContentPrepareForm(Form $form, $data) + { + $data = (object) $data; + + // Make sure we are manipulating the right field. + if (isset($data->type) && $data->type != $this->_name) + { + return; + } + + /** + * Set the configuration for the templates. + * + * These are handed over to Javascript and + * whenever we click on a preset, these values + * are set to each setting on the backend. + */ + if (Factory::getApplication()->isClient('administrator')) + { + Text::script('ACF_FIELD_PREVIEWER'); + Text::script('ACF_FIELD_PREVIEWER_INFO_ICON_TITLE'); + + // Include presets + include 'fields/helper.php'; + + $script = 'window.ACFFAQPresetsData = ' . json_encode($presets) . ';'; + Factory::getDocument()->addScriptDeclaration($script); + HTMLHelper::script('plg_system_nrframework/tffieldsvaluesapplier.js', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_fields_acffaq/faq.js', ['relative' => true, 'version' => 'auto']); + + $script = 'window.ACFFieldsPreviewerData = ' . json_encode([ + 'fullscreenActions' => true, + 'responsiveControls' => true + ]) . ';'; + Factory::getDocument()->addScriptDeclaration($script); + HTMLHelper::script('plg_fields_acffaq/previewer.js', ['relative' => true, 'version' => 'auto']); + } + + + return parent::onContentPrepareForm($form, $data); + } +} \ No newline at end of file diff --git a/plugins/fields/acffaq/acffaq.xml b/plugins/fields/acffaq/acffaq.xml new file mode 100644 index 00000000..1779b8b6 --- /dev/null +++ b/plugins/fields/acffaq/acffaq.xml @@ -0,0 +1,26 @@ + + + ACF_FAQ + ACF_FAQ_DESC + Tassos Marinos + April 2023 + Copyright (C) 2023 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acffaq.php + script.install.helper.php + version.php + fields + language + params + tmpl + + + js + img + + diff --git a/plugins/fields/acffaq/fields/faq.php b/plugins/fields/acffaq/fields/faq.php new file mode 100644 index 00000000..a698aa7a --- /dev/null +++ b/plugins/fields/acffaq/fields/faq.php @@ -0,0 +1,57 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Form\Field\SubformField; + +class JFormFieldFAQ extends SubformField +{ + /** + * Method to attach a JForm object to the field. + * + * @param SimpleXMLElement $element The SimpleXMLElement object representing the tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. + * + * @return boolean True on success. + * + * @since 3.6 + */ + public function setup(SimpleXMLElement $element, $value, $group = null) + { + if (!parent::setup($element, $value, $group)) + { + return false; + } + + $xml = << +
+ + +XML; + $this->formsource = $xml; + + return true; + } + + public function getInput() + { + return '
' . parent::getInput() . '
'; + } +} diff --git a/plugins/fields/acffaq/fields/faqschematoggle.php b/plugins/fields/acffaq/fields/faqschematoggle.php new file mode 100644 index 00000000..1e9be562 --- /dev/null +++ b/plugins/fields/acffaq/fields/faqschematoggle.php @@ -0,0 +1,34 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +require_once JPATH_PLUGINS . '/system/nrframework/fields/nrtoggle.php'; + +use NRFramework\Extension; +use Joomla\CMS\Language\Text; + +class JFormFieldFAQSchemaToggle extends JFormFieldNRToggle +{ + /** + * Method to get the field input markup. + * + * @return string The field input markup. + */ + public function getInput() + { + // If GSD Pro is not installed and activated abort + if (!Extension::isInstalled('gsd', 'plugin') || !Extension::isPro('plg_system_gsd')) + { + return '
' . Text::_('ACF_FAQ_SCHEMA_GSD_MISSING') . '
'; + } + + return parent::getInput(); + } +} \ No newline at end of file diff --git a/plugins/fields/acffaq/fields/helper.php b/plugins/fields/acffaq/fields/helper.php new file mode 100644 index 00000000..7487bd17 --- /dev/null +++ b/plugins/fields/acffaq/fields/helper.php @@ -0,0 +1,100 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +$presets = [ + 1 => [ + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'show_toggle_icon' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'icon' => [ + 'type' => 'radio', + 'value' => 'arrow' + ], + 'icon_position' => [ + 'type' => 'radio', + 'value' => 'right' + ], + ], + 2 => [ + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'show_toggle_icon' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'icon' => [ + 'type' => 'radio', + 'value' => 'plus_minus' + ], + 'icon_position' => [ + 'type' => 'radio', + 'value' => 'left' + ], + ], + 3 => [ + 'initial_state' => [ + 'type' => 'list', + 'value' => 'all-open' + ], + 'keep_one_question_open' => [ + 'type' => 'nrtoggle', + 'value' => false + ], + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'show_toggle_icon' => [ + 'type' => 'nrtoggle', + 'value' => false + ] + ], + 4 => [ + 'separator' => [ + 'type' => 'nrtoggle', + 'value' => false + ], + 'background_color' => [ + 'type' => 'text', + 'value' => '#fff' + ], + 'item_padding' => [ + 'type' => 'number', + 'responsive' => true, + 'dimensions' => true, + 'value' => 20 + ], + 'item_gap' => [ + 'type' => 'number', + 'responsive' => true, + 'value' => 14 + ], + 'show_toggle_icon' => [ + 'type' => 'nrtoggle', + 'value' => true + ], + 'icon' => [ + 'type' => 'radio', + 'value' => 'arrow' + ], + 'icon_position' => [ + 'type' => 'radio', + 'value' => 'right' + ], + ] +]; \ No newline at end of file diff --git a/plugins/fields/acffaq/fields/value.xml b/plugins/fields/acffaq/fields/value.xml new file mode 100644 index 00000000..3b7a23ac --- /dev/null +++ b/plugins/fields/acffaq/fields/value.xml @@ -0,0 +1,19 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/fields/acffaq/language/en-GB/en-GB.plg_fields_acffaq.ini b/plugins/fields/acffaq/language/en-GB/en-GB.plg_fields_acffaq.ini new file mode 100644 index 00000000..41ae787b --- /dev/null +++ b/plugins/fields/acffaq/language/en-GB/en-GB.plg_fields_acffaq.ini @@ -0,0 +1,57 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFFAQ_LABEL="ACF - FAQ" +ACF_FAQ="Fields - ACF FAQ" +ACF_FAQ_VALUE_DESC="Enter your questions and answers to display a FAQ section." +ACF_FAQ_DESC="Add a Frequently Asked Questions section to your site." +ACF_FAQ_TEMPLATE_SELECTOR="Template Selector" +ACF_FAQ_TEMPLATE="Template" +ACF_FAQ_TEMPLATE_DESC="Select the FAQ template." +ACF_FAQ_FAQ="FAQ" +ACF_FAQ_SHOW_TOGGLE_ICON="Show Toggle Icon" +ACF_FAQ_SHOW_TOGGLE_ICON_DESC="Enable to make the question answer show/hide via an icon." +ACF_FAQ_INITIAL_STATE="Initial State" +ACF_FAQ_INITIAL_STATE_DESC="Select the initial state of the FAQ." +ACF_FAQ_INITIAL_STATE_FIRST_OPEN="Show first question as open" +ACF_FAQ_INITIAL_STATE_ALL_OPEN="Show all questions as open" +ACF_FAQ_INITIAL_STATE_ALL_CLOSED="Show all questions as closed" +ACF_FAQ_COLUMNS="Columns" +ACF_FAQ_COLUMNS_DESC="Define in how many columns to show the FAQ." +ACF_FAQ_ITEM_GAP="Item Gap" +ACF_FAQ_ITEM_GAP_DESC="Define the item gap between the FAQ items." +ACF_FAQ_COLUMN_GAP="Column Gap" +ACF_FAQ_COLUMN_GAP_DESC="Define the column gap between the FAQ columns." +ACF_FAQ_ITEM="FAQ Item" +ACF_FAQ_ITEM_BACKGROUND_COLOR="Background Color" +ACF_FAQ_ITEM_BACKGROUND_COLOR_DESC="Set the FAQ item background color." +ACF_FAQ_QUESTION_TEXT_COLOR="Question Text Color" +ACF_FAQ_QUESTION_TEXT_COLOR_DESC="Set the FAQ question text color." +ACF_FAQ_ANSWER_TEXT_COLOR="Answer Text Color" +ACF_FAQ_ANSWER_TEXT_COLOR_DESC="Set the FAQ answer text color." +ACF_FAQ_BORDER_RADIUS_DESC="Set the FAQ item border radius." +ACF_FAQ_ITEM_PADDING="Padding" +ACF_FAQ_ITEM_PADDING_DESC="Set the FAQ item padding." +ACF_FAQ_ICON="Icon" +ACF_FAQ_ICON_DESC="Select the toggle icon." +ACF_FAQ_ICON_POSITION="Icon Position" +ACF_FAQ_ICON_POSITION_DESC="Set the icon position. Whether to display it on the left or right side of the question." +ACF_FAQ_QUESTION="Question" +ACF_FAQ_QUESTION_HINT="Type a question" +ACF_FAQ_ANSWER="Answer" +ACF_FAQ_ANSWER_HINT="Type an answer" +ACF_FAQ_QUESTION_FONT_SIZE_DESC="Set the question font size." +ACF_FAQ_ANSWER_FONT_SIZE_DESC="Set the answer font size." +ACF_FAQ_GENERATE_FAQ="Generate FAQ Schema" +ACF_FAQ_GENERATE_FAQ_DESC="Enable to generate FAQ Schema for your questions and answers." +ACF_FAQ_SCHEMA_GSD_MISSING="The Pro version of the Google Structured Data extension is required to generate the FAQ Schema." +ACF_FAQ_KEEP_ONE_QUESTION_OPEN="Keep One Question Open" +ACF_FAQ_KEEP_ONE_QUESTION_OPEN_DESC="Enable to only keep one question open at a time." +ACF_FAQ_SEPARATOR="Separator" +ACF_FAQ_SEPARATOR_DESC="Enable to add a horizontal separator between FAQ items." +ACF_FAQ_SEPARATOR_COLOR="Separator Color" +ACF_FAQ_SEPARATOR_COLOR_DESC="Set the color of the separator." \ No newline at end of file diff --git a/plugins/fields/acffaq/language/en-GB/en-GB.plg_fields_acffaq.sys.ini b/plugins/fields/acffaq/language/en-GB/en-GB.plg_fields_acffaq.sys.ini new file mode 100644 index 00000000..03d70c77 --- /dev/null +++ b/plugins/fields/acffaq/language/en-GB/en-GB.plg_fields_acffaq.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_FAQ="Fields - ACF FAQ" +ACF_FAQ_DESC="Add a Frequently Asked Questions section to your site." \ No newline at end of file diff --git a/plugins/fields/acffaq/language/es-ES/es-ES.plg_fields_acffaq.ini b/plugins/fields/acffaq/language/es-ES/es-ES.plg_fields_acffaq.ini new file mode 100644 index 00000000..44300fc8 --- /dev/null +++ b/plugins/fields/acffaq/language/es-ES/es-ES.plg_fields_acffaq.ini @@ -0,0 +1,57 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFFAQ_LABEL="ACF - FAQ" +ACF_FAQ="Campos - ACF FAQ" +ACF_FAQ_VALUE_DESC="Ingrese sus preguntas y respuestas para mostrar una sección de preguntas frecuentes." +ACF_FAQ_DESC="Agregue una sección de Preguntas frecuentes a su sitio." +ACF_FAQ_TEMPLATE_SELECTOR="Selector de Plantillas" +ACF_FAQ_TEMPLATE="Plantilla" +ACF_FAQ_TEMPLATE_DESC="Seleccione la plantila de FAQ" +ACF_FAQ_FAQ="FAQ" +ACF_FAQ_SHOW_TOGGLE_ICON="Mostrar Icono de Alternar" +ACF_FAQ_SHOW_TOGGLE_ICON_DESC="Habilite para que la respuesta a la pregunta se muestre/oculte a través de un icono." +ACF_FAQ_INITIAL_STATE="Estado Inicial" +ACF_FAQ_INITIAL_STATE_DESC="Seleccione el estado inicial de las preguntas frecuentes FAQ." +ACF_FAQ_INITIAL_STATE_FIRST_OPEN="Mostrar la primera pregunta como abierta" +ACF_FAQ_INITIAL_STATE_ALL_OPEN="Mostrar todas las preguntas como abiertas" +ACF_FAQ_INITIAL_STATE_ALL_CLOSED="Mostrar todas las preguntas como cerradas" +ACF_FAQ_COLUMNS="Columnas" +ACF_FAQ_COLUMNS_DESC="Defina en cuántas columnas mostrar las preguntas frecuentes." +ACF_FAQ_ITEM_GAP="Espaciado de Elementos" +ACF_FAQ_ITEM_GAP_DESC="Defina el espaciado entre los elementos de las preguntas frecuentes." +ACF_FAQ_COLUMN_GAP="Espaciado de Columnas" +ACF_FAQ_COLUMN_GAP_DESC="Defina el espacio de columna entre las columnas de preguntas frecuentes." +ACF_FAQ_ITEM="Elementos FAQ" +ACF_FAQ_ITEM_BACKGROUND_COLOR="Color de Fondo" +ACF_FAQ_ITEM_BACKGROUND_COLOR_DESC="Establezca el color de fondo del elemento de preguntas frecuentes." +ACF_FAQ_QUESTION_TEXT_COLOR="Color del texto de la pregunta" +ACF_FAQ_QUESTION_TEXT_COLOR_DESC="Establezca el color del texto de la pregunta de preguntas frecuentes." +ACF_FAQ_ANSWER_TEXT_COLOR="Color Texto de Respuesta" +ACF_FAQ_ANSWER_TEXT_COLOR_DESC="Establezca el color del texto de la respuesta a las preguntas frecuentes." +ACF_FAQ_BORDER_RADIUS_DESC="Establezca el radio del borde del elemento de preguntas frecuentes." +ACF_FAQ_ITEM_PADDING="Relleno" +ACF_FAQ_ITEM_PADDING_DESC="Establezca el relleno del elemento de preguntas frecuentes." +ACF_FAQ_ICON="Icono" +ACF_FAQ_ICON_DESC="Seleccione el icono de alternancia." +ACF_FAQ_ICON_POSITION="Posición del Icono" +ACF_FAQ_ICON_POSITION_DESC="Establecer la posición del icono. Ya sea para mostrarlo en el lado izquierdo o derecho de la pregunta." +ACF_FAQ_QUESTION="Pregunta" +ACF_FAQ_QUESTION_HINT="Escriba una pregunta" +ACF_FAQ_ANSWER="Respuesta" +ACF_FAQ_ANSWER_HINT="Escriba una respuesta" +ACF_FAQ_QUESTION_FONT_SIZE_DESC="Establezca el tamaño de fuente de la pregunta." +ACF_FAQ_ANSWER_FONT_SIZE_DESC="Establezca el tamaño de fuente de la respuesta." +ACF_FAQ_GENERATE_FAQ="Generar Esquema de FAQ" +ACF_FAQ_GENERATE_FAQ_DESC="Habilite para generar un esquema de preguntas frecuentes para sus preguntas y respuestas." +ACF_FAQ_SCHEMA_GSD_MISSING="La versión Pro de la extensión Google Structured Data es necesaria para generar el Esquema FAQ." +ACF_FAQ_KEEP_ONE_QUESTION_OPEN="Mantenga una pregunta abierta" +ACF_FAQ_KEEP_ONE_QUESTION_OPEN_DESC="Habilite para mantener solo una pregunta abierta a la vez." +ACF_FAQ_SEPARATOR="Separador" +ACF_FAQ_SEPARATOR_DESC="Habilite para agregar un separador horizontal entre los elementos de preguntas frecuentes." +ACF_FAQ_SEPARATOR_COLOR="Color del Separador" +ACF_FAQ_SEPARATOR_COLOR_DESC="Establezca el color del separador." diff --git a/plugins/fields/acffaq/language/es-ES/es-ES.plg_fields_acffaq.sys.ini b/plugins/fields/acffaq/language/es-ES/es-ES.plg_fields_acffaq.sys.ini new file mode 100644 index 00000000..07499648 --- /dev/null +++ b/plugins/fields/acffaq/language/es-ES/es-ES.plg_fields_acffaq.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2020 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_FAQ="Campos - ACF FAQ" +ACF_FAQ_DESC="Agregue una sección de Preguntas frecuentes a su sitio." diff --git a/plugins/fields/acffaq/params/acffaq.xml b/plugins/fields/acffaq/params/acffaq.xml new file mode 100644 index 00000000..2a9ba3b8 --- /dev/null +++ b/plugins/fields/acffaq/params/acffaq.xml @@ -0,0 +1,194 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acffaq/script.install.helper.php b/plugins/fields/acffaq/script.install.helper.php new file mode 100644 index 00000000..370f81bf --- /dev/null +++ b/plugins/fields/acffaq/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcffaqInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acffaq/script.install.php b/plugins/fields/acffaq/script.install.php new file mode 100644 index 00000000..957c2b9a --- /dev/null +++ b/plugins/fields/acffaq/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFFAQInstallerScript extends PlgFieldsACFFAQInstallerScriptHelper +{ + public $alias = 'acffaq'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acffaq/tmpl/acffaq.php b/plugins/fields/acffaq/tmpl/acffaq.php new file mode 100644 index 00000000..9c99351f --- /dev/null +++ b/plugins/fields/acffaq/tmpl/acffaq.php @@ -0,0 +1,71 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$value = $field->value) +{ + return; +} + +if (is_string($value) && !$value = json_decode($value, true)) +{ + return; +} + +if (!isset($value['value']) && !is_array($value['value'])) +{ + return; +} + +// Prepare the value +$value = array_values($value['value']); +$value = array_filter($value, function($item) { + return !empty($item['question']) && !empty($item['answer']); +}); + +$payload = [ + 'value' => $value, + 'css_class' => ' template_' . $fieldParams->get('template'), + 'keep_one_question_open' => $fieldParams->get('keep_one_question_open', '0') === '1', + 'columns' => (int) $fieldParams->get('columns', 1), + 'item_gap' => $fieldParams->get('item_gap_control.item_gap', 20), + 'column_gap' => $fieldParams->get('column_gap_control.column_gap', 20), + 'item_background_color' => $fieldParams->get('background_color'), + 'item_border_radius' => $fieldParams->get('border_radius_control.item_border_radius'), + 'item_padding' => $fieldParams->get('padding_control.item_padding'), + 'question_font_size' => $fieldParams->get('question_font_size_control.question_font_size'), + 'question_text_color' => $fieldParams->get('question_text_color'), + 'answer_font_size' => $fieldParams->get('answer_font_size_control.answer_font_size'), + 'answer_text_color' => $fieldParams->get('answer_text_color'), + 'answer_padding' => $fieldParams->get('answer_padding_control.answer_padding'), + 'generate_faq' => $fieldParams->get('generate_faq', '0') === '1', + 'separator' => $fieldParams->get('separator', '0') === '1', + 'separator_color' => $fieldParams->get('separator_color'), + 'initial_state' => $fieldParams->get('initial_state', 'first-open') +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +$show_toggle_icon = $fieldParams->get('show_toggle_icon', '1') === '1'; +$payload['show_toggle_icon'] = $show_toggle_icon; +if ($show_toggle_icon) +{ + $payload['icon'] = $fieldParams->get('icon', 'arrow'); + $payload['icon_position'] = $fieldParams->get('icon_position', 'right'); +} + +echo \NRFramework\Widgets\Helper::render('FAQ', $payload); \ No newline at end of file diff --git a/plugins/fields/acffaq/version.php b/plugins/fields/acffaq/version.php new file mode 100644 index 00000000..3dd77202 --- /dev/null +++ b/plugins/fields/acffaq/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfgallery/acfgallery.php b/plugins/fields/acfgallery/acfgallery.php new file mode 100644 index 00000000..ead2ca6f --- /dev/null +++ b/plugins/fields/acfgallery/acfgallery.php @@ -0,0 +1,441 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use NRFramework\Helpers\Widgets\GalleryManager; +use NRFramework\Functions; +use Joomla\Registry\Registry; +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Utility\Utility; +use Joomla\CMS\Language\Text; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); +JLoader::register('ACFGalleryHelper', __DIR__ . '/fields/helper.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFGallery extends ACF_Field +{ + /** + * The validation rule will be used to validate the field on saving + * + * @var string + */ + protected $validate = 'acfrequired'; + + public function onUserAfterSave($user, $isnew, $success, $msg) + { + // Load Fields Component Helper class + JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + $fields = FieldsHelper::getFields('com_users.user', $user, true); + + if (!$fields) + { + return true; + } + + // Get the fields data + $fieldsData = !empty($user['com_fields']) ? $user['com_fields'] : []; + + $this->processFiles($fields, $fieldsData, (object) $user); + } + + public function onContentAfterSave($context, $item, $isNew, $data = []) + { + if (!is_array($data)) + { + return true; + } + + if (!isset($data['com_fields'])) + { + return true; + } + + // Create correct context for category + if ($context == 'com_categories.category') + { + $context = $item->get('extension') . '.categories'; + } + + // Load Fields Component Helper class + JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + // Check the context + $parts = FieldsHelper::extract($context, $item); + + if (!$parts) + { + return true; + } + + // Compile the right context for the fields + $context = $parts[0] . '.' . $parts[1]; + + // Loading the fields + $fields = FieldsHelper::getFields($context, $item); + + if (!$fields) + { + return true; + } + + // Get the fields data + $fieldsData = !empty($data['com_fields']) ? $data['com_fields'] : []; + + $this->processFiles($fields, $fieldsData, $item); + } + + /** + * Processes the files. + * + * Either duplicates the files or uploads them to final directory. + * + * @param array $fields + * @param array $fieldsData + * @param object $item + * + * @return void + */ + private function processFiles($fields = [], $fieldsData = [], $item = []) + { + if (!$fields || !$fieldsData || !$item) + { + return; + } + + // Whether we should clean up the temp folder at the end of this process + $should_clean = false; + + // Get the Fields Model + if (!defined('nrJ4')) + { + $model = JModelLegacy::getInstance('Field', 'FieldsModel', ['ignore_request' => true]); + } + else + { + $model = Factory::getApplication()->bootComponent('com_fields')->getMVCFactory()->createModel('Field', 'Administrator', ['ignore_request' => true]); + } + + // Cache subform fields + $subform_fields = []; + + // Loop over the fields + foreach ($fields as $field) + { + $field_type = $field->type; + + /** + * Check whether a Gallery field is used within the Subform field. + */ + if ($field_type === 'subform') + { + $submitted_subform_value = array_key_exists($field->name, $fieldsData) ? $fieldsData[$field->name] : null; + + // Ensure it has a value + if (!$submitted_subform_value || !$subform_value = json_decode($field->rawvalue, true)) + { + // Update subform field + $model->setFieldValue($field->id, $item->id, json_encode([])); + + continue; + } + + $update = false; + $is_subform_non_repeatable = false; + + // Make non-repeatable subform fields a multi array so we can parse them + if (Functions::startsWith(array_key_first($subform_value), 'field') && $field->fieldparams->get('repeat', '0') === '0') + { + $is_subform_non_repeatable = true; + $subform_value = [$subform_value]; + } + + foreach ($subform_value as $key => &$value) + { + foreach ($value as $_key => &$_value) + { + // Get Field ID + $field_id = str_replace('field', '', $_key); + + // Get Field by ID + $subform_field = isset($subform_fields[$field_id]) ? $subform_fields[$field_id] : $model->getItem($field_id); + + // Only proceed for this field type + if ($subform_field->type !== $this->_name) + { + continue; + } + + // Cache field + if (!isset($subform_fields[$field_id])) + { + $subform_fields[$field_id] = $subform_field; + } + + // Check if value can be json_decoded + if (is_string($_value)) + { + if ($decoded = json_decode($_value, true)) + { + $_value = $decoded; + } + } + + // $_value is a string when batching an item + if (!is_array($_value)) + { + $_value = []; + } + + if (\ACF\Item::isCopying()) + { + // Duplicate files + ACFGalleryHelper::duplicateFiles($_value); + } + else + { + // We should run our cleanup routine at the end + $should_clean = true; + + // Move to final folder + $items = GalleryManager::moveTempItemsToDestination($_value, $subform_field, $this->getDestinationFolder($subform_field, $item)); + + // Save item tags + $_value['items'] = GalleryManager::saveItemTags($items); + } + + $update = true; + } + } + + if ($update) + { + if ($is_subform_non_repeatable) + { + $subform_value = reset($subform_value); + } + + // Update subform field + $model->setFieldValue($field->id, $item->id, json_encode($subform_value)); + } + } + else + { + // Only proceed for this field type + if ($field_type !== $this->_name) + { + continue; + } + + // Determine the value if it is available from the data + $value = array_key_exists($field->name, $fieldsData) ? $fieldsData[$field->name] : null; + + if (!$value) + { + continue; + } + + // Check if value can be json_decoded + if (is_string($value)) + { + if ($decoded = json_decode($value, true)) + { + $value = $decoded; + } + } + + // $value is a string when batching an item + if (!is_array($value)) + { + $value = []; + } + + if (\ACF\Item::isCopying()) + { + // Duplicate files + ACFGalleryHelper::duplicateFiles($value); + } + else + { + // We should run our cleanup routine at the end + $should_clean = true; + + // Move to final folder + $items = GalleryManager::moveTempItemsToDestination($value, $field, $this->getDestinationFolder($field, $item)); + + // Save item tags + $value['items'] = GalleryManager::saveItemTags($items); + } + + // Setting the value for the field and the item + $model->setFieldValue($field->id, $item->id, json_encode($value)); + } + } + + if ($should_clean) + { + // Clean old files from temp folder + GalleryManager::clean(); + } + } + + /** + * Returns the destination folder. + * + * @param object $field + * @param array $item + * + * @return string + */ + private function getDestinationFolder($field, $item) + { + $ds = DIRECTORY_SEPARATOR; + $destination_folder = null; + + $field_id = $field->id; + $item_id = $item->id; + + // Make field params use Registry + if (!$field->fieldparams instanceof Registry) + { + $field->fieldparams = new Registry($field->fieldparams); + } + + switch ($field->fieldparams->get('upload_folder_type', 'auto')) + { + case 'auto': + default: + // Get context and remove `com_` part + $context = preg_replace('/^com_/', '', Factory::getApplication()->input->get('option')); + $destination_folder = ['media', 'acfgallery', $context, $item_id, $field_id]; + break; + case 'custom': + $upload_folder = trim(ltrim($field->fieldparams->get('upload_folder'), $ds), $ds); + + // Smart Tags Instance + $st = new \NRFramework\SmartTags(); + + // Add custom Smart Tags + $catAlias = isset($item->catid) ? $this->getCategoryAlias($item->catid) : ''; + + $custom_tags = [ + 'field_id' => $field_id, + 'cat_id' => isset($item->catid) ? $item->catid : '', + 'cat_alias' => $catAlias, + 'item_catid' => isset($item->catid) ? $item->catid : '', + 'item_catalias' => $catAlias, + 'item_id' => $item_id, + 'item_alias' => isset($item->alias) ? $item->alias : '', + 'item_author_id' => isset($item->created_by) ? $item->created_by : '' + ]; + $st->add($custom_tags, 'field.'); + + // Replace Smart Tags + $upload_folder = $st->replace($upload_folder); + + $destination_folder = [$upload_folder]; + break; + } + + return implode($ds, array_merge([JPATH_ROOT], $destination_folder)) . $ds; + } + + /** + * Returns a category alias by its ID. + * + * @param int $cat_id + * + * @return string + */ + private function getCategoryAlias($cat_id = null) + { + if (!$cat_id) + { + return; + } + + $db = Factory::getDbo(); + + $query = $db->getQuery(true) + ->select($db->quoteName('alias')) + ->from($db->quoteName('#__categories')) + ->where($db->quoteName('id') . ' = ' . (int) $cat_id); + $db->setQuery($query); + + return $db->loadResult(); + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $fieldNode->setAttribute('field_id', $field->id); + + return $fieldNode; + } + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param JForm $form The form + * @param stdClass $data The data + * + * @return void + */ + public function onContentPrepareForm(Joomla\CMS\Form\Form $form, $data) + { + // Make sure we are manipulating the right field. + if (isset($data->type) && $data->type != $this->_name) + { + return; + } + + $result = parent::onContentPrepareForm($form, $data); + + // Display the server's maximum upload size in the field's description + $max_upload_size_str = HTMLHelper::_('number.bytes', Utility::getMaxUploadSize()); + $field_desc = $form->getFieldAttribute('max_file_size', 'description', null, 'fieldparams'); + $form->setFieldAttribute('max_file_size', 'description', Text::sprintf($field_desc, $max_upload_size_str), 'fieldparams'); + + // Set the Field ID in Upload Folder Type description (if field is saved), otherwise, show FIELD_ID placeholder. + // ITEM_ID is not replaceable in the field settings. + $field_id = isset($data->id) ? $data->id : 'FIELD_ID'; + $upload_folder_type_desc = $form->getFieldAttribute('upload_folder_type', 'description', null, 'fieldparams'); + $form->setFieldAttribute('upload_folder_type', 'description', Text::sprintf($upload_folder_type_desc, $field_id), 'fieldparams'); + + return $result; + } +} \ No newline at end of file diff --git a/plugins/fields/acfgallery/acfgallery.xml b/plugins/fields/acfgallery/acfgallery.xml new file mode 100644 index 00000000..5631170a --- /dev/null +++ b/plugins/fields/acfgallery/acfgallery.xml @@ -0,0 +1,26 @@ + + + ACF_GALLERY + ACF_GALLERY_DESC + Tassos Marinos + September 2021 + Copyright (C) 2021 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfgallery.php + script.install.helper.php + version.php + fields + language + params + tmpl + + + img + js + + diff --git a/plugins/fields/acfgallery/fields/acfgallery.php b/plugins/fields/acfgallery/fields/acfgallery.php new file mode 100644 index 00000000..3579e282 --- /dev/null +++ b/plugins/fields/acfgallery/fields/acfgallery.php @@ -0,0 +1,113 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\Field\TextField; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Factory; +use Joomla\Registry\Registry; +use Joomla\Filesystem\Path; + +class JFormFieldACFGallery extends TextField +{ + /** + * Generates the Gallery Field + * + * @return string The field input markup. + */ + protected function getInput() + { + require_once JPATH_SITE . '/plugins/fields/acfgallery/fields/helper.php'; + + $style = (string) $this->element['style']; + $style = ACFGalleryHelper::getStyle($style); + + $data = [ + 'value' => $this->prepareValue(), + 'required' => (string) $this->element['required'] == 'true' ? true : false, + 'name' => (int) $this->element['limit_files'] == 1 ? $this->name . '[items][0]' : $this->name . '[items][ITEM_ID]', + 'limit_files' => (string) $this->element['limit_files'], + 'max_file_size' => (string) $this->element['max_file_size'], + 'style' => $style, + 'original_image_resize' => (string) $this->element['original_image_resize'] === '1', + 'original_image_resize_width' => (string) $this->element['original_image_resize_width'], + 'thumb_width' => (string) $this->element['thumb_width'], + 'thumb_height' => (string) $this->element['thumb_height'], + 'thumb_resize_method' => (string) $this->element['resize_method'], + 'css_class' => ' ordering-' . (string) $this->element['ordering'], + 'disabled' => $this->disabled, + 'field_id' => (int) $this->element['field_id'], + 'item_id' => $this->getItemID(), + 'id' => $this->id, + 'pro' => true + ]; + + HTMLHelper::script('plg_fields_acfgallery/acfgallery.js', ['relative' => true, 'version' => 'auto']); + + return \NRFramework\Widgets\Helper::render('GalleryManager', $data); + } + + private function getItemID() + { + $item_id = (int) Factory::getApplication()->input->get('id'); + + switch (Factory::getApplication()->input->get('option')) + { + case 'com_users': + $item_id = Factory::getUser()->id; + break; + } + + return $item_id; + } + + /** + * The list of uploaded Gallery Items. + * + * @return mixed + */ + private function prepareValue() + { + if (empty($this->value)) + { + return; + } + + $this->value = is_string($this->value) ? json_decode($this->value, true) : (array) $this->value; + + if (!isset($this->value['items'])) + { + return; + } + + $value = []; + + foreach ($this->value['items'] as $key => $file) + { + $file = new Registry($file); + + $value[] = [ + 'source' => $file->get('source'), + 'original' => $file->get('original') ? $file->get('original') : $file->get('image'), + 'exists' => is_file(Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $file->get('thumbnail')]))), + 'caption' => $file->get('caption', ''), + 'thumbnail' => $file->get('thumbnail', ''), + 'is_media_uploader_file' => ($file->get('media_upload_source', 'false') == 'true'), + 'alt' => $file->get('alt', ''), + 'tags' => json_encode($file->get('tags', [])) + ]; + } + + return $value; + } +} diff --git a/plugins/fields/acfgallery/fields/helper.php b/plugins/fields/acfgallery/fields/helper.php new file mode 100644 index 00000000..cdd6f0a3 --- /dev/null +++ b/plugins/fields/acfgallery/fields/helper.php @@ -0,0 +1,162 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Helper\TagsHelper; +use NRFramework\File; +use Joomla\CMS\Uri\Uri; + +class ACFGalleryHelper +{ + /** + * Duplicates files. + * + * @param array $value + * + * @return void + */ + public static function duplicateFiles(&$value = []) + { + if (!is_array($value) || !count($value)) + { + return; + } + + if (!isset($value['items']) || !is_array($value['items']) || !count($value['items'])) + { + return; + } + + foreach ($value['items'] as &$item) + { + if ($newSourcePath = self::duplicateGalleryItemFile('source', $item)) + { + $item['source'] = $newSourcePath; + } + + if ($newImagePath = self::duplicateGalleryItemFile('image', $item)) + { + $item['image'] = $newImagePath; + } + + if ($newThumbnailPath = self::duplicateGalleryItemFile('thumbnail', $item)) + { + $item['thumbnail'] = $newThumbnailPath; + } + + if ($newSlideshowPath = self::duplicateGalleryItemFile('slideshow', $item)) + { + $item['slideshow'] = $newSlideshowPath; + } + } + } + + /** + * Duplicates files item. + * + * @param array $value + * + * @return void + */ + private static function duplicateGalleryItemFile($key, $item) + { + // Duplicate the source image + if (isset($item[$key]) && !empty($item[$key])) + { + // Original file path + $path = implode(DIRECTORY_SEPARATOR, [JPATH_SITE, $item[$key]]); + + if (!file_exists($path)) + { + return; + } + + // New file path + $newPath = File::copy($path, $path); + + return str_replace([JPATH_SITE, JPATH_ROOT], '', $newPath); + } + } + + /** + * Parses and returns the style. + * + * @param string $style + * + * @return string + */ + public static function getStyle($style) + { + // Remove 'z' character from "zjustified" style + // This is done for previewing purposes to display it at the end. + return ltrim($style, 'z'); + } + + /** + * Prepares the Gallery Manager Widget uploaded files prior to being passed + * to the Gallery Widget to display the Gallery on the front-end. + * + * @param array $items + * + * @return array + */ + public static function prepareItems($items) + { + $tagsHelper = new TagsHelper(); + + $parsedTagIds = []; + + foreach ($items as $key => &$item) + { + // Skip items that have not saved properly(items were still uploading and we saved the item) + if ($key === 'ITEM_ID') + { + unset($items[$key]); + continue; + } + + // Get tag names from stored IDs + $itemTags = []; + + $tags = isset($item['tags']) ? $item['tags'] : []; + if (is_array($tags) && count($tags)) + { + foreach ($tags as $tagId) + { + if (isset($parsedTagIds[$tagId])) + { + $itemTags[] = $parsedTagIds[$tagId]; + } + else + { + if (!$tag = $tagsHelper->getTagNames([$tagId])) + { + continue; + } + + $itemTags[] = $tag[0]; + $parsedTagIds[$tagId] = $tag[0]; + } + } + } + + $item = array_merge($item, [ + 'url' => Uri::root() . $item['image'], + 'thumbnail_url' => Uri::root() . $item['thumbnail'], + 'tags' => $itemTags + ]); + } + + return $items; + } +} \ No newline at end of file diff --git a/plugins/fields/acfgallery/language/da-DK/da-DK.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/da-DK/da-DK.plg_fields_acfgallery.ini new file mode 100644 index 00000000..8b386978 --- /dev/null +++ b/plugins/fields/acfgallery/language/da-DK/da-DK.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="ACF - Gallery" +ACF_GALLERY="Felter - ACF Gallery" +ACF_GALLERY_DESC="ACF gallerifeltet giver dig mulighed for at tiløje smukke billedgallerier til dit websted." +ACF_GALLERY_VALUE_DESC="Tilføj nyt eller vælg billeder fra Joomla medier for at oprette et galleri." +ACF_GALLERY_UPLOAD_FOLDER="Upload mappe" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Vælg mappen hvor de uploadede billeder til dit galleri bliver placeret.

Bemærk: Du kan også anvende Smart Tags." +ACF_GALLERY_LIMIT_FILES="Filgrænse" +ACF_GALLERY_LIMIT_FILES_DESC="Hvor mange filer kan uploades? Angiv 0 for ingen grænse." +ACF_GALLERY_MAX_FILE_SIZE="Filtstørrelsesgrænse" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Konfigurer den maksimalt tilladte størrelse for hver uploadede fil i megabyte. Angiv 0 for ingen grænse.

Din serves maksimale upload størrelse er: %s." +ACF_GALLERY_LIGHTBOX="Lightboks popup" +ACF_GALLERY_LIGHTBOX_DESC="Tillad at der ved klik på en miniature vises en lightboks popup med det fulde billede." +ACF_GALLERY_MODULE="Modul som billedtekst" +; ACF_GALLERY_MODULE_DESC="Select a module to be displayed below the image's caption in the lightbox popup." +ACF_GALLERY_STYLE="Stil" +; ACF_GALLERY_STYLE_DESC="Select how your gallery will look like on the front-end. Available styles:

Grid: All items will have the same width and the same height as well. You can define the width and height of the items as well as the number of columns for this layout.

Masonry: All items have the same width, but the height of each will be different. You can define the column's width as well as the number of columns for this layout

Slideshow: This layout offers a dynamic carousel display with convenient thumbnail navigation for a captivating and interactive gallery experience.

Justified: This layout is the opposite of the Masonry layout. All items will have the same height but different widths. You can define the target row height. It's called a target because row height is the lever we use to fit everything nicely. The algorithm will get as close to the target row height as possible." +ACF_GALLERY_COLUMNS="Kolonner" +ACF_GALLERY_COLUMNS_DESC="Angiv hvor mange elementer der skal vise per række på desktop, tablet eller mobilenheder." +; ACF_GALLERY_GAP="Item Gap" +ACF_GALLERY_GAP_DESC="Angiv mellemrummet mellem gallerielementerne på desktop, tablet eller mobilenheder." +ACF_GALLERY_ORIGINAL_IMAGE="Optimer originalt billedeOriginal Image" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Originalt billede skaler" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="optimer det originale billede uploadet af brugeren for at øge indlæsningshastigheden og frigive lagerplads. Dette er fuldskærmsbilledet som dine brugere ser i lightbox popup'en." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Bredde" +; ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Resize the image to a specified width. If the height setting is unavailable, the image will be resized by keeping the image's aspect ratio." +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Height" +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Resize the image to a specified height." +; ACF_GALLERY_IMAGE_RESIZE="Image Resize" +; ACF_GALLERY_IMAGE_RESIZE_DESC="Select the method that will be used to resize the thumbnails in the gallery.

Crop: Some parts of the image might be removed in order to fit the given Width and Height.

Stretch: The image will be stretched and squeezed as needed in order to fit the given Width and Height.

Fit: Keep the original aspect ratio and fit the dimensions defined by the given Width and Height. This could make the thumbnails have empty space in either top and bottom or left and right parts of the image." +ACF_GALLERY_CROP="Beskær" +ACF_GALLERY_STRETCH="Stræk" +ACF_GALLERY_FIT="Tilpas" +ACF_GALLERY_THUMB_WIDTH_DESC="Angiv bredden på dine miniaturer i pixels." +; ACF_GALLERY_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels." +; ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels. If set to 0 (or empty), then the thumbnail will be generated by also keeping the aspect ratio." +; ACF_GALLERY_IMAGE_QUALITY="Image Quality" +; ACF_GALLERY_IMAGE_QUALITY_DESC="Choose the quality for the thumbnails. The quality is a value that ranges between 1 and 100. The higher the quality is, the higher the image size of the thumbnails will be." +ACF_GALLERY_FOLDER_TYPE="Upload destination" +; ACF_GALLERY_FOLDER_TYPE_DESC="Specify a destination folder for uploaded images.

Auto: The images will be uploaded to the following directory: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Note:
COMPONENT is the component name (i.e. content, users, etc...) where this field got populated from.
ITEM_ID is the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...

Additionally a random prefix will be added in the beginning of each file name to increase security.

Custom: Specify a custom destination folder where the images will be stored.

Available Smart Tags:
{field.field_id}: The custom field ID.
{field.item_id}: the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...
{field.item_alias}: the alias of the item this field was populated from.
{field.item_author_id}: The alias of the associated item author.
{field.item_catid}: The category ID of the associated item.
{field.item_catalias}: The category alias of the associated item." +ACF_GALLERY_ORDERING="Elementsortering" +ACF_GALLERY_ORDERING_DESC="Vælg hvordan gallerielementerne vil blive sorteret.

Standard: Viser gallerielementerne i den samme rækkefølge som defineret i Galleristyringen (Tillader omsortering af gallerielementer).
Alfabetisk: Viser gallerielementerne i alfabetisk rækkefølge baseret på filnavnet.
Omvendt alfabetisk: Viser gallerielementerne i omvendt alfabetisk rækkefølge baseret på filnavnet.
Tilfældig: Viser gallerielementerne i tilfældig rækkefølge." +ACF_GALLERY_DEFAULT="Standard" +ACF_GALLERY_ALPHABETICAL="Alfabetisk" +ACF_GALLERY_REVERSE_ALPHABETICAL="Omvendt alfabetisk" +ACF_GALLERY_RANDOM="Tlifældig" +; ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Set the height of the thumbnail, and the approximate height of each gallery item." +; ACF_GALLERY_ITEM_WIDTH="Item Width" +; ACF_GALLERY_ITEM_HEIGHT="Item Height" +; ACF_GALLERY_ROW_TARGET_HEIGHT="Row Target Height" +; ACF_GALLERY_SLIDESHOW="Slideshow" +; ACF_GALLERY_SLIDES_PER_VIEW="Slides Per View" +; ACF_GALLERY_SLIDES_PER_VIEW_DESC="Set how many slides to display at the same time.

Examples:
To show 1 slide, enter: 1
To show 1 and a half slide, enter: 1.5
To show 2 slides, enter: 2" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES="Space Between Slides" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Define the distance between slides in pixels." +; ACF_GALLERY_INFINITE_LOOP="Infinite Loop" +; ACF_GALLERY_INFINITE_LOOP_DESC="Enabling this setting allows the last slide to seamlessly loop back to the first." +; ACF_GALLERY_KEYBOARD_CONTROL="Keyboard Control" +; ACF_GALLERY_KEYBOARD_CONTROL_DESC="Enabling this setting allows you to control the slides using the keyboard arrows." +; ACF_GALLERY_AUTOPLAY="Autoplay" +; ACF_GALLERY_AUTOPLAY_DESC="Enabling this setting will make the slideshow autoplay." +; ACF_GALLERY_AUTOPLAY_PROGRESS="Autoplay Progress" +; ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Enabling this setting will display a circular progress bar on the bottom-right hand side of each slider indicating how long till the next slide will appear." +; ACF_GALLERY_SHOW_THUMBNAILS_DESC="Enabling this setting will display thumbnails below the slideshow, allowing you to view any slide you desire." +; ACF_GALLERY_THUMBNAILS_ARROWS="Navigation Arrows" +; ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Set whether to show navigation arrows in the thumbnails slider." +; ACF_GALLERY_NAV_CONTROLS="Navigation Controls" +; ACF_GALLERY_NAV_CONTROLS_DESC="Select the type of navigation controls.

Arrows: Displays previous and next arrows on top of the slideshow.
Dots: Display dots below the slideshow, allowing you to choose a slide.
Arrows + Dots: Display both arrows and dots pagination." +; ACF_GALLERY_ARROWS="Arrows" +; ACF_GALLERY_DOTS="Dots" +; ACF_GALLERY_ARROWS_DOTS="Arrows + Dots" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR="Theme Color" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Define the theme color." +; ACF_GALLERY_AUTOPLAY_DELAY="Autoplay Delay" +; ACF_GALLERY_AUTOPLAY_DELAY_DESC="Set the autoplay delay until the next slide appears, in milliseconds." +; ACF_GALLERY_TRANSITION_EFFECT="Transition Effect" +; ACF_GALLERY_TRANSITION_EFFECT_DESC="Select the transition effect between slides.

Note: If Slides Per View is >= 2, then the Slide effect will be always used automatically." +; ACF_GALLERY_SLIDE="Slide" +; ACF_GALLERY_FADE="Fade" +; ACF_GALLERY_CUBE="Cube" +; ACF_GALLERY_COVERFLOW="Coverflow" +; ACF_GALLERY_FLIP="Flip" +ACF_GALLERY_THUMBNAILS="Miniaturer" +; ACF_GALLERY_WATERMARK_IMAGES="Watermark Images" +; ACF_GALLERY_WATERMARK_TEXT_PRESET="Preset" +; ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Select a preset for the watermark text." +; ACF_GALLERY_SITE_NAME="Site Name" +; ACF_GALLERY_SITE_URL="Site URL" +; ACF_GALLERY_WATERMARK_TYPE="Type" +; ACF_GALLERY_WATERMARK_TYPE_DESC="Select whether to use a text, an image as a watermark or disable the watermark.
The watermark will be applied on the image uploaded.

To enable the watermark on the thumbnails, enable \\\"Apply on Thumbnails\\\" setting below." +; ACF_GALLERY_WATERMARK_TEXT_DESC="Enter the text to be used as a watermark. You can also use Smart Tags.

File Smart Tags:
{file.filename}: myfile
{file.basename}: myfile.png" +; ACF_WATERMARK_TEXT_COLOR_DESC="Select the color of the watermark text." +; ACF_WATERMARK_FONT_SIZE_DESC="Set the font size of the watermark text in pixels. The font size will be adjusted based on the image's width." +; ACF_WATERMARK_IMAGE_DESC="Select the image to be used as a watermark. The image dimensions will be adjusted based on the image's width." +; ACF_GALLERY_WATERMARK_POSITION="Position" +; ACF_GALLERY_WATERMARK_POSITION_DESC="Select the position of the watermark." +; ACF_WATERMARK_OPACITY="Opacity" +; ACF_WATERMARK_OPACITY_DESC="Set the opacity of the watermark image." +; ACF_WATERMARK_ROTATION="Rotation" +; ACF_WATERMARK_ROTATION_DESC="Rotate the watermark image by entering the desired angle in degrees. Remember, the rotation will occur clockwise." +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Apply on Thumbnails" +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Select whether to apply the watermark on the thumbnails or not. When this is disabled, the watermark will be applied only on the original image." +; ACF_GALLERY_FILTERING_TAGS="Filtering Tags" +; ACF_GALLERY_TAGS_POSITION="Tags Position" +; ACF_GALLERY_TAGS_POSITION_DESC="Select whether to disable the filtering tags, or show a list of tags above or below the gallery items. This will allow your users to filter the gallery items by tag." +; ACF_GALLERY_ABOVE_GALLERY_ITEMS="Above Gallery Items" +; ACF_GALLERY_BELOW_GALLERY_ITEMS="Below Gallery Items" +; ACF_GALLERY_TAGS_SORT="Sort Tags" +; ACF_GALLERY_TAGS_SORT_DESC="Select how the tags will be sorted." +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL="All Tags Label" +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Enter a label to add an \\\"All\\\" item at the start of the tags filter list, allowing you to view all gallery items. Leave this empty if you don't want to display an \\\"All\\\" item.

You can make this multilingual by setting it to a translation string." +; ACF_GALLERY_TAGS_MOBILE="Tags on Mobile" +; ACF_GALLERY_TAGS_MOBILE_DESC="Set whether to display the tags on mobile devices, make them appear as a dropdown or disable them." +; ACF_GALLERY_TAGS_TEXT_COLOR="Text Color" +; ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Set the tags text color." +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Text Color - Hover" +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Set the text color when hovering over a tag item." +; ACF_GALLERY_TAGS_BG_COLOR_HOVER="Background Color - Hover" +; ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Set the background color when hovering over a tag item." +; ACF_GALLERY_SHOW="Show" +; ACF_GALLERY_SHOW_AS_DROPDOWN="Show as dropdown" diff --git a/plugins/fields/acfgallery/language/de-DE/de-DE.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/de-DE/de-DE.plg_fields_acfgallery.ini new file mode 100644 index 00000000..8da3d2a3 --- /dev/null +++ b/plugins/fields/acfgallery/language/de-DE/de-DE.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="ACF - Galerie" +ACF_GALLERY="Felder - ACF Galerie" +ACF_GALLERY_DESC="Das ACF Galerie-Feld erlaubt es Ihnen, schicke Bildergalerie in Deine Seite einzubauen" +ACF_GALLERY_VALUE_DESC="Fügen Sie neue Bilder hinzu oder wählen Sie Bilder aus dem Joomla Media Manager aus, um eine Galerie zu erstellen." +ACF_GALLERY_UPLOAD_FOLDER="Upload-Ordner" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Ein Verzeichnis wählen, in dem die hochgeladenen Bilder gespeichert werden.

Hinweis: Sie können hier auch Smart-Tags verwenden" +ACF_GALLERY_LIMIT_FILES="Dateilimit" +ACF_GALLERY_LIMIT_FILES_DESC="Wie viele Dateien können hochgeladen werden? Geben Sie 0 für unbegrenzt ein." +ACF_GALLERY_MAX_FILE_SIZE="Dateigrößenbeschränkung" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Konfigurieren Sie die maximal zulässige Größe für jede hochgeladene Datei in Megabyte. Geben Sie unbegrenzt 0 ein.

Die maximale Upload-Größe Ihres Servers beträgt: % s ." +ACF_GALLERY_LIGHTBOX="Lightbox Popup" +ACF_GALLERY_LIGHTBOX_DESC="Thumbnails anklickbar machen, um das ganze Bild im Lightbox PopUp anzuzeigen." +ACF_GALLERY_MODULE="Modul als Bildunterschrift" +ACF_GALLERY_MODULE_DESC="Wählen Sie ein Modul aus, das unterhalb der Bildunterschrift im Lighbox PopUp angezeigt werden soll." +ACF_GALLERY_STYLE="Stil" +ACF_GALLERY_STYLE_DESC="Wählen Sie aus, wie Ihre Galerie auf dem Frontend aussehen soll. Verfügbare Stile:

Raster: Alle Elemente haben die gleiche Breite und die gleiche Höhe. Sie können die Breite und Höhe der Elemente sowie die Anzahl der Spalten für dieses Layout festlegen.

Masonry: Alle Elemente haben die gleiche Breite, aber die Höhe der einzelnen Elemente ist unterschiedlich. Sie können die Spaltenbreite sowie die Anzahl der Spalten für dieses Layout festlegen

Slideshow: Dieses Layout bietet eine dynamische Bildwechsel-Anzeige mit praktischer Miniaturbild-Navigation für ein interaktives Galerie-Erlebnis..

Justified: Dieses Layout ist das Gegenteil des Masonry-Layouts. Alle Elemente haben die gleiche Höhe, aber unterschiedliche Breiten. Sie können die Zielzeilenhöhe festlegen. Sie wird Zielhöhe genannt, weil die Zeilenhöhe der Hebel ist, mit dem wir alles gut einpassen können. Der Algorithmus nähert sich der Zielzeilenhöhe so weit wie möglich an." +ACF_GALLERY_COLUMNS="Spalten" +ACF_GALLERY_COLUMNS_DESC="Anzahl der Bilder pro Zeile für Desktop, Tablet oder mobile Geräte." +ACF_GALLERY_GAP="Abstand zum nächsten Element" +ACF_GALLERY_GAP_DESC="Den Abstand zwischen den Bildern in der Galerie einstellen für Desktop, Tablet und mobile Geräte einstellen. " +ACF_GALLERY_ORIGINAL_IMAGE="Original-Bilder optimieren" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Größenänderung der Original-Bilder " +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="Optimieren Sie das hochgeladene Originalbild, um die Ladegeschwindigkeit zu verbessern und Speicherplatz freizugeben. Dies ist das Vollbild, das Ihre Besucher im Lightbox-PopUp sehen." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Breite" +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Ändert die Größe des Bildes auf eine bestimmte Breite. Wenn die Einstellung für die Höhe nicht verfügbar ist, wird die Größe des Bildes unter Beibehaltung des Seitenverhältnisses des Bildes geändert." +ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Höhe" +ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Ändert die Größe des Bildes auf eine bestimmte Höhe." +ACF_GALLERY_IMAGE_RESIZE="Bildgrößenänderung" +ACF_GALLERY_IMAGE_RESIZE_DESC="Wählen Sie die Methode, mit der die Größe der Miniaturbilder in der Galerie geändert werden soll.

Beschnitt : Einige Teile des Bildes können entfernt werden, um in die angegebene Breite und Höhe zu passen..

Stecken: Das Bild wird nach Bedarf gestreckt und gestaucht, damit es in die angegebene Breite und Höhe passt..

Passend: Behalten Sie das ursprüngliche Seitenverhältnis bei und passen Sie die durch die angegebene Breite und Höhe definierten Abmessungen an. Dies kann dazu führen, dass die Miniaturansichten entweder oben und unten oder links und rechts im Bild leeren Platz haben." +ACF_GALLERY_CROP="Beschnitt" +ACF_GALLERY_STRETCH="Dehnen" +ACF_GALLERY_FIT="Einpassen" +ACF_GALLERY_THUMB_WIDTH_DESC="Legen Sie die Breite für Ihre Thumnbnails in Pixeln fest." +ACF_GALLERY_THUMB_HEIGHT_DESC="Legen Sie die Höhe für die Miniaturansichten in Pixeln fest." +ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Legen Sie die Höhe für die Miniaturansichten in Pixeln fest. Wenn der Wert 0 (oder leer) ist, wird die Miniaturansicht unter Beibehaltung des Seitenverhältnisses erstellt." +ACF_GALLERY_IMAGE_QUALITY="Bildqualität" +ACF_GALLERY_IMAGE_QUALITY_DESC="Wählen Sie die Qualität für die Miniaturbilder. Die Qualität ist ein Wert, der zwischen 1 und 100 liegt. Je höher die Qualität ist, desto größer ist die Bildgröße der Miniaturansichten." +ACF_GALLERY_FOLDER_TYPE="Upload-Ziel" +; ACF_GALLERY_FOLDER_TYPE_DESC="Specify a destination folder for uploaded images.

Auto: The images will be uploaded to the following directory: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Note:
COMPONENT is the component name (i.e. content, users, etc...) where this field got populated from.
ITEM_ID is the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...

Additionally a random prefix will be added in the beginning of each file name to increase security.

Custom: Specify a custom destination folder where the images will be stored.

Available Smart Tags:
{field.field_id}: The custom field ID.
{field.item_id}: the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...
{field.item_alias}: the alias of the item this field was populated from.
{field.item_author_id}: The alias of the associated item author.
{field.item_catid}: The category ID of the associated item.
{field.item_catalias}: The category alias of the associated item." +ACF_GALLERY_ORDERING="Sortierung der Bilder" +ACF_GALLERY_ORDERING_DESC="Wählen Sie aus, wie aus, wie die Elemente in der Galerie sortiert werden sollen.

Standard : Zeigt die Bilder in der gleichen Reihenfolge an, wie sie im Galerie-Manager definiert sind (ermöglicht die Neuanordnung der Bilder).
Alphabetisch: Ordnet die Bilder in alphabetischer Reihenfolge nach Dateinamen.
Umgekehrt Alphabetisch: Zeigt die Bilder der Galerie in umgekehrter alphabetischer Reihenfolge nach dem Dateinamen an.
Zufällig : Zeigt die Bilder in zufälliger Reihenfolge." +ACF_GALLERY_DEFAULT="Standard" +ACF_GALLERY_ALPHABETICAL="Alphabetisch" +ACF_GALLERY_REVERSE_ALPHABETICAL="Umgekehrt Alphabetisch" +ACF_GALLERY_RANDOM="Zufällig" +ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Legen Sie die Höhe der Miniaturansicht und die ungefähre Höhe der einzelnen Galerieelemente fest." +ACF_GALLERY_ITEM_WIDTH="Element-Breite" +ACF_GALLERY_ITEM_HEIGHT="Element-Höhe" +ACF_GALLERY_ROW_TARGET_HEIGHT="Zeile Zielhöhe" +ACF_GALLERY_SLIDESHOW="Diaschau" +ACF_GALLERY_SLIDES_PER_VIEW="Folien pro Ansicht" +ACF_GALLERY_SLIDES_PER_VIEW_DESC="Festlegen, wie viele Slides gleichzeitig angezeigt werden sollen.

Beispiel:
Um 1 Slide anzuzeigen, Tragen Sie eine 1 ein
Um 1 1/2 Slides gleichzeitig anzuzeigen, tragen Sie eine 1.5 ein
Für 2 Slides tragen Sie eine 2 ein" +ACF_GALLERY_SPACE_BETWEEN_SLIDES="Abstand zwischen den Slides" +ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Definiert den Abstand zwischen den Slides in Pixeln" +ACF_GALLERY_INFINITE_LOOP="Endlosschleife" +ACF_GALLERY_INFINITE_LOOP_DESC="Wenn Sie diese Einstellung aktivieren, wird der letzte Slide nahtlos zum ersten zurückgeführt." +ACF_GALLERY_KEYBOARD_CONTROL="Tastatursteuerung" +ACF_GALLERY_KEYBOARD_CONTROL_DESC="Wenn Sie diese Einstellung aktivieren, können Sie die Slides mit den Pfeiltasten der Tastatur steuern." +ACF_GALLERY_AUTOPLAY="Autoplay" +ACF_GALLERY_AUTOPLAY_DESC="Wenn Sie diese Einstellung aktivieren, wird die Slideshow automatisch abgespielt." +ACF_GALLERY_AUTOPLAY_PROGRESS="Autoplay Fortschritt" +ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Wenn Sie diese Einstellung aktivieren, wird unten rechts an jedem Slide ein animierter Fortschrittskreis angezeigt, der angibt, wie lange es dauert, bis der nächste Slide erscheint." +ACF_GALLERY_SHOW_THUMBNAILS_DESC="Wenn Sie diese Einstellung aktivieren, werden unter der Slideshow Miniaturbilder angezeigt, so dass Sie jedes gewünschte Dia ansehen können." +ACF_GALLERY_THUMBNAILS_ARROWS="Navigationspfeile" +ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Legen Sie fest, ob Navigationspfeile im Slide für Miniaturansichten angezeigt werden sollen." +ACF_GALLERY_NAV_CONTROLS="Steuerelemente" +ACF_GALLERY_NAV_CONTROLS_DESC="Wählen Sie die Art der Navigationssteuerung.

Pfeile: Zeigt Vor- und Zurückpfeile für die Slideshow an.
Dots: Zeigt eine Dot-Navigation unter der Slideshow, mit der Sie zu anderen Slides springen können.
Pfeile und Dots: Zeigt sowohl die Pfeil- als auch die Dot-Navigation." +ACF_GALLERY_ARROWS="Pfeile" +ACF_GALLERY_DOTS="Punkte" +ACF_GALLERY_ARROWS_DOTS="Pfeile + Punkte" +ACF_GALLERY_SLIDESHOW_THEME_COLOR="Theme Farbe" +ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Definiert die Theme-Farbe" +ACF_GALLERY_AUTOPLAY_DELAY="Autoplay-Verzögerung" +ACF_GALLERY_AUTOPLAY_DELAY_DESC="Legt die Verzögerung für die automatische Wiedergabe bis zum Erscheinen des nächsten Slides in Millisekunden fest." +ACF_GALLERY_TRANSITION_EFFECT="Übergangs-Effekt" +; ACF_GALLERY_TRANSITION_EFFECT_DESC="Select the transition effect between slides.

Note: If Slides Per View is >= 2, then the Slide effect will be always used automatically." +ACF_GALLERY_SLIDE="Diaschau (slide)" +ACF_GALLERY_FADE="Blenden (fade)" +ACF_GALLERY_CUBE="Würfel" +ACF_GALLERY_COVERFLOW="Coverflow" +ACF_GALLERY_FLIP="Drehen (flip)" +ACF_GALLERY_THUMBNAILS="Vorschaubilder" +ACF_GALLERY_WATERMARK_IMAGES="Wasserzeichen Bilder" +ACF_GALLERY_WATERMARK_TEXT_PRESET="Vorlage" +ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Wählen Sie eine Vorlage für den Wasserzeichen-Text." +ACF_GALLERY_SITE_NAME="Seiten-Name" +ACF_GALLERY_SITE_URL="Seiten-URL" +ACF_GALLERY_WATERMARK_TYPE="Typ" +ACF_GALLERY_WATERMARK_TYPE_DESC="Wählen Sie, ob Sie einen Text oder ein Bild als Wasserzeichen verwenden oder das Wasserzeichen deaktivieren möchten..
Das Wasserzeichen wird auf das hochgeladene Bild angewendet.

Um das Wasserzeichen auf den Vorschaubildern zu aktivieren, aktivieren Sie \\\"Auf Vorschaubilder anwenden\\\" in der Einstellung darunter." +ACF_GALLERY_WATERMARK_TEXT_DESC="Geben Sie den Text ein, der als Wasserzeichen verwendet werden soll. Sie können auch Smart Tags verwenden.

Datei Smart Tag:
{file.filename}: myfile
{file.basename}: myfile.png" +ACF_WATERMARK_TEXT_COLOR_DESC="Wählen Sie die Textfarbe des Wasserzeichens" +ACF_WATERMARK_FONT_SIZE_DESC="Legen Sie die Schriftgröße des Wasserzeichentextes in Pixeln fest. Die Schriftgröße wird an die Breite des Bildes angepasst." +; ACF_WATERMARK_IMAGE_DESC="Select the image to be used as a watermark. The image dimensions will be adjusted based on the image's width." +; ACF_GALLERY_WATERMARK_POSITION="Position" +; ACF_GALLERY_WATERMARK_POSITION_DESC="Select the position of the watermark." +; ACF_WATERMARK_OPACITY="Opacity" +; ACF_WATERMARK_OPACITY_DESC="Set the opacity of the watermark image." +; ACF_WATERMARK_ROTATION="Rotation" +; ACF_WATERMARK_ROTATION_DESC="Rotate the watermark image by entering the desired angle in degrees. Remember, the rotation will occur clockwise." +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Apply on Thumbnails" +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Select whether to apply the watermark on the thumbnails or not. When this is disabled, the watermark will be applied only on the original image." +; ACF_GALLERY_FILTERING_TAGS="Filtering Tags" +; ACF_GALLERY_TAGS_POSITION="Tags Position" +; ACF_GALLERY_TAGS_POSITION_DESC="Select whether to disable the filtering tags, or show a list of tags above or below the gallery items. This will allow your users to filter the gallery items by tag." +; ACF_GALLERY_ABOVE_GALLERY_ITEMS="Above Gallery Items" +; ACF_GALLERY_BELOW_GALLERY_ITEMS="Below Gallery Items" +; ACF_GALLERY_TAGS_SORT="Sort Tags" +; ACF_GALLERY_TAGS_SORT_DESC="Select how the tags will be sorted." +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL="All Tags Label" +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Enter a label to add an \\\"All\\\" item at the start of the tags filter list, allowing you to view all gallery items. Leave this empty if you don't want to display an \\\"All\\\" item.

You can make this multilingual by setting it to a translation string." +; ACF_GALLERY_TAGS_MOBILE="Tags on Mobile" +; ACF_GALLERY_TAGS_MOBILE_DESC="Set whether to display the tags on mobile devices, make them appear as a dropdown or disable them." +; ACF_GALLERY_TAGS_TEXT_COLOR="Text Color" +; ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Set the tags text color." +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Text Color - Hover" +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Set the text color when hovering over a tag item." +; ACF_GALLERY_TAGS_BG_COLOR_HOVER="Background Color - Hover" +; ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Set the background color when hovering over a tag item." +; ACF_GALLERY_SHOW="Show" +; ACF_GALLERY_SHOW_AS_DROPDOWN="Show as dropdown" diff --git a/plugins/fields/acfgallery/language/en-GB/en-GB.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/en-GB/en-GB.plg_fields_acfgallery.ini new file mode 100644 index 00000000..df3f801c --- /dev/null +++ b/plugins/fields/acfgallery/language/en-GB/en-GB.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="ACF - Gallery" +ACF_GALLERY="Fields - ACF Gallery" +ACF_GALLERY_DESC="The ACF Gallery Field allows you to add beautiful image galleries to your site." +ACF_GALLERY_VALUE_DESC="Add new or select images from the Joomla Media Manager to create a gallery." +ACF_GALLERY_UPLOAD_FOLDER="Upload Folder" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Select the folder where the uploaded images for your gallery will be stored.

Note: You can also use Smart Tags." +ACF_GALLERY_LIMIT_FILES="Files Limit" +ACF_GALLERY_LIMIT_FILES_DESC="How many files can be uploaded? Enter 0 for no limit." +ACF_GALLERY_MAX_FILE_SIZE="File Size Limit" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Configure the maximum allowable size for each uploaded file in megabytes. Enter 0 for no limit.

Your server's maximum upload size is: %s." +ACF_GALLERY_LIGHTBOX="Lightbox Popup" +ACF_GALLERY_LIGHTBOX_DESC="Allow to click on a thumbnail and display the full image in a lightbox popup." +ACF_GALLERY_MODULE="Module as Caption" +ACF_GALLERY_MODULE_DESC="Select a module to be displayed below the image's caption in the lightbox popup." +ACF_GALLERY_STYLE="Style" +ACF_GALLERY_STYLE_DESC="Select how your gallery will look like on the front-end. Available styles:

Grid: All items will have the same width and the same height as well. You can define the width and height of the items as well as the number of columns for this layout.

Masonry: All items have the same width, but the height of each will be different. You can define the column's width as well as the number of columns for this layout

Slideshow: This layout offers a dynamic carousel display with convenient thumbnail navigation for a captivating and interactive gallery experience.

Justified: This layout is the opposite of the Masonry layout. All items will have the same height but different widths. You can define the target row height. It's called a target because row height is the lever we use to fit everything nicely. The algorithm will get as close to the target row height as possible." +ACF_GALLERY_COLUMNS="Columns" +ACF_GALLERY_COLUMNS_DESC="Set how many items to show per row in desktop, tablet or mobile devices." +ACF_GALLERY_GAP="Item Gap" +ACF_GALLERY_GAP_DESC="Set the space between the gallery items in desktop, tablet or mobile devices." +ACF_GALLERY_ORIGINAL_IMAGE="Optimize Original Image" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Original Image Resize" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="Optimize the original image uploaded by the user to increase loading speed and free up storage space. This is the fullscreen image your users see in the lightbox popup." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Width" +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Resize the image to a specified width. If the height setting is unavailable, the image will be resized by keeping the image's aspect ratio." +ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Height" +ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Resize the image to a specified height." +ACF_GALLERY_IMAGE_RESIZE="Image Resize" +ACF_GALLERY_IMAGE_RESIZE_DESC="Select the method that will be used to resize the thumbnails in the gallery.

Crop: Some parts of the image might be removed in order to fit the given Width and Height.

Stretch: The image will be stretched and squeezed as needed in order to fit the given Width and Height.

Fit: Keep the original aspect ratio and fit the dimensions defined by the given Width and Height. This could make the thumbnails have empty space in either top and bottom or left and right parts of the image." +ACF_GALLERY_CROP="Crop" +ACF_GALLERY_STRETCH="Stretch" +ACF_GALLERY_FIT="Fit" +ACF_GALLERY_THUMB_WIDTH_DESC="Set the width for your thumbnails in pixels." +ACF_GALLERY_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels." +ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels. If set to 0 (or empty), then the thumbnail will be generated by also keeping the aspect ratio." +ACF_GALLERY_IMAGE_QUALITY="Image Quality" +ACF_GALLERY_IMAGE_QUALITY_DESC="Choose the quality for the thumbnails. The quality is a value that ranges between 1 and 100. The higher the quality is, the higher the image size of the thumbnails will be." +ACF_GALLERY_FOLDER_TYPE="Upload Destination" +ACF_GALLERY_FOLDER_TYPE_DESC="Specify a destination folder for uploaded images.

Auto: The images will be uploaded to the following directory: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Note:
COMPONENT is the component name (i.e. content, users, etc...) where this field got populated from.
ITEM_ID is the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...

Additionally a random prefix will be added in the beginning of each file name to increase security.

Custom: Specify a custom destination folder where the images will be stored.

Available Smart Tags:
{field.field_id}: The custom field ID.
{field.item_id}: the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...
{field.item_alias}: the alias of the item this field was populated from.
{field.item_author_id}: The alias of the associated item author.
{field.item_catid}: The category ID of the associated item.
{field.item_catalias}: The category alias of the associated item." +ACF_GALLERY_ORDERING="Item Ordering" +ACF_GALLERY_ORDERING_DESC="Select how the gallery items will be ordered.

Default: Displays the gallery items in the same order as defined in the Gallery Manager (Allows reordering of gallery items).
Alphabetical: Displays the gallery items in alpabetical order based on the file name.
Reverse alphabetical: Displays the gallery items in reverse alphabetical order based on the file name.
Random: Displays the gallery items in random order." +ACF_GALLERY_DEFAULT="Default" +ACF_GALLERY_ALPHABETICAL="Alphabetical" +ACF_GALLERY_REVERSE_ALPHABETICAL="Reverse alphabetical" +ACF_GALLERY_RANDOM="Random" +ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Set the height of the thumbnail, and the approximate height of each gallery item." +ACF_GALLERY_ITEM_WIDTH="Item Width" +ACF_GALLERY_ITEM_HEIGHT="Item Height" +ACF_GALLERY_ROW_TARGET_HEIGHT="Row Target Height" +ACF_GALLERY_SLIDESHOW="Slideshow" +ACF_GALLERY_SLIDES_PER_VIEW="Slides Per View" +ACF_GALLERY_SLIDES_PER_VIEW_DESC="Set how many slides to display at the same time.

Examples:
To show 1 slide, enter: 1
To show 1 and a half slide, enter: 1.5
To show 2 slides, enter: 2" +ACF_GALLERY_SPACE_BETWEEN_SLIDES="Space Between Slides" +ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Define the distance between slides in pixels." +ACF_GALLERY_INFINITE_LOOP="Infinite Loop" +ACF_GALLERY_INFINITE_LOOP_DESC="Enabling this setting allows the last slide to seamlessly loop back to the first." +ACF_GALLERY_KEYBOARD_CONTROL="Keyboard Control" +ACF_GALLERY_KEYBOARD_CONTROL_DESC="Enabling this setting allows you to control the slides using the keyboard arrows." +ACF_GALLERY_AUTOPLAY="Autoplay" +ACF_GALLERY_AUTOPLAY_DESC="Enabling this setting will make the slideshow autoplay." +ACF_GALLERY_AUTOPLAY_PROGRESS="Autoplay Progress" +ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Enabling this setting will display a circular progress bar on the bottom-right hand side of each slider indicating how long till the next slide will appear." +ACF_GALLERY_SHOW_THUMBNAILS_DESC="Enabling this setting will display thumbnails below the slideshow, allowing you to view any slide you desire." +ACF_GALLERY_THUMBNAILS_ARROWS="Navigation Arrows" +ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Set whether to show navigation arrows in the thumbnails slider." +ACF_GALLERY_NAV_CONTROLS="Navigation Controls" +ACF_GALLERY_NAV_CONTROLS_DESC="Select the type of navigation controls.

Arrows: Displays previous and next arrows on top of the slideshow.
Dots: Display dots below the slideshow, allowing you to choose a slide.
Arrows + Dots: Display both arrows and dots pagination." +ACF_GALLERY_ARROWS="Arrows" +ACF_GALLERY_DOTS="Dots" +ACF_GALLERY_ARROWS_DOTS="Arrows + Dots" +ACF_GALLERY_SLIDESHOW_THEME_COLOR="Theme Color" +ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Define the theme color." +ACF_GALLERY_AUTOPLAY_DELAY="Autoplay Delay" +ACF_GALLERY_AUTOPLAY_DELAY_DESC="Set the autoplay delay until the next slide appears, in milliseconds." +ACF_GALLERY_TRANSITION_EFFECT="Transition Effect" +ACF_GALLERY_TRANSITION_EFFECT_DESC="Select the transition effect between slides.

Note: If Slides Per View is >= 2, then the Slide effect will be always used automatically." +ACF_GALLERY_SLIDE="Slide" +ACF_GALLERY_FADE="Fade" +ACF_GALLERY_CUBE="Cube" +ACF_GALLERY_COVERFLOW="Coverflow" +ACF_GALLERY_FLIP="Flip" +ACF_GALLERY_THUMBNAILS="Thumbnails" +ACF_GALLERY_WATERMARK_IMAGES="Watermark Images" +ACF_GALLERY_WATERMARK_TEXT_PRESET="Preset" +ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Select a preset for the watermark text." +ACF_GALLERY_SITE_NAME="Site Name" +ACF_GALLERY_SITE_URL="Site URL" +ACF_GALLERY_WATERMARK_TYPE="Type" +ACF_GALLERY_WATERMARK_TYPE_DESC="Select whether to use a text, an image as a watermark or disable the watermark.
The watermark will be applied on the image uploaded.

To enable the watermark on the thumbnails, enable \"Apply on Thumbnails\" setting below." +ACF_GALLERY_WATERMARK_TEXT_DESC="Enter the text to be used as a watermark. You can also use Smart Tags.

File Smart Tags:
{file.filename}: myfile
{file.basename}: myfile.png" +ACF_WATERMARK_TEXT_COLOR_DESC="Select the color of the watermark text." +ACF_WATERMARK_FONT_SIZE_DESC="Set the font size of the watermark text in pixels. The font size will be adjusted based on the image's width." +ACF_WATERMARK_IMAGE_DESC="Select the image to be used as a watermark. The image dimensions will be adjusted based on the image's width." +ACF_GALLERY_WATERMARK_POSITION="Position" +ACF_GALLERY_WATERMARK_POSITION_DESC="Select the position of the watermark." +ACF_WATERMARK_OPACITY="Opacity" +ACF_WATERMARK_OPACITY_DESC="Set the opacity of the watermark image." +ACF_WATERMARK_ROTATION="Rotation" +ACF_WATERMARK_ROTATION_DESC="Rotate the watermark image by entering the desired angle in degrees. Remember, the rotation will occur clockwise." +ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Apply on Thumbnails" +ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Select whether to apply the watermark on the thumbnails or not. When this is disabled, the watermark will be applied only on the original image." +ACF_GALLERY_FILTERING_TAGS="Filtering Tags" +ACF_GALLERY_TAGS_POSITION="Tags Position" +ACF_GALLERY_TAGS_POSITION_DESC="Select whether to disable the filtering tags, or show a list of tags above or below the gallery items. This will allow your users to filter the gallery items by tag." +ACF_GALLERY_ABOVE_GALLERY_ITEMS="Above Gallery Items" +ACF_GALLERY_BELOW_GALLERY_ITEMS="Below Gallery Items" +ACF_GALLERY_TAGS_SORT="Sort Tags" +ACF_GALLERY_TAGS_SORT_DESC="Select how the tags will be sorted." +ACF_GALLERY_ALL_TAGS_ITEM_LABEL="All Tags Label" +ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Enter a label to add an \"All\" item at the start of the tags filter list, allowing you to view all gallery items. Leave this empty if you don't want to display an \"All\" item.

You can make this multilingual by setting it to a translation string." +ACF_GALLERY_TAGS_MOBILE="Tags on Mobile" +ACF_GALLERY_TAGS_MOBILE_DESC="Set whether to display the tags on mobile devices, make them appear as a dropdown or disable them." +ACF_GALLERY_TAGS_TEXT_COLOR="Text Color" +ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Set the tags text color." +ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Text Color - Hover" +ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Set the text color when hovering over a tag item." +ACF_GALLERY_TAGS_BG_COLOR_HOVER="Background Color - Hover" +ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Set the background color when hovering over a tag item." +ACF_GALLERY_SHOW="Show" +ACF_GALLERY_SHOW_AS_DROPDOWN="Show as dropdown" \ No newline at end of file diff --git a/plugins/fields/acfgallery/language/en-GB/en-GB.plg_fields_acfgallery.sys.ini b/plugins/fields/acfgallery/language/en-GB/en-GB.plg_fields_acfgallery.sys.ini new file mode 100644 index 00000000..6de489b7 --- /dev/null +++ b/plugins/fields/acfgallery/language/en-GB/en-GB.plg_fields_acfgallery.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_GALLERY="Fields - ACF Gallery" +ACF_GALLERY_DESC="The ACF Gallery Field allows you to add beautiful image galleries to your site." \ No newline at end of file diff --git a/plugins/fields/acfgallery/language/es-ES/es-ES.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/es-ES/es-ES.plg_fields_acfgallery.ini new file mode 100644 index 00000000..13d4ad66 --- /dev/null +++ b/plugins/fields/acfgallery/language/es-ES/es-ES.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="ACF - Galeria" +ACF_GALLERY="Campos - ACF Galeria" +ACF_GALLERY_DESC="El campo Galería de ACF le permite agregar bellas galerías de imágenes a su sitio." +ACF_GALLERY_VALUE_DESC="Agregue imágenes nuevas o seleccione imágenes del Administrador de medios de Joomla para crear una galería." +ACF_GALLERY_UPLOAD_FOLDER="Cargar Carpeta" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Seleccione la carpeta donde se almacenarán las imágenes cargadas para su galería.

Nota: También puede usar etiquetas inteligentes." +ACF_GALLERY_LIMIT_FILES="Límite de Archivos" +ACF_GALLERY_LIMIT_FILES_DESC="¿Cuántos archivos se pueden subir? Ingrese 0 para no tener límite." +ACF_GALLERY_MAX_FILE_SIZE="Límite de Tamaño de Archivo" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Configure el tamaño máximo permitido para cada archivo subido en megabytes. Ingrese 0 para no tener límite.

El tamaño máximo de carga de su servidor es: %s." +ACF_GALLERY_LIGHTBOX="Ventana Emergente" +ACF_GALLERY_LIGHTBOX_DESC="Permita hacer clic en una miniatura y mostrar la imagen completa en una ventana emergente de lightbox." +ACF_GALLERY_MODULE="Módulo como Título" +ACF_GALLERY_MODULE_DESC="Seleccione un módulo que se mostrará debajo del título de la imagen en la ventana emergente." +ACF_GALLERY_STYLE="Estilo" +ACF_GALLERY_STYLE_DESC="Seleccione cómo se verá su galería en la interfaz. Estilos disponibles:

Cuadricula: todos los elementos tendrán el mismo ancho y también la misma altura. Puede definir el ancho y el alto de los elementos, así como el número de columnas para este diseño.

Masonry: todos los elementos tienen el mismo ancho, pero la altura de cada uno será diferente. Puede definir el ancho de la columna, así como el número de columnas para este diseño.

Carrusel: este diseño ofrece una visualización dinámica en carrusel con una conveniente navegación en miniatura para una experiencia de galería cautivadora e interactiva.

Justificado: este diseño es lo opuesto al diseño Masonry. Todos los artículos tendrán la misma altura pero diferentes anchos. Puede definir la altura de la fila de destino. Se llama objetivo porque la altura de la fila es la palanca que usamos para que todo quede bien. El algoritmo se acercará lo más posible a la altura de la fila objetivo." +ACF_GALLERY_COLUMNS="Columnas" +ACF_GALLERY_COLUMNS_DESC="Establezca cuántos elementos mostrar por fila en dispositivos de escritorio, tabletas o dispositivos móviles." +ACF_GALLERY_GAP="Espaciado entre elementos" +ACF_GALLERY_GAP_DESC="Establezca el espacio entre los elementos de la galería en dispositivos de escritorio, tabletas o móviles.ap" +ACF_GALLERY_ORIGINAL_IMAGE="Optimizar Imagen Original" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Redimensionar Imagen Original" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="Optimice la imagen original cargada por el usuario para aumentar la velocidad de carga y liberar espacio de almacenamiento. Esta es la imagen de pantalla completa que ven los usuarios en la ventana emergente de lightbox." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Ancho" +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Cambie el tamaño de la imagen a un ancho específico. Si la configuración de altura no está disponible, se cambiará el tamaño de la imagen manteniendo la relación de aspecto de la imagen." +ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Altura" +ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Cambie el tamaño de la imagen a una altura especificada." +ACF_GALLERY_IMAGE_RESIZE="Cambiar tamaño de imagen" +ACF_GALLERY_IMAGE_RESIZE_DESC="Seleccione el método que se utilizará para cambiar el tamaño de las miniaturas en la galería.

Recortar: algunas partes de la imagen pueden eliminarse para ajustarse al ancho y alto dados.

Estirar: la imagen se estirará y comprimirá según sea necesario para ajustarse al ancho y alto dados.

Ajustar: Mantenga la relación de aspecto original y ajuste las dimensiones definidas por el Ancho y Alto dados. Esto podría hacer que las miniaturas tengan espacios vacíos en la parte superior e inferior o en la parte izquierda y derecha de la imagen." +ACF_GALLERY_CROP="Fijo" +ACF_GALLERY_STRETCH="Estirado" +ACF_GALLERY_FIT="Adaptativo" +ACF_GALLERY_THUMB_WIDTH_DESC="Establezca el ancho de sus miniaturas en píxeles." +ACF_GALLERY_THUMB_HEIGHT_DESC="Establece la altura de tus miniaturas en píxeles." +ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Establece la altura de tus miniaturas en píxeles. Si se establece en 0 (o está vacía), la miniatura se generará manteniendo también la relación de aspecto." +ACF_GALLERY_IMAGE_QUALITY="Calidad de la imagen" +ACF_GALLERY_IMAGE_QUALITY_DESC="Elija la calidad de las miniaturas. La calidad es un valor que oscila entre 1 y 100. Cuanto mayor sea la calidad, mayor será el tamaño de la imagen de las miniaturas." +ACF_GALLERY_FOLDER_TYPE="Cargar Destino" +ACF_GALLERY_FOLDER_TYPE_DESC="Especifique una carpeta de destino para las imágenes cargadas.

Auto: Las imágenes se cargarán en el siguiente directorio: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Nota:
COMPONENT es el nombre del componente (es decir, contenido, usuarios, etc.) donde El campo se completó desde.
ITEM_ID es el ID del elemento desde el que se completó este campo. es decir, un ID de artículo de Joomla, ID de usuario, ID de contacto, etc.

Además, se agregará un prefijo aleatorio al principio de cada nombre de archivo para aumentar la seguridad.

Personalizado: especifique una carpeta de destino personalizada donde se almacenarán las imágenes.

Etiquetas Inteligentes Disponibles:
{field.field_id}: el ID del campo personalizado.
{field.item_id}: el ID del elemento desde el que se completó este campo. es decir, un ID de artículo de Joomla, ID de usuario, ID de contacto, etc...
{field.item_alias}: el alias del elemento desde el que se completó este campo.
{field.item_author_id}: el alias del autor del elemento asociado.
{field. item_catid}: el ID de categoría del artículo asociado.
{field.item_catalias}: el alias de categoría del artículo asociado." +ACF_GALLERY_ORDERING="Orden de Artiçículos" +ACF_GALLERY_ORDERING_DESC="Seleccione cómo se ordenarán los elementos de la galería.

Por defecto:Muestra los elementos de la galería en el mismo orden definido en el Administrador de la galería (permite reordenar los elementos de la galería).
Alfabético: Muestra los elementos de la galería en orden alfabético según el nombre del archivo.
Alfabético Inverso: Muestra los elementos de la galería en orden alfabético inverso según el nombre del archivo.
Aleatorio:Muestra los elementos de la galería en orden aleatorio." +ACF_GALLERY_DEFAULT="Por Defecto" +ACF_GALLERY_ALPHABETICAL="Alfabético" +ACF_GALLERY_REVERSE_ALPHABETICAL="Alfabético Inverso" +ACF_GALLERY_RANDOM="Aleatorio" +ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Establezca la altura de la miniatura y la altura aproximada de cada elemento de la galería." +ACF_GALLERY_ITEM_WIDTH="Ancho del elemento" +ACF_GALLERY_ITEM_HEIGHT="Altura del elemento" +ACF_GALLERY_ROW_TARGET_HEIGHT="Altura de la fila destino" +ACF_GALLERY_SLIDESHOW="Carrusel" +ACF_GALLERY_SLIDES_PER_VIEW="Diapositivas por vista" +ACF_GALLERY_SLIDES_PER_VIEW_DESC="Establezca cuántas diapositivas se mostrarán al mismo tiempo.

Ejemplos
: Para mostrar 1 diapositiva, ingrese: 1
Para mostrar 1 diapositiva y media, ingrese: 1.5
Para mostrar 2 diapositivas, ingrese: 2" +ACF_GALLERY_SPACE_BETWEEN_SLIDES="Espacio entre diapositivas" +ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Defina la distancia entre diapositivas en píxeles." +ACF_GALLERY_INFINITE_LOOP="Bucle infinito" +ACF_GALLERY_INFINITE_LOOP_DESC="Activar esta configuración permite que las diapositivas vuelva a reproducirse de manera continua desde la primera." +ACF_GALLERY_KEYBOARD_CONTROL="Control con teclado" +ACF_GALLERY_KEYBOARD_CONTROL_DESC="Habilitar esta configuración le permite controlar las diapositivas usando las flechas del teclado." +ACF_GALLERY_AUTOPLAY="Auto-reproducción" +ACF_GALLERY_AUTOPLAY_DESC="Habilitar esta configuración hará que la presentación de diapositivas se reproduzca automáticamente." +ACF_GALLERY_AUTOPLAY_PROGRESS="Progreso de reproducción automática" +ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Al habilitar esta configuración se mostrará una barra de progreso circular en la parte inferior derecha de cada control deslizante que indica cuánto tiempo falta para que aparezca la siguiente diapositiva." +ACF_GALLERY_SHOW_THUMBNAILS_DESC="Al habilitar esta configuración se mostrarán miniaturas debajo de la presentación de diapositivas, lo que le permitirá ver cualquier diapositiva que desee." +ACF_GALLERY_THUMBNAILS_ARROWS="Flechas de navegación" +ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Establezca si se mostrarán flechas de navegación en el control deslizante de miniaturas." +ACF_GALLERY_NAV_CONTROLS="Controles de Navegación" +ACF_GALLERY_NAV_CONTROLS_DESC="Seleccione el tipo de controles de navegación.

Flechas: muestra las flechas anterior y siguiente en la parte superior de la presentación de diapositivas.
Puntos: muestra puntos debajo de la presentación de diapositivas, lo que le permite elegir una diapositiva.
Flechas + puntos: muestra la paginación con flechas y puntos." +ACF_GALLERY_ARROWS="Flechas" +ACF_GALLERY_DOTS="Puntos" +ACF_GALLERY_ARROWS_DOTS="Flechas + Puntos" +ACF_GALLERY_SLIDESHOW_THEME_COLOR="Color del tema" +ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Defina el color del tema." +ACF_GALLERY_AUTOPLAY_DELAY="Retraso de reproducción automática" +ACF_GALLERY_AUTOPLAY_DELAY_DESC="Establezca el retraso de la reproducción automática hasta que aparezca la siguiente diapositiva, en milisegundos." +ACF_GALLERY_TRANSITION_EFFECT="Efecto de transición" +ACF_GALLERY_TRANSITION_EFFECT_DESC="Seleccione el efecto de transición entre diapositivas.

Nota: Si Diapositivas por vista es >= 2, el efecto Diapositiva siempre se usará automáticamente." +ACF_GALLERY_SLIDE="Diapositiva" +ACF_GALLERY_FADE="Fundido" +ACF_GALLERY_CUBE="Cubo" +ACF_GALLERY_COVERFLOW="Cubierta" +ACF_GALLERY_FLIP="Voltear" +ACF_GALLERY_THUMBNAILS="Miniaturas" +ACF_GALLERY_WATERMARK_IMAGES="Imágenes de marca de agua" +ACF_GALLERY_WATERMARK_TEXT_PRESET="Preestablecido" +ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Seleccione un ajuste preestablecido para el texto de la marca de agua." +ACF_GALLERY_SITE_NAME="Nombre del sitio" +ACF_GALLERY_SITE_URL="URL del Sitio " +ACF_GALLERY_WATERMARK_TYPE="Tipo" +ACF_GALLERY_WATERMARK_TYPE_DESC="Seleccione si desea utilizar un texto, una imagen como marca de agua o desactivar la marca de agua.
La marca de agua se aplicará a la imagen cargada.

Para habilitar la marca de agua en las miniaturas, habilite la configuración \\\"Aplicar en miniaturas\\\" a continuación." +ACF_GALLERY_WATERMARK_TEXT_DESC="Ingrese el texto que se utilizará como marca de agua. También puedes usar etiquetas inteligentes.

Etiquetas inteligentes de archivo:
{file.filename}: myfile
{file.basename}: myfile.png" +ACF_WATERMARK_TEXT_COLOR_DESC="Seleccione el color del texto de la marca de agua." +ACF_WATERMARK_FONT_SIZE_DESC="Establezca el tamaño de fuente del texto de la marca de agua en píxeles. El tamaño de fuente se ajustará según el ancho de la imagen." +ACF_WATERMARK_IMAGE_DESC="Seleccione la imagen que se utilizará como marca de agua. Las dimensiones de la imagen se ajustarán según el ancho de la imagen." +ACF_GALLERY_WATERMARK_POSITION="Posición" +ACF_GALLERY_WATERMARK_POSITION_DESC="Seleccione la posición de la marca de agua." +ACF_WATERMARK_OPACITY="Opacidad" +ACF_WATERMARK_OPACITY_DESC="Establece la opacidad de la imagen de la marca de agua." +ACF_WATERMARK_ROTATION="Rotación" +ACF_WATERMARK_ROTATION_DESC="Gire la imagen de la marca de agua ingresando el ángulo deseado en grados. Recuerde, la rotación se producirá en el sentido de las agujas del reloj." +ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Aplicar en miniaturas" +ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Seleccione si desea aplicar la marca de agua en las miniaturas o no. Cuando esto está deshabilitado, la marca de agua se aplicará solo en la imagen original." +ACF_GALLERY_FILTERING_TAGS="Etiquetas de filtrado" +ACF_GALLERY_TAGS_POSITION="Posición de las etiquetas" +ACF_GALLERY_TAGS_POSITION_DESC="Seleccione si desea desactivar las etiquetas de filtrado o mostrar una lista de etiquetas encima o debajo de los elementos de la galería. Esto permitirá a sus usuarios filtrar los elementos de la galería por etiqueta." +ACF_GALLERY_ABOVE_GALLERY_ITEMS="Elementos anteriores de la galería" +ACF_GALLERY_BELOW_GALLERY_ITEMS="Elementos posteriores de la galería" +ACF_GALLERY_TAGS_SORT="Ordenar etiquetas" +ACF_GALLERY_TAGS_SORT_DESC="Seleccione cómo se ordenarán las etiquetas." +ACF_GALLERY_ALL_TAGS_ITEM_LABEL="Nombre de todas las etiquetas" +ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Ingrese una etiqueta para agregar un elemento \\\"All\\\" al inicio de la lista de filtros de etiquetas, lo que le permitirá ver todos los elementos de la galería. Déjelo vacío si no desea mostrar un elemento \\\"All\\\".

Puede hacerlo multilingüe configurándolo en una cadena de traducción." +ACF_GALLERY_TAGS_MOBILE="Etiquetas en el móvil" +ACF_GALLERY_TAGS_MOBILE_DESC="Establezca si desea mostrar las etiquetas en dispositivos móviles, hacer que aparezcan como un menú desplegable o desactivarlas." +ACF_GALLERY_TAGS_TEXT_COLOR="Color del Texto" +ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Establezca el color del texto de las etiquetas." +ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Color del texto: pasar el cursor" +ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Establezca el color del texto al pasar el cursor sobre un elemento de etiqueta." +ACF_GALLERY_TAGS_BG_COLOR_HOVER="Color de fondo: pasar el cursor" +ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Establezca el color de fondo al pasar el cursor sobre un elemento de etiqueta." +ACF_GALLERY_SHOW="Mostrar" +ACF_GALLERY_SHOW_AS_DROPDOWN="Mostrar como desplegable" diff --git a/plugins/fields/acfgallery/language/es-ES/es-ES.plg_fields_acfgallery.sys.ini b/plugins/fields/acfgallery/language/es-ES/es-ES.plg_fields_acfgallery.sys.ini new file mode 100644 index 00000000..2959939a --- /dev/null +++ b/plugins/fields/acfgallery/language/es-ES/es-ES.plg_fields_acfgallery.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_GALLERY="Campos - ACF Gallery" +ACF_GALLERY_DESC="El campo Galería de ACF le permite agregar bellas galerías de imágenes a su sitio." diff --git a/plugins/fields/acfgallery/language/fr-FR/fr-FR.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/fr-FR/fr-FR.plg_fields_acfgallery.ini new file mode 100644 index 00000000..5c21dae0 --- /dev/null +++ b/plugins/fields/acfgallery/language/fr-FR/fr-FR.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="Galerie ACF " +ACF_GALLERY="Champs : galerie ACF" +ACF_GALLERY_DESC="Le champ galerie ACF vous permet d'ajouter de belles galeries d'images à votre site" +ACF_GALLERY_VALUE_DESC="Ajoutez ou sélectionnez des images depuis le gestionnaire de médias pour créer la galerie" +ACF_GALLERY_UPLOAD_FOLDER="Dossier d'pload" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Sélectionnez le dossier où enregistrer les images de votre galerie.

Note : Vous pouvez aussi utiliser les Smart Tags." +ACF_GALLERY_LIMIT_FILES="Nb maximal de fichiers" +ACF_GALLERY_LIMIT_FILES_DESC="Combien de fichiers peuvent être transférés ? Saisir 0 pour aucune limite" +ACF_GALLERY_MAX_FILE_SIZE="Limite de poids des fichiers" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Configurez la taille maximale en mégaoctets autorisée pour chaque fichier. Saisissez 0 pour ne pas limiter.

Le limite du serveur est : %s." +ACF_GALLERY_LIGHTBOX="Popup lightbox" +ACF_GALLERY_LIGHTBOX_DESC="Permet de cliquer sur l'image pour afficher l'original dans un popup lightbox." +ACF_GALLERY_MODULE="Module comme légende" +; ACF_GALLERY_MODULE_DESC="Select a module to be displayed below the image's caption in the lightbox popup." +ACF_GALLERY_STYLE="Style" +; ACF_GALLERY_STYLE_DESC="Select how your gallery will look like on the front-end. Available styles:

Grid: All items will have the same width and the same height as well. You can define the width and height of the items as well as the number of columns for this layout.

Masonry: All items have the same width, but the height of each will be different. You can define the column's width as well as the number of columns for this layout

Slideshow: This layout offers a dynamic carousel display with convenient thumbnail navigation for a captivating and interactive gallery experience.

Justified: This layout is the opposite of the Masonry layout. All items will have the same height but different widths. You can define the target row height. It's called a target because row height is the lever we use to fit everything nicely. The algorithm will get as close to the target row height as possible." +ACF_GALLERY_COLUMNS="Colonnes" +ACF_GALLERY_COLUMNS_DESC="Définissez le nombre d'images par ligne sur ordinateur, tablette et téléphone portable" +; ACF_GALLERY_GAP="Item Gap" +ACF_GALLERY_GAP_DESC="Définissez l'espace entre les éléments de la galerie sur ordinateur, tablette ou téléphone portable" +ACF_GALLERY_ORIGINAL_IMAGE="Optimiser l'image originale ?" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Redimensionner l'image originale" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="Optimiser l'image originale pour améliorer le temps d'affichage et réduire la consommation d'espace disque. Il s'agit de l'image que vos visiteurs verront dans la lightbox." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Largeur" +; ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Resize the image to a specified width. If the height setting is unavailable, the image will be resized by keeping the image's aspect ratio." +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Height" +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Resize the image to a specified height." +; ACF_GALLERY_IMAGE_RESIZE="Image Resize" +; ACF_GALLERY_IMAGE_RESIZE_DESC="Select the method that will be used to resize the thumbnails in the gallery.

Crop: Some parts of the image might be removed in order to fit the given Width and Height.

Stretch: The image will be stretched and squeezed as needed in order to fit the given Width and Height.

Fit: Keep the original aspect ratio and fit the dimensions defined by the given Width and Height. This could make the thumbnails have empty space in either top and bottom or left and right parts of the image." +ACF_GALLERY_CROP="Recadrage" +ACF_GALLERY_STRETCH="Étirement" +ACF_GALLERY_FIT="Ajustage" +ACF_GALLERY_THUMB_WIDTH_DESC="Largeur des vignettes en pixels" +; ACF_GALLERY_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels." +; ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels. If set to 0 (or empty), then the thumbnail will be generated by also keeping the aspect ratio." +; ACF_GALLERY_IMAGE_QUALITY="Image Quality" +; ACF_GALLERY_IMAGE_QUALITY_DESC="Choose the quality for the thumbnails. The quality is a value that ranges between 1 and 100. The higher the quality is, the higher the image size of the thumbnails will be." +ACF_GALLERY_FOLDER_TYPE="Destination des images" +; ACF_GALLERY_FOLDER_TYPE_DESC="Specify a destination folder for uploaded images.

Auto: The images will be uploaded to the following directory: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Note:
COMPONENT is the component name (i.e. content, users, etc...) where this field got populated from.
ITEM_ID is the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...

Additionally a random prefix will be added in the beginning of each file name to increase security.

Custom: Specify a custom destination folder where the images will be stored.

Available Smart Tags:
{field.field_id}: The custom field ID.
{field.item_id}: the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...
{field.item_alias}: the alias of the item this field was populated from.
{field.item_author_id}: The alias of the associated item author.
{field.item_catid}: The category ID of the associated item.
{field.item_catalias}: The category alias of the associated item." +ACF_GALLERY_ORDERING="Tri des éléments" +ACF_GALLERY_ORDERING_DESC="Définissez l'ordre de tri des images de la galerie.

Défaut: Affiche les images dans le même ordre qye dans le gestionnaire de galeries (Avec possibilité de changer le mode de tri).
Alphabétique: Affiche les images dans l'ordre alphabétique des noms de fichiers.
Alphabétique inverse: Affiche les images dans l'ordre alphabétique inverse des noms de fichiers.
Aléatoire: Affiche les images dans un ordre aléatoire" +ACF_GALLERY_DEFAULT="Défaut" +ACF_GALLERY_ALPHABETICAL="Alphabétique" +ACF_GALLERY_REVERSE_ALPHABETICAL="Alphabétique inverse" +ACF_GALLERY_RANDOM="Aléatoire" +; ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Set the height of the thumbnail, and the approximate height of each gallery item." +; ACF_GALLERY_ITEM_WIDTH="Item Width" +; ACF_GALLERY_ITEM_HEIGHT="Item Height" +; ACF_GALLERY_ROW_TARGET_HEIGHT="Row Target Height" +; ACF_GALLERY_SLIDESHOW="Slideshow" +; ACF_GALLERY_SLIDES_PER_VIEW="Slides Per View" +; ACF_GALLERY_SLIDES_PER_VIEW_DESC="Set how many slides to display at the same time.

Examples:
To show 1 slide, enter: 1
To show 1 and a half slide, enter: 1.5
To show 2 slides, enter: 2" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES="Space Between Slides" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Define the distance between slides in pixels." +; ACF_GALLERY_INFINITE_LOOP="Infinite Loop" +; ACF_GALLERY_INFINITE_LOOP_DESC="Enabling this setting allows the last slide to seamlessly loop back to the first." +; ACF_GALLERY_KEYBOARD_CONTROL="Keyboard Control" +; ACF_GALLERY_KEYBOARD_CONTROL_DESC="Enabling this setting allows you to control the slides using the keyboard arrows." +; ACF_GALLERY_AUTOPLAY="Autoplay" +; ACF_GALLERY_AUTOPLAY_DESC="Enabling this setting will make the slideshow autoplay." +; ACF_GALLERY_AUTOPLAY_PROGRESS="Autoplay Progress" +; ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Enabling this setting will display a circular progress bar on the bottom-right hand side of each slider indicating how long till the next slide will appear." +; ACF_GALLERY_SHOW_THUMBNAILS_DESC="Enabling this setting will display thumbnails below the slideshow, allowing you to view any slide you desire." +; ACF_GALLERY_THUMBNAILS_ARROWS="Navigation Arrows" +; ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Set whether to show navigation arrows in the thumbnails slider." +; ACF_GALLERY_NAV_CONTROLS="Navigation Controls" +; ACF_GALLERY_NAV_CONTROLS_DESC="Select the type of navigation controls.

Arrows: Displays previous and next arrows on top of the slideshow.
Dots: Display dots below the slideshow, allowing you to choose a slide.
Arrows + Dots: Display both arrows and dots pagination." +; ACF_GALLERY_ARROWS="Arrows" +; ACF_GALLERY_DOTS="Dots" +; ACF_GALLERY_ARROWS_DOTS="Arrows + Dots" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR="Theme Color" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Define the theme color." +; ACF_GALLERY_AUTOPLAY_DELAY="Autoplay Delay" +; ACF_GALLERY_AUTOPLAY_DELAY_DESC="Set the autoplay delay until the next slide appears, in milliseconds." +; ACF_GALLERY_TRANSITION_EFFECT="Transition Effect" +; ACF_GALLERY_TRANSITION_EFFECT_DESC="Select the transition effect between slides.

Note: If Slides Per View is >= 2, then the Slide effect will be always used automatically." +; ACF_GALLERY_SLIDE="Slide" +; ACF_GALLERY_FADE="Fade" +; ACF_GALLERY_CUBE="Cube" +; ACF_GALLERY_COVERFLOW="Coverflow" +; ACF_GALLERY_FLIP="Flip" +ACF_GALLERY_THUMBNAILS="Vignettes" +; ACF_GALLERY_WATERMARK_IMAGES="Watermark Images" +; ACF_GALLERY_WATERMARK_TEXT_PRESET="Preset" +; ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Select a preset for the watermark text." +; ACF_GALLERY_SITE_NAME="Site Name" +; ACF_GALLERY_SITE_URL="Site URL" +; ACF_GALLERY_WATERMARK_TYPE="Type" +; ACF_GALLERY_WATERMARK_TYPE_DESC="Select whether to use a text, an image as a watermark or disable the watermark.
The watermark will be applied on the image uploaded.

To enable the watermark on the thumbnails, enable \\\"Apply on Thumbnails\\\" setting below." +; ACF_GALLERY_WATERMARK_TEXT_DESC="Enter the text to be used as a watermark. You can also use Smart Tags.

File Smart Tags:
{file.filename}: myfile
{file.basename}: myfile.png" +; ACF_WATERMARK_TEXT_COLOR_DESC="Select the color of the watermark text." +; ACF_WATERMARK_FONT_SIZE_DESC="Set the font size of the watermark text in pixels. The font size will be adjusted based on the image's width." +; ACF_WATERMARK_IMAGE_DESC="Select the image to be used as a watermark. The image dimensions will be adjusted based on the image's width." +; ACF_GALLERY_WATERMARK_POSITION="Position" +; ACF_GALLERY_WATERMARK_POSITION_DESC="Select the position of the watermark." +; ACF_WATERMARK_OPACITY="Opacity" +; ACF_WATERMARK_OPACITY_DESC="Set the opacity of the watermark image." +; ACF_WATERMARK_ROTATION="Rotation" +; ACF_WATERMARK_ROTATION_DESC="Rotate the watermark image by entering the desired angle in degrees. Remember, the rotation will occur clockwise." +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Apply on Thumbnails" +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Select whether to apply the watermark on the thumbnails or not. When this is disabled, the watermark will be applied only on the original image." +; ACF_GALLERY_FILTERING_TAGS="Filtering Tags" +; ACF_GALLERY_TAGS_POSITION="Tags Position" +; ACF_GALLERY_TAGS_POSITION_DESC="Select whether to disable the filtering tags, or show a list of tags above or below the gallery items. This will allow your users to filter the gallery items by tag." +; ACF_GALLERY_ABOVE_GALLERY_ITEMS="Above Gallery Items" +; ACF_GALLERY_BELOW_GALLERY_ITEMS="Below Gallery Items" +; ACF_GALLERY_TAGS_SORT="Sort Tags" +; ACF_GALLERY_TAGS_SORT_DESC="Select how the tags will be sorted." +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL="All Tags Label" +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Enter a label to add an \\\"All\\\" item at the start of the tags filter list, allowing you to view all gallery items. Leave this empty if you don't want to display an \\\"All\\\" item.

You can make this multilingual by setting it to a translation string." +; ACF_GALLERY_TAGS_MOBILE="Tags on Mobile" +; ACF_GALLERY_TAGS_MOBILE_DESC="Set whether to display the tags on mobile devices, make them appear as a dropdown or disable them." +; ACF_GALLERY_TAGS_TEXT_COLOR="Text Color" +; ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Set the tags text color." +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Text Color - Hover" +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Set the text color when hovering over a tag item." +; ACF_GALLERY_TAGS_BG_COLOR_HOVER="Background Color - Hover" +; ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Set the background color when hovering over a tag item." +; ACF_GALLERY_SHOW="Show" +; ACF_GALLERY_SHOW_AS_DROPDOWN="Show as dropdown" diff --git a/plugins/fields/acfgallery/language/it-IT/it-IT.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/it-IT/it-IT.plg_fields_acfgallery.ini new file mode 100644 index 00000000..90f34271 --- /dev/null +++ b/plugins/fields/acfgallery/language/it-IT/it-IT.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="ACF - Galleria" +ACF_GALLERY="Campi - ACF Galleria" +ACF_GALLERY_DESC="Il campo ACF Galleria ti permette di aggiungere fantastiche gallerie di immagini al tuo sito." +ACF_GALLERY_VALUE_DESC="Aggiungi o seleziona nuove immagini dalla Gestione media di Joomla per creare una galleria." +ACF_GALLERY_UPLOAD_FOLDER="Cartella di caricamento" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Seleziona la cartella dove le immagini caricate per la tua galleria verranno memorizzate.

Nota: Puoi anche usare smart tag." +ACF_GALLERY_LIMIT_FILES="Limite file" +ACF_GALLERY_LIMIT_FILES_DESC="Quanti file possono essere caricati? Inserisci 0 per nessun limite." +ACF_GALLERY_MAX_FILE_SIZE="Limite dimensione file" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Configura la dimensione massima consentita per ogni file caricato in megabyte. Inserisci 0 per nessun limite.

La dimensione massima di caricamento del tuo server è: %s." +ACF_GALLERY_LIGHTBOX="Finestra popup lightbox" +ACF_GALLERY_LIGHTBOX_DESC="Consenti di fare clic su una miniatura e visualizzare l'immagine completa in un popup lightbox." +ACF_GALLERY_MODULE="Modulo come didascalia" +; ACF_GALLERY_MODULE_DESC="Select a module to be displayed below the image's caption in the lightbox popup." +ACF_GALLERY_STYLE="Stile" +ACF_GALLERY_STYLE_DESC="Scegli come apparirà la tua galleria nel front-end. Stili disponibili:

Grid: Tutti gli elementi avranno la stessa larghezza e la stessa altezza. Puoi definire la larghezza e l'altezza degli elementi, nonché il numero di colonne per questo layout

Masonry: Tutti gli elementi avranno la stessa larghezza, ma l'altezza di ciascuno sarà diversa. Puoi definire la larghezza delle colonne e il numero di colonne per questo layout.

Slideshow: Questo layout offre una visualizzazione dinamica a carosello con una comoda navigazione a miniature per un'esperienza di galleria coinvolgente e interattiva.

Justified: Questo layout è l'opposto del layout Masonry. Tutti gli elementi avranno la stessa altezza ma larghezze diverse. Puoi definire l'altezza target delle righe. È chiamata target perché l'altezza delle righe è la leva che usiamo per adattare tutto perfettamente. L'algoritmo si avvicinerà il più possibile all'altezza target delle righe." +ACF_GALLERY_COLUMNS="Colonne" +ACF_GALLERY_COLUMNS_DESC="Imposta quanti elementi mostrare per riga su desktop, tablet o dispositivi mobili." +; ACF_GALLERY_GAP="Item Gap" +ACF_GALLERY_GAP_DESC="Imposta lo spazio tra gli elementi della galleria su desktop, tablet o dispositivi mobili." +ACF_GALLERY_ORIGINAL_IMAGE="Ottimizza immagine originale" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Ridimensiona immagine originale" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="Ottimizza l'immagine originale caricata dall'utente per aumentare la velocità di caricamento e liberare spazio di archiviazione. Questa è l'immagine a schermo intero che i tuoi utenti vedono nel popup lightbox." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Larghezza" +; ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Resize the image to a specified width. If the height setting is unavailable, the image will be resized by keeping the image's aspect ratio." +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Height" +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Resize the image to a specified height." +; ACF_GALLERY_IMAGE_RESIZE="Image Resize" +; ACF_GALLERY_IMAGE_RESIZE_DESC="Select the method that will be used to resize the thumbnails in the gallery.

Crop: Some parts of the image might be removed in order to fit the given Width and Height.

Stretch: The image will be stretched and squeezed as needed in order to fit the given Width and Height.

Fit: Keep the original aspect ratio and fit the dimensions defined by the given Width and Height. This could make the thumbnails have empty space in either top and bottom or left and right parts of the image." +ACF_GALLERY_CROP="Ritaglia" +ACF_GALLERY_STRETCH="Allunga" +ACF_GALLERY_FIT="Adatta" +ACF_GALLERY_THUMB_WIDTH_DESC="Imposta la larghezza delle miniature in pixel." +; ACF_GALLERY_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels." +; ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels. If set to 0 (or empty), then the thumbnail will be generated by also keeping the aspect ratio." +; ACF_GALLERY_IMAGE_QUALITY="Image Quality" +; ACF_GALLERY_IMAGE_QUALITY_DESC="Choose the quality for the thumbnails. The quality is a value that ranges between 1 and 100. The higher the quality is, the higher the image size of the thumbnails will be." +ACF_GALLERY_FOLDER_TYPE="Destinazione caricamento" +; ACF_GALLERY_FOLDER_TYPE_DESC="Specify a destination folder for uploaded images.

Auto: The images will be uploaded to the following directory: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Note:
COMPONENT is the component name (i.e. content, users, etc...) where this field got populated from.
ITEM_ID is the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...

Additionally a random prefix will be added in the beginning of each file name to increase security.

Custom: Specify a custom destination folder where the images will be stored.

Available Smart Tags:
{field.field_id}: The custom field ID.
{field.item_id}: the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...
{field.item_alias}: the alias of the item this field was populated from.
{field.item_author_id}: The alias of the associated item author.
{field.item_catid}: The category ID of the associated item.
{field.item_catalias}: The category alias of the associated item." +ACF_GALLERY_ORDERING="Ordinamento elementi" +ACF_GALLERY_ORDERING_DESC="Seleziona come verranno ordinati gli elementi della galleria.

Predefinito: visualizza gli elementi della galleria nello stesso ordine definito in Gestione galleria (consente il riordino degli elementi della galleria).
Alfabetico: visualizza gli elementi della galleria in ordine alpabetico in base al nome del file.
Alfabetico inverso: visualizza gli elementi della galleria in ordine alfabetico inverso in base al nome del file.
Casuale: visualizza gli elementi della galleria in ordine casuale." +ACF_GALLERY_DEFAULT="Predefinito" +ACF_GALLERY_ALPHABETICAL="Alfabetico" +ACF_GALLERY_REVERSE_ALPHABETICAL="Alfabetico inverso" +ACF_GALLERY_RANDOM="Casuale" +; ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Set the height of the thumbnail, and the approximate height of each gallery item." +; ACF_GALLERY_ITEM_WIDTH="Item Width" +; ACF_GALLERY_ITEM_HEIGHT="Item Height" +; ACF_GALLERY_ROW_TARGET_HEIGHT="Row Target Height" +; ACF_GALLERY_SLIDESHOW="Slideshow" +; ACF_GALLERY_SLIDES_PER_VIEW="Slides Per View" +; ACF_GALLERY_SLIDES_PER_VIEW_DESC="Set how many slides to display at the same time.

Examples:
To show 1 slide, enter: 1
To show 1 and a half slide, enter: 1.5
To show 2 slides, enter: 2" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES="Space Between Slides" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Define the distance between slides in pixels." +; ACF_GALLERY_INFINITE_LOOP="Infinite Loop" +; ACF_GALLERY_INFINITE_LOOP_DESC="Enabling this setting allows the last slide to seamlessly loop back to the first." +; ACF_GALLERY_KEYBOARD_CONTROL="Keyboard Control" +; ACF_GALLERY_KEYBOARD_CONTROL_DESC="Enabling this setting allows you to control the slides using the keyboard arrows." +; ACF_GALLERY_AUTOPLAY="Autoplay" +; ACF_GALLERY_AUTOPLAY_DESC="Enabling this setting will make the slideshow autoplay." +; ACF_GALLERY_AUTOPLAY_PROGRESS="Autoplay Progress" +; ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Enabling this setting will display a circular progress bar on the bottom-right hand side of each slider indicating how long till the next slide will appear." +; ACF_GALLERY_SHOW_THUMBNAILS_DESC="Enabling this setting will display thumbnails below the slideshow, allowing you to view any slide you desire." +; ACF_GALLERY_THUMBNAILS_ARROWS="Navigation Arrows" +; ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Set whether to show navigation arrows in the thumbnails slider." +; ACF_GALLERY_NAV_CONTROLS="Navigation Controls" +; ACF_GALLERY_NAV_CONTROLS_DESC="Select the type of navigation controls.

Arrows: Displays previous and next arrows on top of the slideshow.
Dots: Display dots below the slideshow, allowing you to choose a slide.
Arrows + Dots: Display both arrows and dots pagination." +; ACF_GALLERY_ARROWS="Arrows" +; ACF_GALLERY_DOTS="Dots" +; ACF_GALLERY_ARROWS_DOTS="Arrows + Dots" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR="Theme Color" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Define the theme color." +; ACF_GALLERY_AUTOPLAY_DELAY="Autoplay Delay" +; ACF_GALLERY_AUTOPLAY_DELAY_DESC="Set the autoplay delay until the next slide appears, in milliseconds." +; ACF_GALLERY_TRANSITION_EFFECT="Transition Effect" +; ACF_GALLERY_TRANSITION_EFFECT_DESC="Select the transition effect between slides.

Note: If Slides Per View is >= 2, then the Slide effect will be always used automatically." +; ACF_GALLERY_SLIDE="Slide" +; ACF_GALLERY_FADE="Fade" +; ACF_GALLERY_CUBE="Cube" +; ACF_GALLERY_COVERFLOW="Coverflow" +; ACF_GALLERY_FLIP="Flip" +ACF_GALLERY_THUMBNAILS="Miniature" +; ACF_GALLERY_WATERMARK_IMAGES="Watermark Images" +; ACF_GALLERY_WATERMARK_TEXT_PRESET="Preset" +; ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Select a preset for the watermark text." +; ACF_GALLERY_SITE_NAME="Site Name" +; ACF_GALLERY_SITE_URL="Site URL" +; ACF_GALLERY_WATERMARK_TYPE="Type" +; ACF_GALLERY_WATERMARK_TYPE_DESC="Select whether to use a text, an image as a watermark or disable the watermark.
The watermark will be applied on the image uploaded.

To enable the watermark on the thumbnails, enable \\\"Apply on Thumbnails\\\" setting below." +; ACF_GALLERY_WATERMARK_TEXT_DESC="Enter the text to be used as a watermark. You can also use Smart Tags.

File Smart Tags:
{file.filename}: myfile
{file.basename}: myfile.png" +; ACF_WATERMARK_TEXT_COLOR_DESC="Select the color of the watermark text." +; ACF_WATERMARK_FONT_SIZE_DESC="Set the font size of the watermark text in pixels. The font size will be adjusted based on the image's width." +; ACF_WATERMARK_IMAGE_DESC="Select the image to be used as a watermark. The image dimensions will be adjusted based on the image's width." +; ACF_GALLERY_WATERMARK_POSITION="Position" +; ACF_GALLERY_WATERMARK_POSITION_DESC="Select the position of the watermark." +; ACF_WATERMARK_OPACITY="Opacity" +; ACF_WATERMARK_OPACITY_DESC="Set the opacity of the watermark image." +; ACF_WATERMARK_ROTATION="Rotation" +; ACF_WATERMARK_ROTATION_DESC="Rotate the watermark image by entering the desired angle in degrees. Remember, the rotation will occur clockwise." +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Apply on Thumbnails" +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Select whether to apply the watermark on the thumbnails or not. When this is disabled, the watermark will be applied only on the original image." +; ACF_GALLERY_FILTERING_TAGS="Filtering Tags" +; ACF_GALLERY_TAGS_POSITION="Tags Position" +; ACF_GALLERY_TAGS_POSITION_DESC="Select whether to disable the filtering tags, or show a list of tags above or below the gallery items. This will allow your users to filter the gallery items by tag." +; ACF_GALLERY_ABOVE_GALLERY_ITEMS="Above Gallery Items" +; ACF_GALLERY_BELOW_GALLERY_ITEMS="Below Gallery Items" +; ACF_GALLERY_TAGS_SORT="Sort Tags" +; ACF_GALLERY_TAGS_SORT_DESC="Select how the tags will be sorted." +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL="All Tags Label" +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Enter a label to add an \\\"All\\\" item at the start of the tags filter list, allowing you to view all gallery items. Leave this empty if you don't want to display an \\\"All\\\" item.

You can make this multilingual by setting it to a translation string." +; ACF_GALLERY_TAGS_MOBILE="Tags on Mobile" +; ACF_GALLERY_TAGS_MOBILE_DESC="Set whether to display the tags on mobile devices, make them appear as a dropdown or disable them." +; ACF_GALLERY_TAGS_TEXT_COLOR="Text Color" +; ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Set the tags text color." +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Text Color - Hover" +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Set the text color when hovering over a tag item." +; ACF_GALLERY_TAGS_BG_COLOR_HOVER="Background Color - Hover" +; ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Set the background color when hovering over a tag item." +; ACF_GALLERY_SHOW="Show" +; ACF_GALLERY_SHOW_AS_DROPDOWN="Show as dropdown" diff --git a/plugins/fields/acfgallery/language/sv-SE/sv-SE.plg_fields_acfgallery.ini b/plugins/fields/acfgallery/language/sv-SE/sv-SE.plg_fields_acfgallery.ini new file mode 100644 index 00000000..af1878a9 --- /dev/null +++ b/plugins/fields/acfgallery/language/sv-SE/sv-SE.plg_fields_acfgallery.ini @@ -0,0 +1,127 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGALLERY_LABEL="ACF - Gallery" +ACF_GALLERY="Fält - ACF Gallery" +ACF_GALLERY_DESC="ACF Gallery Field låter dig lägga till snygga bildgallerier på din webbplats." +ACF_GALLERY_VALUE_DESC="Lägg till nya eller välj bilder från Joomla Media Manager för att skapa ett galleri." +ACF_GALLERY_UPLOAD_FOLDER="Uppladdningsmapp" +ACF_GALLERY_UPLOAD_FOLDER_DESC="Välj mapp där de uppladdade bilderna till ditt galleri ska sparas.

Observera: Du kan även använda smarta taggar." +ACF_GALLERY_LIMIT_FILES="Filbegränsning" +ACF_GALLERY_LIMIT_FILES_DESC="Hur många filer kan laddas upp? Ange 0 för ingen begränsning." +ACF_GALLERY_MAX_FILE_SIZE="Filstorlek gräns" +ACF_GALLERY_MAX_FILE_SIZE_DESC="Ange högsta tillåtna storlek för varje uppladdad fil i megabyte. Ange 0 för obegränsad.

Servern tillåter maxuppladdningsstorlek: %s." +ACF_GALLERY_LIGHTBOX="Lightbox Popup" +ACF_GALLERY_LIGHTBOX_DESC="Tillåt att klicka på en miniatyr och visa hela bilden i en lightbox popup." +ACF_GALLERY_MODULE="Modul som bildtext" +; ACF_GALLERY_MODULE_DESC="Select a module to be displayed below the image's caption in the lightbox popup." +ACF_GALLERY_STYLE="Stil" +; ACF_GALLERY_STYLE_DESC="Select how your gallery will look like on the front-end. Available styles:

Grid: All items will have the same width and the same height as well. You can define the width and height of the items as well as the number of columns for this layout.

Masonry: All items have the same width, but the height of each will be different. You can define the column's width as well as the number of columns for this layout

Slideshow: This layout offers a dynamic carousel display with convenient thumbnail navigation for a captivating and interactive gallery experience.

Justified: This layout is the opposite of the Masonry layout. All items will have the same height but different widths. You can define the target row height. It's called a target because row height is the lever we use to fit everything nicely. The algorithm will get as close to the target row height as possible." +ACF_GALLERY_COLUMNS="Columns" +ACF_GALLERY_COLUMNS_DESC="Ställ in hur många objekt som ska visas per rad på datorer, surfplattor eller mobila enheter." +; ACF_GALLERY_GAP="Item Gap" +ACF_GALLERY_GAP_DESC="Ställ in utrymmet mellan galleriobjekten på datorer, surfplattor eller mobila enheter." +ACF_GALLERY_ORIGINAL_IMAGE="Optimera originalbild" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE="Skala om originalbild" +ACF_GALLERY_ORIGINAL_IMAGE_RESIZE_DESC="Optimera originalbilden som laddats upp av användare för att öka laddningshastighet och frigöra lagringsutrymme. Det här är helskärmsbilden som användarna ser i lightbox popup-fönstret." +ACF_GALLERY_ORIGINAL_IMAGE_WIDTH="Bredd" +; ACF_GALLERY_ORIGINAL_IMAGE_WIDTH_DESC="Resize the image to a specified width. If the height setting is unavailable, the image will be resized by keeping the image's aspect ratio." +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT="Height" +; ACF_GALLERY_ORIGINAL_IMAGE_HEIGHT_DESC="Resize the image to a specified height." +; ACF_GALLERY_IMAGE_RESIZE="Image Resize" +; ACF_GALLERY_IMAGE_RESIZE_DESC="Select the method that will be used to resize the thumbnails in the gallery.

Crop: Some parts of the image might be removed in order to fit the given Width and Height.

Stretch: The image will be stretched and squeezed as needed in order to fit the given Width and Height.

Fit: Keep the original aspect ratio and fit the dimensions defined by the given Width and Height. This could make the thumbnails have empty space in either top and bottom or left and right parts of the image." +ACF_GALLERY_CROP="Beskär" +ACF_GALLERY_STRETCH="Sträck ut" +ACF_GALLERY_FIT="Passa in" +ACF_GALLERY_THUMB_WIDTH_DESC="Ställ in bredden för miniatyrerna i pixlar." +; ACF_GALLERY_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels." +; ACF_GALLERY_SLIDESHOW_THUMB_HEIGHT_DESC="Set the height for your thumbnails in pixels. If set to 0 (or empty), then the thumbnail will be generated by also keeping the aspect ratio." +; ACF_GALLERY_IMAGE_QUALITY="Image Quality" +; ACF_GALLERY_IMAGE_QUALITY_DESC="Choose the quality for the thumbnails. The quality is a value that ranges between 1 and 100. The higher the quality is, the higher the image size of the thumbnails will be." +ACF_GALLERY_FOLDER_TYPE="Uppladdningsmapp" +; ACF_GALLERY_FOLDER_TYPE_DESC="Specify a destination folder for uploaded images.

Auto: The images will be uploaded to the following directory: /media/acfgallery/com_COMPONENT/ITEM_ID/%s.

Note:
COMPONENT is the component name (i.e. content, users, etc...) where this field got populated from.
ITEM_ID is the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...

Additionally a random prefix will be added in the beginning of each file name to increase security.

Custom: Specify a custom destination folder where the images will be stored.

Available Smart Tags:
{field.field_id}: The custom field ID.
{field.item_id}: the ID of the item this field was populated from. i.e. a Joomla article ID, User ID, Contact ID, etc...
{field.item_alias}: the alias of the item this field was populated from.
{field.item_author_id}: The alias of the associated item author.
{field.item_catid}: The category ID of the associated item.
{field.item_catalias}: The category alias of the associated item." +ACF_GALLERY_ORDERING="Sortering" +ACF_GALLERY_ORDERING_DESC="Välj hur objekten i galleriet ska sorteras.

Standard: Visar objekten i galleriet i samma ordning som de har angivits iGallerihanteraren (Tillåter omsortering).
Alfabetisk: Visar objekten i galleriet i alfabetisk ordning baserat på filnamn.
Omvänd alfabetisk: Visar objekten i galleriet i omvänd alfabetisk ordning baserat på fil namn.
Slumpvis: Visar objekten i galleriet i slumpvis ordning." +ACF_GALLERY_DEFAULT="Standard" +ACF_GALLERY_ALPHABETICAL="Alfabetisk" +ACF_GALLERY_REVERSE_ALPHABETICAL="Omvänd alfabetisk" +ACF_GALLERY_RANDOM="Slumpmässig" +; ACF_GALLERY_JUSTIFIED_ITEM_HEIGHT_DESC="Set the height of the thumbnail, and the approximate height of each gallery item." +; ACF_GALLERY_ITEM_WIDTH="Item Width" +; ACF_GALLERY_ITEM_HEIGHT="Item Height" +; ACF_GALLERY_ROW_TARGET_HEIGHT="Row Target Height" +; ACF_GALLERY_SLIDESHOW="Slideshow" +; ACF_GALLERY_SLIDES_PER_VIEW="Slides Per View" +; ACF_GALLERY_SLIDES_PER_VIEW_DESC="Set how many slides to display at the same time.

Examples:
To show 1 slide, enter: 1
To show 1 and a half slide, enter: 1.5
To show 2 slides, enter: 2" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES="Space Between Slides" +; ACF_GALLERY_SPACE_BETWEEN_SLIDES_DESC="Define the distance between slides in pixels." +; ACF_GALLERY_INFINITE_LOOP="Infinite Loop" +; ACF_GALLERY_INFINITE_LOOP_DESC="Enabling this setting allows the last slide to seamlessly loop back to the first." +; ACF_GALLERY_KEYBOARD_CONTROL="Keyboard Control" +; ACF_GALLERY_KEYBOARD_CONTROL_DESC="Enabling this setting allows you to control the slides using the keyboard arrows." +; ACF_GALLERY_AUTOPLAY="Autoplay" +; ACF_GALLERY_AUTOPLAY_DESC="Enabling this setting will make the slideshow autoplay." +; ACF_GALLERY_AUTOPLAY_PROGRESS="Autoplay Progress" +; ACF_GALLERY_AUTOPLAY_PROGRESS_DESC="Enabling this setting will display a circular progress bar on the bottom-right hand side of each slider indicating how long till the next slide will appear." +; ACF_GALLERY_SHOW_THUMBNAILS_DESC="Enabling this setting will display thumbnails below the slideshow, allowing you to view any slide you desire." +; ACF_GALLERY_THUMBNAILS_ARROWS="Navigation Arrows" +; ACF_GALLERY_THUMBNAILS_ARROWS_DESC="Set whether to show navigation arrows in the thumbnails slider." +; ACF_GALLERY_NAV_CONTROLS="Navigation Controls" +; ACF_GALLERY_NAV_CONTROLS_DESC="Select the type of navigation controls.

Arrows: Displays previous and next arrows on top of the slideshow.
Dots: Display dots below the slideshow, allowing you to choose a slide.
Arrows + Dots: Display both arrows and dots pagination." +; ACF_GALLERY_ARROWS="Arrows" +; ACF_GALLERY_DOTS="Dots" +; ACF_GALLERY_ARROWS_DOTS="Arrows + Dots" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR="Theme Color" +; ACF_GALLERY_SLIDESHOW_THEME_COLOR_DESC="Define the theme color." +; ACF_GALLERY_AUTOPLAY_DELAY="Autoplay Delay" +; ACF_GALLERY_AUTOPLAY_DELAY_DESC="Set the autoplay delay until the next slide appears, in milliseconds." +; ACF_GALLERY_TRANSITION_EFFECT="Transition Effect" +; ACF_GALLERY_TRANSITION_EFFECT_DESC="Select the transition effect between slides.

Note: If Slides Per View is >= 2, then the Slide effect will be always used automatically." +; ACF_GALLERY_SLIDE="Slide" +; ACF_GALLERY_FADE="Fade" +; ACF_GALLERY_CUBE="Cube" +; ACF_GALLERY_COVERFLOW="Coverflow" +; ACF_GALLERY_FLIP="Flip" +ACF_GALLERY_THUMBNAILS="Miniatyrer" +; ACF_GALLERY_WATERMARK_IMAGES="Watermark Images" +; ACF_GALLERY_WATERMARK_TEXT_PRESET="Preset" +; ACF_GALLERY_WATERMARK_TEXT_PRESET_DESC="Select a preset for the watermark text." +; ACF_GALLERY_SITE_NAME="Site Name" +; ACF_GALLERY_SITE_URL="Site URL" +; ACF_GALLERY_WATERMARK_TYPE="Type" +; ACF_GALLERY_WATERMARK_TYPE_DESC="Select whether to use a text, an image as a watermark or disable the watermark.
The watermark will be applied on the image uploaded.

To enable the watermark on the thumbnails, enable \\\"Apply on Thumbnails\\\" setting below." +; ACF_GALLERY_WATERMARK_TEXT_DESC="Enter the text to be used as a watermark. You can also use Smart Tags.

File Smart Tags:
{file.filename}: myfile
{file.basename}: myfile.png" +; ACF_WATERMARK_TEXT_COLOR_DESC="Select the color of the watermark text." +; ACF_WATERMARK_FONT_SIZE_DESC="Set the font size of the watermark text in pixels. The font size will be adjusted based on the image's width." +; ACF_WATERMARK_IMAGE_DESC="Select the image to be used as a watermark. The image dimensions will be adjusted based on the image's width." +; ACF_GALLERY_WATERMARK_POSITION="Position" +; ACF_GALLERY_WATERMARK_POSITION_DESC="Select the position of the watermark." +; ACF_WATERMARK_OPACITY="Opacity" +; ACF_WATERMARK_OPACITY_DESC="Set the opacity of the watermark image." +; ACF_WATERMARK_ROTATION="Rotation" +; ACF_WATERMARK_ROTATION_DESC="Rotate the watermark image by entering the desired angle in degrees. Remember, the rotation will occur clockwise." +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS="Apply on Thumbnails" +; ACF_GALLERY_WATERMARK_APPLY_ON_THUMBNAILS_DESC="Select whether to apply the watermark on the thumbnails or not. When this is disabled, the watermark will be applied only on the original image." +; ACF_GALLERY_FILTERING_TAGS="Filtering Tags" +; ACF_GALLERY_TAGS_POSITION="Tags Position" +; ACF_GALLERY_TAGS_POSITION_DESC="Select whether to disable the filtering tags, or show a list of tags above or below the gallery items. This will allow your users to filter the gallery items by tag." +; ACF_GALLERY_ABOVE_GALLERY_ITEMS="Above Gallery Items" +; ACF_GALLERY_BELOW_GALLERY_ITEMS="Below Gallery Items" +; ACF_GALLERY_TAGS_SORT="Sort Tags" +; ACF_GALLERY_TAGS_SORT_DESC="Select how the tags will be sorted." +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL="All Tags Label" +; ACF_GALLERY_ALL_TAGS_ITEM_LABEL_DESC="Enter a label to add an \\\"All\\\" item at the start of the tags filter list, allowing you to view all gallery items. Leave this empty if you don't want to display an \\\"All\\\" item.

You can make this multilingual by setting it to a translation string." +; ACF_GALLERY_TAGS_MOBILE="Tags on Mobile" +; ACF_GALLERY_TAGS_MOBILE_DESC="Set whether to display the tags on mobile devices, make them appear as a dropdown or disable them." +; ACF_GALLERY_TAGS_TEXT_COLOR="Text Color" +; ACF_GALLERY_TAGS_TEXT_COLOR_DESC="Set the tags text color." +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER="Text Color - Hover" +; ACF_GALLERY_TAGS_TEXT_COLOR_HOVER_DESC="Set the text color when hovering over a tag item." +; ACF_GALLERY_TAGS_BG_COLOR_HOVER="Background Color - Hover" +; ACF_GALLERY_TAGS_BG_COLOR_HOVER_DESC="Set the background color when hovering over a tag item." +; ACF_GALLERY_SHOW="Show" +; ACF_GALLERY_SHOW_AS_DROPDOWN="Show as dropdown" diff --git a/plugins/fields/acfgallery/params/acfgallery.xml b/plugins/fields/acfgallery/params/acfgallery.xml new file mode 100644 index 00000000..65e4177d --- /dev/null +++ b/plugins/fields/acfgallery/params/acfgallery.xml @@ -0,0 +1,469 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfgallery/script.install.helper.php b/plugins/fields/acfgallery/script.install.helper.php new file mode 100644 index 00000000..f7cb6839 --- /dev/null +++ b/plugins/fields/acfgallery/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfgalleryInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfgallery/script.install.php b/plugins/fields/acfgallery/script.install.php new file mode 100644 index 00000000..4c4cb85c --- /dev/null +++ b/plugins/fields/acfgallery/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFGalleryInstallerScript extends PlgFieldsACFGalleryInstallerScriptHelper +{ + public $alias = 'acfgallery'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfgallery/tmpl/acfgallery.php b/plugins/fields/acfgallery/tmpl/acfgallery.php new file mode 100644 index 00000000..bc16e9af --- /dev/null +++ b/plugins/fields/acfgallery/tmpl/acfgallery.php @@ -0,0 +1,83 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$gallery_data = $field->value) +{ + return; +} + +require_once JPATH_SITE . '/plugins/fields/acfgallery/fields/helper.php'; + +if (is_string($gallery_data) && !$gallery_data = json_decode($gallery_data, true)) +{ + return; +} + +$gallery_items = isset($gallery_data['items']) ? $gallery_data['items'] : []; + +$style = $fieldParams->get('style', 'grid'); +$style = ACFGalleryHelper::getStyle($style); + +$payload = [ + 'items' => ACFGalleryHelper::prepareItems($gallery_items), + 'ordering' => $fieldParams->get('ordering', 'default'), + 'lightbox' => $fieldParams->get('lightbox', '0') === '1', + 'module' => $fieldParams->get('module', ''), + 'justified_item_height' => $fieldParams->get('justified_item_height'), + 'thumb_width' => $fieldParams->get('thumb_width', ''), + 'thumb_height' => $style === 'grid.svg' ? $fieldParams->get('thumb_height', '0') : null, + 'style' => $style, + 'columns' => $fieldParams->get('devices_columns.columns', []), + 'gap' => $fieldParams->get('devices_gap.gap', []), + // tags + 'tags_position' => $fieldParams->get('tags.position', 'disabled'), + 'tags_ordering' => $fieldParams->get('tags.ordering', 'default'), + 'all_tags_item_label' => $fieldParams->get('tags.all_tags_item_label', 'All'), + 'tags_mobile' => $fieldParams->get('tags.mobile', 'show'), + 'tags_text_color' => $fieldParams->get('tags.text_color', '#555'), + 'tags_text_color_hover' => $fieldParams->get('tags.text_color_hover', '#fff'), + 'tags_bg_color_hover' => $fieldParams->get('tags.bg_color_hover', '#1E3148') +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +$widgetName = $style === 'slideshow' ? 'Slideshow' : 'Gallery'; + +// Add Slideshow-related payload +if ($style === 'slideshow') +{ + $payload = array_merge($payload, [ + 'lightbox' => $fieldParams->get('slideshow_lightbox', '0') === '1', + 'module' => $fieldParams->get('slideshow_module', ''), + 'ordering' => $fieldParams->get('ordering', 'default'), + 'slides_per_view' => $fieldParams->get('slides_per_view', ['desktop' => 1]), + 'space_between_slides' => $fieldParams->get('space_between_slides', ['desktop' => 10]), + 'infinite_loop' => $fieldParams->get('infinite_loop', '0') === '1', + 'keyboard_control' => $fieldParams->get('keyboard_control', '0') === '1', + 'autoplay' => $fieldParams->get('autoplay', '0') === '1', + 'autoplay_delay' => $fieldParams->get('autoplay_delay', 3000), + 'autoplay_progress' => $fieldParams->get('autoplay_progress', '0') === '1', + 'show_thumbnails' => $fieldParams->get('show_thumbnails', '0') === '1', + 'show_thumbnails_arrows' => $fieldParams->get('show_thumbnails_arrows', '0') === '1', + 'nav_controls' => $fieldParams->get('nav_controls', 'none'), + 'theme_color' => $fieldParams->get('theme_color', '#333'), + 'transition_effect' => $fieldParams->get('transition_effect', 'slide') + ]); +} + +echo \NRFramework\Widgets\Helper::render($widgetName, $payload); \ No newline at end of file diff --git a/plugins/fields/acfgallery/version.php b/plugins/fields/acfgallery/version.php new file mode 100644 index 00000000..8e294844 --- /dev/null +++ b/plugins/fields/acfgallery/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfgravatar/acfgravatar.php b/plugins/fields/acfgravatar/acfgravatar.php new file mode 100644 index 00000000..873a7a7b --- /dev/null +++ b/plugins/fields/acfgravatar/acfgravatar.php @@ -0,0 +1,42 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFGravatar extends ACF_Field +{ + protected $validate = 'acfgravatarvalidator'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'ACF_GRAVATAR_HINT'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; +} diff --git a/plugins/fields/acfgravatar/acfgravatar.xml b/plugins/fields/acfgravatar/acfgravatar.xml new file mode 100644 index 00000000..40bac943 --- /dev/null +++ b/plugins/fields/acfgravatar/acfgravatar.xml @@ -0,0 +1,21 @@ + + + ACF_GRAVATAR + ACF_GRAVATAR_DESC + Tassos Marinos + May 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfgravatar.php + script.install.helper.php + version.php + params + tmpl + language + + diff --git a/plugins/fields/acfgravatar/language/en-GB/en-GB.plg_fields_acfgravatar.ini b/plugins/fields/acfgravatar/language/en-GB/en-GB.plg_fields_acfgravatar.ini new file mode 100644 index 00000000..8954bb2f --- /dev/null +++ b/plugins/fields/acfgravatar/language/en-GB/en-GB.plg_fields_acfgravatar.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGRAVATAR_LABEL="ACF - Gravatar" +ACF_GRAVATAR="Fields - ACF Gravatar" +ACF_GRAVATAR_DESC="Enter your email address to display a unique avatar." +ACF_GRAVATAR_PIXELS="Pixels" +ACF_GRAVATAR_PIXELS_DESC="Enter the avatar size in pixels. Example: 100" +ACF_GRAVATAR_ROUNDED_AVATAR="Rounded Avatar" +ACF_GRAVATAR_ROUNDED_AVATAR_DESC="Select whether the avatar will have rounded corners." +ACF_GRAVATAR_VALUE_DESC="Enter your email address to display a unique avatar." +ACF_GRAVATAR_HINT="Enter your email address" \ No newline at end of file diff --git a/plugins/fields/acfgravatar/language/en-GB/en-GB.plg_fields_acfgravatar.sys.ini b/plugins/fields/acfgravatar/language/en-GB/en-GB.plg_fields_acfgravatar.sys.ini new file mode 100644 index 00000000..5c5d7761 --- /dev/null +++ b/plugins/fields/acfgravatar/language/en-GB/en-GB.plg_fields_acfgravatar.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_GRAVATAR="Fields - ACF Gravatar" +ACF_GRAVATAR_DESC="Enter your email address to display a unique avatar." \ No newline at end of file diff --git a/plugins/fields/acfgravatar/language/es-ES/es-ES.plg_fields_acfgravatar.ini b/plugins/fields/acfgravatar/language/es-ES/es-ES.plg_fields_acfgravatar.ini new file mode 100644 index 00000000..b60aeb56 --- /dev/null +++ b/plugins/fields/acfgravatar/language/es-ES/es-ES.plg_fields_acfgravatar.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFGRAVATAR_LABEL="ACF - Gravatar" +ACF_GRAVATAR="Campos - ACF Gravatar" +ACF_GRAVATAR_DESC="Ingrese su dirección de correo electrónico para mostrar un avatar único." +ACF_GRAVATAR_PIXELS="Pixeles" +ACF_GRAVATAR_PIXELS_DESC="Introduzca el tamaño del avatar en píxeles. Ejemplo: 100" +ACF_GRAVATAR_ROUNDED_AVATAR="Avatar Redondeado" +ACF_GRAVATAR_ROUNDED_AVATAR_DESC="Seleccione si el avatar tendrá esquinas redondeadas." +ACF_GRAVATAR_VALUE_DESC="Ingrese su dirección de correo electrónico para mostrar un avatar único." +ACF_GRAVATAR_HINT="Ingrese su dirección de correo electrónico" diff --git a/plugins/fields/acfgravatar/language/es-ES/es-ES.plg_fields_acfgravatar.sys.ini b/plugins/fields/acfgravatar/language/es-ES/es-ES.plg_fields_acfgravatar.sys.ini new file mode 100644 index 00000000..e8d68838 --- /dev/null +++ b/plugins/fields/acfgravatar/language/es-ES/es-ES.plg_fields_acfgravatar.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_GRAVATAR="Campos - ACF Gravatar" +ACF_GRAVATAR_DESC="Ingrese su dirección de correo electrónico para mostrar un avatar único." diff --git a/plugins/fields/acfgravatar/params/acfgravatar.xml b/plugins/fields/acfgravatar/params/acfgravatar.xml new file mode 100644 index 00000000..0625543b --- /dev/null +++ b/plugins/fields/acfgravatar/params/acfgravatar.xml @@ -0,0 +1,18 @@ + +
+ +
+ + +
+
+
diff --git a/plugins/fields/acfgravatar/script.install.helper.php b/plugins/fields/acfgravatar/script.install.helper.php new file mode 100644 index 00000000..0a375dbf --- /dev/null +++ b/plugins/fields/acfgravatar/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfgravatarInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfgravatar/script.install.php b/plugins/fields/acfgravatar/script.install.php new file mode 100644 index 00000000..193d9b33 --- /dev/null +++ b/plugins/fields/acfgravatar/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFGravatarInstallerScript extends PlgFieldsACFGravatarInstallerScriptHelper +{ + public $alias = 'acfgravatar'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfgravatar/tmpl/acfgravatar.php b/plugins/fields/acfgravatar/tmpl/acfgravatar.php new file mode 100644 index 00000000..8324e112 --- /dev/null +++ b/plugins/fields/acfgravatar/tmpl/acfgravatar.php @@ -0,0 +1,41 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +if (!$email = $field->value) +{ + return; +} + +// Replace Smile Pack Smart Tags +if (class_exists('SmilePack\Helpers\SmartTags')) +{ + SmilePack\Helpers\SmartTags::doSmartTagReplacements($email); +} + +// Check if valid email is given +if (!filter_var($email, FILTER_VALIDATE_EMAIL)) +{ + return; +} + +// get size, if we have a rounded avatar and default icon +$size = $fieldParams->get('size', '100'); +$rounded_avatar = $fieldParams->get('rounded_avatar', false); +$rounded_avatar_att = ($rounded_avatar) ? ' style="border-radius:100%;"' : ''; +$default = 'identicon'; + +// build img element +$buffer = ''; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfgravatar/version.php b/plugins/fields/acfgravatar/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfgravatar/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfhtml5audio/acfhtml5audio.php b/plugins/fields/acfhtml5audio/acfhtml5audio.php new file mode 100644 index 00000000..f7e03cd8 --- /dev/null +++ b/plugins/fields/acfhtml5audio/acfhtml5audio.php @@ -0,0 +1,40 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFHTML5Audio extends ACF_Field +{ + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'list/song.mp3'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; +} diff --git a/plugins/fields/acfhtml5audio/acfhtml5audio.xml b/plugins/fields/acfhtml5audio/acfhtml5audio.xml new file mode 100644 index 00000000..3a2168a7 --- /dev/null +++ b/plugins/fields/acfhtml5audio/acfhtml5audio.xml @@ -0,0 +1,21 @@ + + + ACF_HTML5AUDIO + ACF_HTML5AUDIO_DESC + Tassos Marinos + April 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfhtml5audio.php + script.install.helper.php + version.php + params + tmpl + language + + diff --git a/plugins/fields/acfhtml5audio/language/ca-ES/ca-ES.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/ca-ES/ca-ES.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..08290707 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/ca-ES/ca-ES.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - Àudio HTML5" +ACF_HTML5AUDIO="camps - ACF Àudio HTML5" +ACF_HTML5AUDIO_DESC="El connector Camp d'àudio HTML5 mostra un reproductor d'àudio." +ACF_HTML5AUDIO_VALUE_DESC="L'URL de l'arxiu d'àudio. Valors possibles:

Una ruta relativa:
playlist/song.mp3

Un URL absolut:
http://www.site.com/song.wav

Formats suportats:
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/da-DK/da-DK.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/da-DK/da-DK.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..2042b3a3 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/da-DK/da-DK.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 lyd" +ACF_HTML5AUDIO="Felter - ACF HTML5 lyd" +ACF_HTML5AUDIO_DESC="HTML5 lyd felt plugin'et viser en lydafspiller." +ACF_HTML5AUDIO_VALUE_DESC="URL for lydfilen. Mulige værdier:

En relativ URL:
playliste/sang.mp3

En absolut URL:
http://www.site.com/song.wav

Understøttede formater:
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/de-DE/de-DE.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/de-DE/de-DE.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..02a88843 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/de-DE/de-DE.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Fields - ACF HTML5 Audio" +ACF_HTML5AUDIO_DESC="The HTML5 Audio field plugin displays an audio player." +ACF_HTML5AUDIO_VALUE_DESC="The URL of the audio file. Possible values:

A relative URL:
playlist/song.mp3

An absolute URL:
http://www.site.com/song.wav

Supported formats:
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/en-GB/en-GB.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/en-GB/en-GB.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..e1ce29e3 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/en-GB/en-GB.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Fields - ACF HTML5 Audio" +ACF_HTML5AUDIO_DESC="Select an audio file in the back-end and display an HTML5 Audio Player in the front-end." +ACF_HTML5AUDIO_VALUE_DESC="The URL of the audio file. Possible values:

A relative URL:
playlist/song.mp3

An absolute URL:
http://www.site.com/song.wav

Supported formats:
MP3, Wav, Ogg" \ No newline at end of file diff --git a/plugins/fields/acfhtml5audio/language/en-GB/en-GB.plg_fields_acfhtml5audio.sys.ini b/plugins/fields/acfhtml5audio/language/en-GB/en-GB.plg_fields_acfhtml5audio.sys.ini new file mode 100644 index 00000000..0839d93c --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/en-GB/en-GB.plg_fields_acfhtml5audio.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_HTML5AUDIO="Fields - ACF HTML5 Audio" +ACF_HTML5AUDIO_DESC="Select an audio file in the back-end and display an HTML5 Audio Player in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfhtml5audio/language/es-ES/es-ES.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/es-ES/es-ES.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..4d8de146 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/es-ES/es-ES.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - Audio HTML5" +ACF_HTML5AUDIO="Campos - ACF Audio HTML5" +ACF_HTML5AUDIO_DESC="El complemento de campo Audio HTML5 muestra un reproductor de audio." +ACF_HTML5AUDIO_VALUE_DESC="La URL del archivo de audio. Valores posibles:

Una URL relativa:
playlist/song.mp3

Una URL absoluta:
http://www.site.com/song.wav

Formatos admitidos:
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/es-ES/es-ES.plg_fields_acfhtml5audio.sys.ini b/plugins/fields/acfhtml5audio/language/es-ES/es-ES.plg_fields_acfhtml5audio.sys.ini new file mode 100644 index 00000000..89f714e3 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/es-ES/es-ES.plg_fields_acfhtml5audio.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_HTML5AUDIO="Campos - ACF Audio HTML5" +ACF_HTML5AUDIO_DESC="Seleccione un archivo de audio en el back-end y muestre un reproductor de audio HTML5 en el front-end." diff --git a/plugins/fields/acfhtml5audio/language/fr-FR/fr-FR.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/fr-FR/fr-FR.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..86d1081c --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/fr-FR/fr-FR.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Champs - Audio HTML5 ACF" +ACF_HTML5AUDIO_DESC="Le plugin de champ audio HTML5 affiche un lecteur audio." +ACF_HTML5AUDIO_VALUE_DESC="L'URL du fichier audio. Valeurs possibles :

Lien relatif :
playlist/song.mp3

Lien absolu :
http://www.site.com/song.wav

Format supportés ::
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/it-IT/it-IT.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/it-IT/it-IT.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..719371e7 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/it-IT/it-IT.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - audio HTML5 " +ACF_HTML5AUDIO="Campi - audio HTML5 ACF" +ACF_HTML5AUDIO_DESC="Il plugin del campo audio HTML5 visualizza un lettore audio." +ACF_HTML5AUDIO_VALUE_DESC="L'URL del file audio. Possibili valori:

Un URL relativo:
playlist/song.mp3

Un URL assoluto:
http://www.site.com/song.wav

Formati supportati:
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/nl-NL/nl-NL.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/nl-NL/nl-NL.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..7030d82c --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/nl-NL/nl-NL.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Velden - ACF HTML5 Audio" +ACF_HTML5AUDIO_DESC="De HTML5 Audio-veldplug-in toont een audiospeler." +ACF_HTML5AUDIO_VALUE_DESC="De URL van het audiobestand. Mogelijke waarden:


een relatieve URL:

afspeellijst/nummer.mp3


Een absolute URL:

http: //www.website.nl/liedje.wav


Ondersteunde formaten:

MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/ru-RU/ru-RU.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/ru-RU/ru-RU.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..f77b29ae --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/ru-RU/ru-RU.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Поля - ACF HTML5 Audio" +ACF_HTML5AUDIO_DESC="Плагин HTML5 Audio field отображает аудиоплеер." +ACF_HTML5AUDIO_VALUE_DESC="URL-адрес аудиофайла. Возможные значения:

Относительный URL-адрес:
playlist / song.mp3

Абсолютный URL-адрес :
http://www.site.com/song.wav

Поддерживаемые форматы :
MP3, Wav, Ogg " diff --git a/plugins/fields/acfhtml5audio/language/sv-SE/sv-SE.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/sv-SE/sv-SE.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..8e02b368 --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/sv-SE/sv-SE.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Fält - ACF HTML5 Audio" +ACF_HTML5AUDIO_DESC="HTML5 Audio-fält pluginen visar en ljudspelare." +ACF_HTML5AUDIO_VALUE_DESC="URLen till audiofilen. Möjliga värden:

En relativ URL:
playlist/song.mp3

En absolut URL:
http://www.site.com/song.wav

Format som stöds:
MP3, Wav, Ogg" diff --git a/plugins/fields/acfhtml5audio/language/uk-UA/uk-UA.plg_fields_acfhtml5audio.ini b/plugins/fields/acfhtml5audio/language/uk-UA/uk-UA.plg_fields_acfhtml5audio.ini new file mode 100644 index 00000000..8694c9ab --- /dev/null +++ b/plugins/fields/acfhtml5audio/language/uk-UA/uk-UA.plg_fields_acfhtml5audio.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFHTML5AUDIO_LABEL="ACF - HTML5 Audio" +ACF_HTML5AUDIO="Поля - Аудіо ACF HTML5" +ACF_HTML5AUDIO_DESC="Плагін аудіо поля HTML5 відображає аудіоплеєр." +ACF_HTML5AUDIO_VALUE_DESC="URL-адреса аудіофайлу. Можливі значення:

Відносна URL-адреса:
плейлист / song.mp3

Абсолютна URL-адреса :
http://www.site.com/song.wav |
Підтримувані формати : MP3, WAV, Ogg " diff --git a/plugins/fields/acfhtml5audio/params/acfhtml5audio.xml b/plugins/fields/acfhtml5audio/params/acfhtml5audio.xml new file mode 100644 index 00000000..82adaa3d --- /dev/null +++ b/plugins/fields/acfhtml5audio/params/acfhtml5audio.xml @@ -0,0 +1,32 @@ + +
+ +
+ + + + + + + + + +
+
+
diff --git a/plugins/fields/acfhtml5audio/script.install.helper.php b/plugins/fields/acfhtml5audio/script.install.helper.php new file mode 100644 index 00000000..d3803420 --- /dev/null +++ b/plugins/fields/acfhtml5audio/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfhtml5audioInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfhtml5audio/script.install.php b/plugins/fields/acfhtml5audio/script.install.php new file mode 100644 index 00000000..d4edb61f --- /dev/null +++ b/plugins/fields/acfhtml5audio/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFHTML5AudioInstallerScript extends PlgFieldsACFHTML5AudioInstallerScriptHelper +{ + public $alias = 'acfhtml5audio'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfhtml5audio/tmpl/acfhtml5audio.php b/plugins/fields/acfhtml5audio/tmpl/acfhtml5audio.php new file mode 100644 index 00000000..bd9af9a3 --- /dev/null +++ b/plugins/fields/acfhtml5audio/tmpl/acfhtml5audio.php @@ -0,0 +1,49 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +// Prepare source +$audios = ACFHelper::getFileSources($field->value, array('mp3', 'wav','ogg')); + +if (!is_array($audios) || count($audios) == 0) +{ + return; +} + +// Get first audio file only for now. Multiple audios will be supported in the future. +$audio = $audios[0]; + +// Setup Variables +$id = 'acf_html5audio_' . $item->id . '_' . $field->id; +$preload = $fieldParams->get('preload', 'auto'); + +// Prepare HTML attributes +$attributes = array_filter(array( + $fieldParams->get('controls', true) ? 'controls' : '', + $fieldParams->get('loop', false) ? 'loop' : '', + $fieldParams->get('muted', false) ? 'muted' : '', + $fieldParams->get('autoplay', false) ? 'autoplay' : '' +)); + +// Output +$buffer = ' + '; + +echo $buffer; diff --git a/plugins/fields/acfhtml5audio/version.php b/plugins/fields/acfhtml5audio/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfhtml5audio/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfiframe/acfiframe.php b/plugins/fields/acfiframe/acfiframe.php new file mode 100644 index 00000000..8672d437 --- /dev/null +++ b/plugins/fields/acfiframe/acfiframe.php @@ -0,0 +1,49 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFIframe extends ACF_Field +{ + protected $validate = 'url'; + + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'url'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'http://'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xxlarge w-100'; +} diff --git a/plugins/fields/acfiframe/acfiframe.xml b/plugins/fields/acfiframe/acfiframe.xml new file mode 100644 index 00000000..d8dcc4bf --- /dev/null +++ b/plugins/fields/acfiframe/acfiframe.xml @@ -0,0 +1,21 @@ + + + ACF_IFRAME + ACF_IFRAME_DESC + Tassos Marinos + May 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfiframe.php + script.install.helper.php + version.php + params + tmpl + language + + diff --git a/plugins/fields/acfiframe/language/ca-ES/ca-ES.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/ca-ES/ca-ES.plg_fields_acfiframe.ini new file mode 100644 index 00000000..978f24fd --- /dev/null +++ b/plugins/fields/acfiframe/language/ca-ES/ca-ES.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Camps - ACF IFrame" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Escriu l'URL de l'IFrame. També pots incrustar un vídeo des de Youtube o Vimeo. Exemple: http://www.tassos.gr o https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Barres de desplaçament" +ACF_IFRAME_IFRAMESCROLLING_DESC="Escull si mostrar barres de desplaçament dins l'IFrame" +ACF_IFRAME_ASYNC="Carregar asíncronament" +ACF_IFRAME_ASYNC_DESC="Si està activat, l'iframe es carregarà de manera asíncrona un cop s'hagi carregat totalment la pàgina." +ACF_IFRAME_IFRAMEPARAMS="ibuts HTML" +ACF_IFRAME_IFRAMEPARAMS_DESC="Afegeix atributs HTML extra a l'element IFrame." diff --git a/plugins/fields/acfiframe/language/da-DK/da-DK.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/da-DK/da-DK.plg_fields_acfiframe.ini new file mode 100644 index 00000000..3087d48f --- /dev/null +++ b/plugins/fields/acfiframe/language/da-DK/da-DK.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Felter - ACF IFrame" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Angiv IFrame'ens URL adresse. Du indlejrer også en video fra YouTube eller Vimeo.

Eksempelt: http://www.tassos.gr eller https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Rullebjælker" +ACF_IFRAME_IFRAMESCROLLING_DESC="Vælg om IFrame'ens rullebjælker skal vises eller ej" +ACF_IFRAME_ASYNC="Indlæs async" +ACF_IFRAME_ASYNC_DESC="Hvis aktiveret, så vil iframe'en indlæses asynkront efter at siden er blevet fuldt indlæst." +ACF_IFRAME_IFRAMEPARAMS="HTML attributter" +ACF_IFRAME_IFRAMEPARAMS_DESC="Tilføj ekstra HTML attributter til IFrame elementet." diff --git a/plugins/fields/acfiframe/language/de-DE/de-DE.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/de-DE/de-DE.plg_fields_acfiframe.ini new file mode 100644 index 00000000..d66f5da8 --- /dev/null +++ b/plugins/fields/acfiframe/language/de-DE/de-DE.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Felder - ACF IFrame" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Geben Sie die URL-Adresse des IFrames ein. Sie haben auch ein Video von YouTube oder Vimeo eingebettet.

Beispiel: http://www.tassos.gr oder https://www.youtube.com/embed/IpCdMGfducg . " +ACF_IFRAME_IFRAMESCROLLING="Bildlaufleisten" +ACF_IFRAME_IFRAMESCROLLING_DESC="Wählen Sie aus, ob die Bildlaufleisten von IFrame angezeigt werden sollen oder nicht." +ACF_IFRAME_ASYNC="Async laden" +ACF_IFRAME_ASYNC_DESC="Wenn aktiviert, wird der Iframe asynchron geladen, nachdem die Seite vollständig geladen wurde." +ACF_IFRAME_IFRAMEPARAMS="HTML-Attribute" +ACF_IFRAME_IFRAMEPARAMS_DESC="Füge dem IFrame-Element zusätzliche HTML-Attribute hinzu." diff --git a/plugins/fields/acfiframe/language/en-GB/en-GB.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/en-GB/en-GB.plg_fields_acfiframe.ini new file mode 100644 index 00000000..f52da0a8 --- /dev/null +++ b/plugins/fields/acfiframe/language/en-GB/en-GB.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Fields - ACF IFrame" +ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Enter the IFrame's URL address. You embed a video from YouTube or Vimeo too.

Example: http://www.tassos.gr or https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Scrollbars" +ACF_IFRAME_IFRAMESCROLLING_DESC="Choose whether to show IFrame's scrollbars or not" +ACF_IFRAME_ASYNC="Load Async" +ACF_IFRAME_ASYNC_DESC="If enabled, the iframe will be loaded asynchronously after the page has fully loaded." +ACF_IFRAME_IFRAMEPARAMS="HTML Attributes" +ACF_IFRAME_IFRAMEPARAMS_DESC="Add extra HTML attributes to the IFrame element." \ No newline at end of file diff --git a/plugins/fields/acfiframe/language/en-GB/en-GB.plg_fields_acfiframe.sys.ini b/plugins/fields/acfiframe/language/en-GB/en-GB.plg_fields_acfiframe.sys.ini new file mode 100644 index 00000000..b95c5729 --- /dev/null +++ b/plugins/fields/acfiframe/language/en-GB/en-GB.plg_fields_acfiframe.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_IFRAME="Fields - ACF Iframe" +ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfiframe/language/es-ES/es-ES.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/es-ES/es-ES.plg_fields_acfiframe.ini new file mode 100644 index 00000000..af3ee45e --- /dev/null +++ b/plugins/fields/acfiframe/language/es-ES/es-ES.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +ACF_IFRAME_VALUE_DESC="Ingrese la URL del IFrame" +ACF_IFRAME="Campos - ACF IFrame" +ACF_IFRAME_DESC="Ingrese una URL en el back-end para mostrarla dentro de un iFrame en el front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Introduzca la dirección URL del IFrame. Incrustara también un video de YouTube o Vimeo.

Ejemplo: http://www.tassos.gr or https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Barras de desplazamiento" +ACF_IFRAME_IFRAMESCROLLING_DESC="Elija si desea mostrar las barras de desplazamiento de IFrame o no" +ACF_IFRAME_ASYNC="Carga Asíncrona" +ACF_IFRAME_ASYNC_DESC="Si está habilitado, el iframe se cargará de forma asíncrona después de que la página se haya cargado por completo." +ACF_IFRAME_IFRAMEPARAMS="Atributos HTML" +ACF_IFRAME_IFRAMEPARAMS_DESC="Agregue atributos HTML adicionales al elemento IFrame." diff --git a/plugins/fields/acfiframe/language/es-ES/es-ES.plg_fields_acfiframe.sys.ini b/plugins/fields/acfiframe/language/es-ES/es-ES.plg_fields_acfiframe.sys.ini new file mode 100644 index 00000000..b467a5a9 --- /dev/null +++ b/plugins/fields/acfiframe/language/es-ES/es-ES.plg_fields_acfiframe.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_IFRAME="Campos - ACF IFrame" +ACF_IFRAME_DESC="Ingrese una URL en el back-end para mostrarla dentro de un iFrame en el front-end." diff --git a/plugins/fields/acfiframe/language/fr-FR/fr-FR.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/fr-FR/fr-FR.plg_fields_acfiframe.ini new file mode 100644 index 00000000..66435582 --- /dev/null +++ b/plugins/fields/acfiframe/language/fr-FR/fr-FR.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Champs - IFrame ACF" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Entrez l'adresse URL d'une IFrame. Vous pouvez aussi intégrer une vidéo depuis YouTube ou Vimeo.

Exemple : http://www.tassos.gr ou https://www.youtube.com/embed/IpCdMGfducg" +ACF_IFRAME_IFRAMESCROLLING="Barres de défilement" +ACF_IFRAME_IFRAMESCROLLING_DESC="Choisissez d'afficher les barres de défilement de l'IFrame ou non" +ACF_IFRAME_ASYNC="Charger Asynchrone" +ACF_IFRAME_ASYNC_DESC="Si activé, l'IFrame sera chargée de façon asynchrone après le chargement total de la page" +ACF_IFRAME_IFRAMEPARAMS="Attributs HTML" +ACF_IFRAME_IFRAMEPARAMS_DESC="Ajouter des attributs HTML supplémentaires à l'élément IFrame" diff --git a/plugins/fields/acfiframe/language/it-IT/it-IT.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/it-IT/it-IT.plg_fields_acfiframe.ini new file mode 100644 index 00000000..7e0622e4 --- /dev/null +++ b/plugins/fields/acfiframe/language/it-IT/it-IT.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Campi - IFrame ACF" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Immettere l'indirizzo URL dell'IFrame. Incorpora anche un video da YouTube o Vimeo.

Esempio: http://www.tassos.gr or https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Barre di scorrimento" +ACF_IFRAME_IFRAMESCROLLING_DESC="Scegli se mostrare o meno le barre di scorrimento con un IFrame" +ACF_IFRAME_ASYNC="Carica in maniera asincrona" +ACF_IFRAME_ASYNC_DESC="Se abilitato, l'Iframe verrà caricato in modo asincrono dopo che la pagina è stata caricata completamente." +ACF_IFRAME_IFRAMEPARAMS="Attributi HTML" +ACF_IFRAME_IFRAMEPARAMS_DESC="Aggiungi attributi HTML extra all'elemento IFrame." diff --git a/plugins/fields/acfiframe/language/nl-NL/nl-NL.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/nl-NL/nl-NL.plg_fields_acfiframe.ini new file mode 100644 index 00000000..4632160c --- /dev/null +++ b/plugins/fields/acfiframe/language/nl-NL/nl-NL.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Velden - ACF IFrame" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Voer het URL-adres van het IFrame in. Je kunt ook een video van YouTube of Vimeo insluiten.

Bijvoorbeeld: http://www.tassos.gr of https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Schuifbalken" +ACF_IFRAME_IFRAMESCROLLING_DESC="Kies of je IFrame-schuifbalken wilt weergeven." +ACF_IFRAME_ASYNC="Laad Asynchroon" +ACF_IFRAME_ASYNC_DESC="Indien ingeschakeld, wordt het iframe asynchroon geladen nadat de pagina volledig is geladen." +ACF_IFRAME_IFRAMEPARAMS="HTML-kenmerken" +ACF_IFRAME_IFRAMEPARAMS_DESC="Voeg extra HTML-kenmerken toe aan het IFrame-element toe." diff --git a/plugins/fields/acfiframe/language/ru-RU/ru-RU.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/ru-RU/ru-RU.plg_fields_acfiframe.ini new file mode 100644 index 00000000..04dfc802 --- /dev/null +++ b/plugins/fields/acfiframe/language/ru-RU/ru-RU.plg_fields_acfiframe.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +ACF_IFRAME="Поля - ACF IFrame" +ACF_IFRAME_DESC="Асинхронно отображать содержимое любого URL-адреса в пределах кадра." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Введите URL-адрес IFrame. Вы также встраиваете видео с YouTube или Vimeo.

Пример: http://www.tassos.gr или https://www.youtube.com/embed/IpCdMGfducg «" +ACF_IFRAME_IFRAMESCROLLING="Scrollbars" +ACF_IFRAME_IFRAMESCROLLING_DESC="Выберите, показывать ли полосы прокрутки IFrame или нет" +ACF_IFRAME_ASYNC="Загрузить асинхронно" +ACF_IFRAME_ASYNC_DESC="Если включено, iframe будет загружаться асинхронно после полной загрузки страницы." +ACF_IFRAME_IFRAMEPARAMS="Атрибуты HTML" +ACF_IFRAME_IFRAMEPARAMS_DESC="Добавить дополнительные атрибуты HTML к элементу IFrame." diff --git a/plugins/fields/acfiframe/language/sv-SE/sv-SE.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/sv-SE/sv-SE.plg_fields_acfiframe.ini new file mode 100644 index 00000000..237054a5 --- /dev/null +++ b/plugins/fields/acfiframe/language/sv-SE/sv-SE.plg_fields_acfiframe.ini @@ -0,0 +1,19 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +; ACF_IFRAME_VALUE_DESC="Enter the URL of the IFrame" +ACF_IFRAME="Fält - ACF IFrame" +; ACF_IFRAME_DESC="Enter a URL in the back-end to display it within an iFrame in the front-end." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Ange IFrames URL. Du kan bädda in en video från YouTube eller Vimeo också. Exempel: http://www.tassos.gr eller https://www.youtube.com/embed/IpCdMGfducg." +ACF_IFRAME_IFRAMESCROLLING="Rullningslister" +ACF_IFRAME_IFRAMESCROLLING_DESC="Välj om IFrames rullningslister ska visas eller inte" +ACF_IFRAME_ASYNC="Ladda Async" +ACF_IFRAME_ASYNC_DESC="Om aktiverad, laddas iframe asynkront efter att sidan har laddats helt." +ACF_IFRAME_IFRAMEPARAMS="HTML Attribut" +ACF_IFRAME_IFRAMEPARAMS_DESC="Lägg till extra HTML-attribut till IFrame-elementet." diff --git a/plugins/fields/acfiframe/language/uk-UA/uk-UA.plg_fields_acfiframe.ini b/plugins/fields/acfiframe/language/uk-UA/uk-UA.plg_fields_acfiframe.ini new file mode 100644 index 00000000..f7c3a19d --- /dev/null +++ b/plugins/fields/acfiframe/language/uk-UA/uk-UA.plg_fields_acfiframe.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFIFRAME_LABEL="ACF - IFrame" +ACF_IFRAME="Поля - ACF IFrame" +ACF_IFRAME_DESC="Відобразити вміст з будь-якої URL-адреси в кадрі асихронно." +ACF_IFRAME_IFRAMEURL="URL" +ACF_IFRAME_IFRAMEURL_DESC="Введіть URL-адресу IFrame. Ви також вставили відео з YouTube або Vimeo.
Приклад: http://www.tassos.gr або https://www.youtube.com/embed/IpCdMGfducg . " +ACF_IFRAME_IFRAMESCROLLING="Панелі прокрутки" +ACF_IFRAME_IFRAMESCROLLING_DESC="Виберіть, чи потрібно показувати смуги прокрутки IFrame чи ні" +ACF_IFRAME_ASYNC="Завантажити асинхронізацію" +ACF_IFRAME_ASYNC_DESC="Якщо увімкнено, кадр iframe буде завантажений асинхронно після повного завантаження сторінки." +ACF_IFRAME_IFRAMEPARAMS="Атрибути HTML" +ACF_IFRAME_IFRAMEPARAMS_DESC="Додати додаткові атрибути HTML до елемента IFrame." diff --git a/plugins/fields/acfiframe/params/acfiframe.xml b/plugins/fields/acfiframe/params/acfiframe.xml new file mode 100644 index 00000000..8934a359 --- /dev/null +++ b/plugins/fields/acfiframe/params/acfiframe.xml @@ -0,0 +1,28 @@ + +
+ +
+ + + + + + + + +
+
+
diff --git a/plugins/fields/acfiframe/script.install.helper.php b/plugins/fields/acfiframe/script.install.helper.php new file mode 100644 index 00000000..3f5aebdf --- /dev/null +++ b/plugins/fields/acfiframe/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfiframeInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfiframe/script.install.php b/plugins/fields/acfiframe/script.install.php new file mode 100644 index 00000000..c7bb1b3f --- /dev/null +++ b/plugins/fields/acfiframe/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFIframeInstallerScript extends PlgFieldsACFIframeInstallerScriptHelper +{ + public $alias = 'acfiframe'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfiframe/tmpl/acfiframe.php b/plugins/fields/acfiframe/tmpl/acfiframe.php new file mode 100644 index 00000000..37293422 --- /dev/null +++ b/plugins/fields/acfiframe/tmpl/acfiframe.php @@ -0,0 +1,67 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +if (!$iframe = $field->value) +{ + return; +} + +$buffer = ''; + +// Setup Variables +$id = 'acf_iframe_' . $item->id . '_' . $field->id; +$height = $fieldParams->get('iframeheight', '500px'); +$scrolling = $fieldParams->get('iframescrolling', 'auto'); +$params = $fieldParams->get('iframeparams', 'auto'); +$async = (bool) $fieldParams->get('iframeasync', false); + +// Output +$content = ' + +'; + +$buffer .= '
'; + +//if not async +if (!$async) { + $buffer .= $content; +} + +$buffer .= '
'; + +echo $buffer; + +// if async +// We can't use addScriptDeclaration() here due to a bug which is fires twices the same event. +// https://github.com/joomla/joomla-cms/issues/21004 +if ($async) { + echo ''; +} \ No newline at end of file diff --git a/plugins/fields/acfiframe/version.php b/plugins/fields/acfiframe/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfiframe/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfmap/acfmap.php b/plugins/fields/acfmap/acfmap.php new file mode 100644 index 00000000..bcb47b7f --- /dev/null +++ b/plugins/fields/acfmap/acfmap.php @@ -0,0 +1,119 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Language\Text; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFMap extends ACF_Field +{ + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return; + } + + $fieldNode->setAttribute('filter', 'raw'); + + return $fieldNode; + } + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + * + * @since 3.7.0 + */ + public function onContentPrepareForm(Joomla\CMS\Form\Form $form, $data) + { + // When editing a new and unsaved field the $data variable is passed as an array for a reason. + $data = is_array($data) ? (object) $data : $data; + + // Make sure we are manipulating the right field. + if (!isset($data->type) || $data->type != $this->_name) + { + return; + } + + // Get provider + $provider = isset($data->fieldparams['provider']) ? $data->fieldparams['provider'] : false; + + // Abort if no provider is set + if (!$provider) + { + return parent::onContentPrepareForm($form, $data); + } + + // For OSM, we only check its key when we want to use satellite + if ($provider === 'OpenStreetMap') + { + $osm_maptype = isset($data->fieldparams['openstreetmap_maptype']) ? $data->fieldparams['openstreetmap_maptype'] : 'road'; + if ($osm_maptype === 'road') + { + return parent::onContentPrepareForm($form, $data); + } + } + + $key = $this->params->get(strtolower($provider) . '_key'); + + // Display a warning message to set the API key if empty + if (empty($key)) + { + $extensionID = NRFramework\Functions::getExtensionID('acfmap', 'fields'); + $backEndURL = 'index.php?option=com_plugins&task=plugin.edit&extension_id=' . $extensionID; + $url = Uri::base() . $backEndURL; + + $provider_name = $provider; + + switch (strtolower($provider)) + { + case 'bingmap': + $provider_name = 'Bing Maps'; + break; + case 'googlemap': + $provider_name = 'Google Maps'; + break; + } + + Factory::getApplication()->enqueueMessage(Text::sprintf('ACF_MAP_API_KEY_WARNING', $provider_name, $url), 'warning'); + } + + return parent::onContentPrepareForm($form, $data); + } +} diff --git a/plugins/fields/acfmap/acfmap.xml b/plugins/fields/acfmap/acfmap.xml new file mode 100644 index 00000000..afb775d8 --- /dev/null +++ b/plugins/fields/acfmap/acfmap.xml @@ -0,0 +1,52 @@ + + + ACF_MAP + ACF_MAP_DESC + Tassos Marinos + May 2023 + Copyright (C) 2023 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfmap.php + script.install.helper.php + version.php + language + params + tmpl + fields + + + +
+ + + +
+
+
+ + img + +
diff --git a/plugins/fields/acfmap/fields/acfmap.php b/plugins/fields/acfmap/fields/acfmap.php new file mode 100644 index 00000000..34586742 --- /dev/null +++ b/plugins/fields/acfmap/fields/acfmap.php @@ -0,0 +1,66 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Form\Field\HiddenField; + +class JFormFieldACFMap extends HiddenField +{ + /** + * Method to get the field input markup. + * + * @return string The field input markup. + */ + public function getInput() + { + $this->class = 'tf-map-editor--value'; + + $value = is_string($this->value) && !empty($this->value) ? (json_decode($this->value) ? json_decode($this->value, true) : []) : []; + + $show_sidebar = isset($this->element['show_sidebar']) ? (string) $this->element['show_sidebar'] === '1' : true; + + $payload = [ + 'readonly' => $this->readonly, + 'disabled' => $this->disabled, + 'name' => $this->name, + 'width' => 700, + 'required' => $this->required, + 'id' => $this->id, + 'showSidebar' => $show_sidebar, + + + 'scale' => (string) $this->element['scale'] !== '0' ? (string) $this->element['scale'] : false, + 'pro' => true, + 'maxMarkers' => isset($this->element['maximum_markers']) ? (int) $this->element['maximum_markers'] : 0, + 'markerImage' => (string) $this->element['markerImage'], + + ]; + + + + $default_coords = (string) $this->element['default_coords']; + if ($default_coords) + { + $default_coords = array_map('trim', explode(',', $default_coords)); + if (count($default_coords) === 2) + { + $payload['lat'] = $default_coords[0]; + $payload['long'] = $default_coords[1]; + } + } + + if ($value) + { + $payload['value'] = $value; + } + + return \NRFramework\Widgets\Helper::render('MapEditor', $payload) . parent::getInput(); + } +} \ No newline at end of file diff --git a/plugins/fields/acfmap/language/en-GB/en-GB.plg_fields_acfmap.ini b/plugins/fields/acfmap/language/en-GB/en-GB.plg_fields_acfmap.ini new file mode 100644 index 00000000..0ca7591c --- /dev/null +++ b/plugins/fields/acfmap/language/en-GB/en-GB.plg_fields_acfmap.ini @@ -0,0 +1,67 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMAP_LABEL="ACF - Map" +ACF_MAP="Fields - ACF Map" +ACF_MAP_DESC="Display an interactive map based on OpenStreetMap, Google Maps, or Bing Maps and add unlimited markers." +ACF_MAP_VALUE_DESC="Pick a location from the map, enter an address or coordinates in the search field." +ACF_MAP_WIDTH="Map Width" +ACF_MAP_WIDTH_DESC="" +ACF_MAP_HEIGHT="Map Height" +ACF_MAP_HEIGHT_DESC="" +ACF_MAP_ADDRESS="Address" +ACF_MAP_ADDRESS_DESC="Enter an address or coordinates to search" +ACF_MAP_DEFAULT_COORDS="Default Map Coordinates" +ACF_MAP_DEFAULT_COORDS_DESC="Enter the default latitude and longitude coordinates separated by comma." +ACF_MAP_SCALE="Map Scale" +ACF_MAP_SCALE_DESC="Select the scale unit that appears at the bottom left side of the map." +ACF_MAP_SCALE_METRIC="Metric" +ACF_MAP_SCALE_IMPERIAL="Imperial" +ACF_MAP_PROVIDER="Provider" +ACF_MAP_PROVIDER_DESC="Select the map provider." +ACF_MAP_OPENSTREETMAP="OpenStreetMap" +ACF_MAP_GOOGLE_MAP="Google Map" +ACF_MAP_BING_MAP="Bing Map" +ACF_MAP_UNLIMITED_MARKERS="Unlimited Markers" +ACF_MAP_UNLIMITED_MARKERS_DESC="The free version allows up to 1 map marker. Upgrade to Pro to unlock unlimited map markers." +ACF_MAP_BINGMAP_APIKEY="Bing Maps API Key" +ACF_MAP_BINGMAP_APIKEY_DESC="Set your Bing Maps API Key.

Click on the 'Get a Bing Maps API Key' link for instructions on how you can get one." +ACF_MAP_BINGMAP_APIKEY_GET="Get a Bing Maps API Key" +ACF_MAP_GOOGLEMAP_APIKEY="Google Maps API Key" +ACF_MAP_GOOGLEMAP_APIKEY_DESC="Set your Google Maps API Key.

Click on the 'Get a Google Maps API Key' link for instructions on how you can get one." +ACF_MAP_GOOGLEMAP_APIKEY_GET="Get a Google Maps API Key" +ACF_MAP_API_KEY_WARNING="In order for %s to run, you will need to set your API Key. Set API Key now." +ACF_MAP_TYPE="Map Type" +ACF_MAP_TYPE_DESC="Select the map type." +ACF_MAP_SATELLITE="Satellite" +ACF_MAP_TERRAIN="Terrain" +ACF_MAP_HYBRID="Hybrid" +ACF_MAP_ROAD="Road" +ACF_MAP_DARK_MODE="Dark mode" +ACF_MAP_LIGHT_MODE="Light mode" +ACF_MAP_BIRDSEYE="Birdseye" +ACF_MAP_GRAYSCALE="Grayscale" +ACF_MAP_ORDNANCESURVEY="Ordnance Survey" +ACF_MAP_OPENSTREETMAP_APIKEY="OpenStreetMap ArcGIS API Key" +ACF_MAP_OPENSTREETMAP_APIKEY_DESC="Set your ArcGIS Developer API Key required by the OpenStreetMap to show the map in satellite mode.

Click on the 'Get an ArcGIS Developer API Key' link for instructions on how you can get one." +ACF_MAP_OPENSTREETMAP_APIKEY_GET="Get an ArcGIS Developer API Key" +ACF_MAP_ZOOM_LEVEL="Zoom Level" +ACF_MAP_ZOOM_LEVEL_DESC="Set the map zoom." +ACF_MAP_MAP_CENTER="Map Center" +ACF_MAP_MAP_CENTER_DESC="Set whether to set a fixed zoom for the map or let the map provider auto-zoom and center the map around the markers." +ACF_MAP_PRESET_ZOOM="Preset Zoom" +ACF_MAP_FIT_BOUNDS="Fit Bounds" +ACF_MAP_SET_MAP_CENTER="Set Map Center" +ACF_MAP_SET_MAP_CENTER_DESC="Define where to center the map. If no value is set, it defaults to the first marker added on the map." +ACF_MAP_ENABLE_INFO_WINDOW="Enable Info Window" +ACF_MAP_ENABLE_INFO_WINDOW_DESC="Set whether to display an info window whenever a map marker is clicked.
By default, it will show each map marker address, however, you can define a custom label and description for each map marker." +ACF_MAP_MAXIMUM_MARKERS="Maximum Markers" +ACF_MAP_MAXIMUM_MARKERS_DESC="Set the maximum number of markers that can be added on the map. Enter 0 for unlimited markers." +ACF_MAP_MARKER_IMAGE="Marker Image" +ACF_MAP_MARKER_IMAGE_DESC="Replace the default marker image by uploading an image of your choice." +ACF_MAP_SHOW_SIDEBAR="Show Sidebar" +ACF_MAP_SHOW_SIDEBAR_DESC="Set whether to show the sidebar on the left side of the map editor. The sidebar allows you to filter, edit, delete markers, as well as import locations." \ No newline at end of file diff --git a/plugins/fields/acfmap/language/en-GB/en-GB.plg_fields_acfmap.sys.ini b/plugins/fields/acfmap/language/en-GB/en-GB.plg_fields_acfmap.sys.ini new file mode 100644 index 00000000..ddcb1f12 --- /dev/null +++ b/plugins/fields/acfmap/language/en-GB/en-GB.plg_fields_acfmap.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_MAP="Fields - ACF Map" +ACF_MAP_DESC="Display an interactive map based on OpenStreetMap, Google Maps, or Bing Maps." \ No newline at end of file diff --git a/plugins/fields/acfmap/language/es-ES/es-ES.plg_fields_acfmap.ini b/plugins/fields/acfmap/language/es-ES/es-ES.plg_fields_acfmap.ini new file mode 100644 index 00000000..7a79cfff --- /dev/null +++ b/plugins/fields/acfmap/language/es-ES/es-ES.plg_fields_acfmap.ini @@ -0,0 +1,67 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMAP_LABEL="ACF - Mapa" +ACF_MAP="Campos - ACF Mapas" +ACF_MAP_DESC="Muestre un mapa interactivo basado en OpenStreetMap, Google Maps o Bing Maps y agregue marcadores ilimitados." +ACF_MAP_VALUE_DESC="Elija una ubicación del mapa, ingrese una dirección o coordenadas en el campo de búsqueda." +ACF_MAP_WIDTH="Ancho del Mapa" +ACF_MAP_WIDTH_DESC="" +ACF_MAP_HEIGHT="Altura del Mapa" +ACF_MAP_HEIGHT_DESC="" +ACF_MAP_ADDRESS="Dirección" +ACF_MAP_ADDRESS_DESC="Introduzca una dirección o coordenadas para buscar" +ACF_MAP_DEFAULT_COORDS="Coordenadas de Mapa Predeterminadas" +ACF_MAP_DEFAULT_COORDS_DESC="Introduzca las coordenadas de latitud y longitud predeterminadas separadas por comas." +ACF_MAP_SCALE="Escala del Mapa" +ACF_MAP_SCALE_DESC="Seleccione la unidad de escala que aparece en la parte inferior izquierda del mapa." +ACF_MAP_SCALE_METRIC="Métrica" +ACF_MAP_SCALE_IMPERIAL="Imperial" +ACF_MAP_PROVIDER="Proveedor" +ACF_MAP_PROVIDER_DESC="Seleccione un proveedor de mapas." +ACF_MAP_OPENSTREETMAP="OpenStreetMap" +ACF_MAP_GOOGLE_MAP="Google Map" +ACF_MAP_BING_MAP="Bing Map" +ACF_MAP_UNLIMITED_MARKERS="Marcadores Ilimitados" +ACF_MAP_UNLIMITED_MARKERS_DESC="La versión gratuita solo permite 1 marcador de mapa. Actualice a Pro para desbloquear marcadores de mapa ilimitados." +ACF_MAP_BINGMAP_APIKEY="Clave API de Bing Maps" +ACF_MAP_BINGMAP_APIKEY_DESC="Configure su clave API de Bing Maps.

Haga clic en el enlace 'Obtener una clave API de Bing Maps' para obtener instrucciones sobre cómo obtener una." +ACF_MAP_BINGMAP_APIKEY_GET="Obtener una clave API de Bing Maps" +ACF_MAP_GOOGLEMAP_APIKEY="Clave API de Google Maps" +ACF_MAP_GOOGLEMAP_APIKEY_DESC="Configure su clave de API de Google Maps.

Haga clic en el enlace 'Obtener una clave API de Google Maps' para obtener instrucciones sobre cómo obtener una." +ACF_MAP_GOOGLEMAP_APIKEY_GET="Obtener una clave API de Google Maps" +ACF_MAP_API_KEY_WARNING="Para que %s se ejecute, deberá configurar su clave API. Establecer clave API ahora." +ACF_MAP_TYPE="Tipo de Mapa" +ACF_MAP_TYPE_DESC="Seleccione el tipo de mapa" +ACF_MAP_SATELLITE="Satelite" +ACF_MAP_TERRAIN="Terreno" +ACF_MAP_HYBRID="Hibrido" +ACF_MAP_ROAD="Camino" +ACF_MAP_DARK_MODE="Modo oscuro" +ACF_MAP_LIGHT_MODE="Modo luminoso" +ACF_MAP_BIRDSEYE="Ojo de pájaro" +ACF_MAP_GRAYSCALE="Escala de grises" +ACF_MAP_ORDNANCESURVEY="Encuesta de Artillería" +ACF_MAP_OPENSTREETMAP_APIKEY="Clave API OpenStreetMap ArcGIS" +ACF_MAP_OPENSTREETMAP_APIKEY_DESC="Configure su clave API del desarrollador de ArcGIS requerida por OpenStreetMap para mostrar el mapa en modo satélite.

Haga clic en el enlace 'Obtener una clave API de ArcGIS Developer' para obtener instrucciones sobre cómo obtener una." +ACF_MAP_OPENSTREETMAP_APIKEY_GET="Obtener una clave API de ArcGIS Developer" +ACF_MAP_ZOOM_LEVEL="Nivel de Zoom" +ACF_MAP_ZOOM_LEVEL_DESC="Establecer el zoom del mapa" +ACF_MAP_MAP_CENTER="Mapa Centrado" +ACF_MAP_MAP_CENTER_DESC="Establezca si desea establecer un zoom fijo para el mapa o dejar que el proveedor del mapa haga zoom automáticamente y centre el mapa alrededor de los marcadores." +ACF_MAP_PRESET_ZOOM="Zoom preestablecido" +ACF_MAP_FIT_BOUNDS="Ajustar Límites" +ACF_MAP_SET_MAP_CENTER="Establecer Centro del Mapa" +ACF_MAP_SET_MAP_CENTER_DESC="Defina dónde centrar el mapa. Si no se establece ningún valor, el valor predeterminado es el primer marcador agregado en el mapa." +ACF_MAP_ENABLE_INFO_WINDOW="Habilitar ventana de información" +ACF_MAP_ENABLE_INFO_WINDOW_DESC="Establece si mostrar una ventana de información cada vez que se hace clic en un marcador de mapa.
De manera predeterminada, mostrará la dirección de cada marcador del mapa; sin embargo, puede definir una etiqueta personalizada y una descripción para cada marcador de mapa." +ACF_MAP_MAXIMUM_MARKERS="Marcadores máximos" +ACF_MAP_MAXIMUM_MARKERS_DESC="Establece el número máximo de marcadores que se pueden agregar en el mapa. Ingrese 0 para marcadores ilimitados." +ACF_MAP_MARKER_IMAGE="Imagen del Marcador" +ACF_MAP_MARKER_IMAGE_DESC="Reemplace la imagen del marcador predeterminado cargando una imagen de su elección." +ACF_MAP_SHOW_SIDEBAR="Mostrar barra lateral" +ACF_MAP_SHOW_SIDEBAR_DESC="Establecer si mostrar la barra lateral en el lado izquierdo del editor de mapas. La barra lateral te permite filtrar, editar, eliminar marcadores, así como importar ubicaciones." diff --git a/plugins/fields/acfmap/language/es-ES/es-ES.plg_fields_acfmap.sys.ini b/plugins/fields/acfmap/language/es-ES/es-ES.plg_fields_acfmap.sys.ini new file mode 100644 index 00000000..db2777a2 --- /dev/null +++ b/plugins/fields/acfmap/language/es-ES/es-ES.plg_fields_acfmap.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_MAP="Campos - ACF Map" +ACF_MAP_DESC="Muestre un mapa interactivo basado en OpenStreetMap, Google Maps o Bing Maps." diff --git a/plugins/fields/acfmap/params/acfmap.xml b/plugins/fields/acfmap/params/acfmap.xml new file mode 100644 index 00000000..daba0ec6 --- /dev/null +++ b/plugins/fields/acfmap/params/acfmap.xml @@ -0,0 +1,144 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfmap/script.install.helper.php b/plugins/fields/acfmap/script.install.helper.php new file mode 100644 index 00000000..f72f82a0 --- /dev/null +++ b/plugins/fields/acfmap/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfmapInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfmap/script.install.php b/plugins/fields/acfmap/script.install.php new file mode 100644 index 00000000..95de92fb --- /dev/null +++ b/plugins/fields/acfmap/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFMapInstallerScript extends PlgFieldsACFMapInstallerScriptHelper +{ + public $alias = 'acfmap'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfmap/tmpl/acfmap.php b/plugins/fields/acfmap/tmpl/acfmap.php new file mode 100644 index 00000000..26c86ea9 --- /dev/null +++ b/plugins/fields/acfmap/tmpl/acfmap.php @@ -0,0 +1,62 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; + +if (!$markers = $field->value) +{ + return; +} + +if (is_string($markers) && !$markers = json_decode($markers, true)) +{ + return; +} + +// Get Plugin Params +$plugin = PluginHelper::getPlugin('fields', 'acfmap'); +$params = new Registry($plugin->params); + +// Find provider +$provider = $fieldParams->get('provider', 'OpenStreetMap'); +$provider_key = $params->get(strtolower($provider) . '_key'); +$maptype = $fieldParams->get(strtolower($provider) . '_maptype'); + +$maptype = is_null($maptype) ? ($provider === 'GoogleMap' ? 'roadmap' : 'road') : $maptype; + +$payload = $fieldParams->flatten(); +$payload = array_merge($payload, [ + 'replaceSmartTags' => true, + 'provider_key' => $provider_key, + 'width' => $fieldParams->get('width_control.width'), + 'height' => $fieldParams->get('height_control.height'), + 'markers' => $markers, + 'map_center' => $fieldParams->get('map_center.coordinates'), + 'enable_info_window' => $fieldParams->get('enable_info_window', '0') !== '0' ? $fieldParams->get('enable_info_window', '0') : false, + + + 'pro' => true, + 'view' => $maptype, + 'scale' => $fieldParams->get('scale', '0') !== '0' ? $fieldParams->get('scale', '0') : false + +]); + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render($provider, $payload); \ No newline at end of file diff --git a/plugins/fields/acfmap/version.php b/plugins/fields/acfmap/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfmap/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfmodule/acfmodule.php b/plugins/fields/acfmodule/acfmodule.php new file mode 100644 index 00000000..f506a04b --- /dev/null +++ b/plugins/fields/acfmodule/acfmodule.php @@ -0,0 +1,110 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Form\Form; +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +use Joomla\CMS\Helper\ModuleHelper; + +class PlgFieldsACFModule extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'Modules'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + if (!$module = ModuleHelper::getModuleById($option->getValue())) + { + continue; + } + + $option->setLabel($module->title); + } + + return $options; + } + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $field->value = \NRFramework\Functions::loadModule($field->value); + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + // Include only front-end modules + $fieldNode->setAttribute('client', 0); + + return $fieldNode; + } +} diff --git a/plugins/fields/acfmodule/acfmodule.xml b/plugins/fields/acfmodule/acfmodule.xml new file mode 100644 index 00000000..7ce7ef95 --- /dev/null +++ b/plugins/fields/acfmodule/acfmodule.xml @@ -0,0 +1,20 @@ + + + ACF_MODULE + ACF_MODULE_DESC + Tassos Marinos + April 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfmodule.php + script.install.helper.php + version.php + tmpl + language + + diff --git a/plugins/fields/acfmodule/language/ca-ES/ca-ES.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/ca-ES/ca-ES.plg_fields_acfmodule.ini new file mode 100644 index 00000000..86d1ebcf --- /dev/null +++ b/plugins/fields/acfmodule/language/ca-ES/ca-ES.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Mòdul" +ACF_MODULE="Camps - ACF Mòdul" +ACF_MODULE_DESC="Renderitza qualsevol mòdul Joomla! existent" +ACF_MODULE_VALUE_DESC="Escull el mòdul del llistat a renderitzar a la part pública" diff --git a/plugins/fields/acfmodule/language/da-DK/da-DK.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/da-DK/da-DK.plg_fields_acfmodule.ini new file mode 100644 index 00000000..c12f1292 --- /dev/null +++ b/plugins/fields/acfmodule/language/da-DK/da-DK.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - modul" +ACF_MODULE="Felter - ACF modul" +ACF_MODULE_DESC="Gengiv ethvert eksisterende Joomla! modul" +ACF_MODULE_VALUE_DESC="Vælg et modul fra listen for at gengive det i frontend" diff --git a/plugins/fields/acfmodule/language/de-DE/de-DE.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/de-DE/de-DE.plg_fields_acfmodule.ini new file mode 100644 index 00000000..9973e8d2 --- /dev/null +++ b/plugins/fields/acfmodule/language/de-DE/de-DE.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Modul" +ACF_MODULE="Felder - ACF-Modul" +ACF_MODULE_DESC="Vorhandenes Joomla! -Modul rendern" +ACF_MODULE_VALUE_DESC="Wählen Sie ein Modul aus der Liste aus, das im Front-End gerendert werden soll." diff --git a/plugins/fields/acfmodule/language/en-GB/en-GB.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/en-GB/en-GB.plg_fields_acfmodule.ini new file mode 100644 index 00000000..109ec5a1 --- /dev/null +++ b/plugins/fields/acfmodule/language/en-GB/en-GB.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Module Loader" +ACF_MODULE="Fields - ACF Module Loader" +ACF_MODULE_DESC="Select any existing Joomla! Module in the back-end and render it in the front-end." +ACF_MODULE_VALUE_DESC="Select a Module from the list to render in the front-end" \ No newline at end of file diff --git a/plugins/fields/acfmodule/language/en-GB/en-GB.plg_fields_acfmodule.sys.ini b/plugins/fields/acfmodule/language/en-GB/en-GB.plg_fields_acfmodule.sys.ini new file mode 100644 index 00000000..d94508a7 --- /dev/null +++ b/plugins/fields/acfmodule/language/en-GB/en-GB.plg_fields_acfmodule.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_MODULE="Fields - ACF Module Loader" +ACF_MODULE_DESC="Select any existing Joomla! Module in the back-end and render it in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfmodule/language/es-ES/es-ES.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/es-ES/es-ES.plg_fields_acfmodule.ini new file mode 100644 index 00000000..267a10e4 --- /dev/null +++ b/plugins/fields/acfmodule/language/es-ES/es-ES.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Módulo" +ACF_MODULE="Campos - ACF Módulo" +ACF_MODULE_DESC="Renderiza cualquier módulo ¡Joomla!" +ACF_MODULE_VALUE_DESC="Seleccione un Módulo de la lista para renderizar en el front-end" diff --git a/plugins/fields/acfmodule/language/es-ES/es-ES.plg_fields_acfmodule.sys.ini b/plugins/fields/acfmodule/language/es-ES/es-ES.plg_fields_acfmodule.sys.ini new file mode 100644 index 00000000..9a2398cd --- /dev/null +++ b/plugins/fields/acfmodule/language/es-ES/es-ES.plg_fields_acfmodule.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_MODULE="Campos -ACF Module Loader" +ACF_MODULE_DESC="Seleccione cualquier módulo de Joomla existente en el back-end y renderícelo en el front-end." diff --git a/plugins/fields/acfmodule/language/fr-FR/fr-FR.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/fr-FR/fr-FR.plg_fields_acfmodule.ini new file mode 100644 index 00000000..e9b30f5c --- /dev/null +++ b/plugins/fields/acfmodule/language/fr-FR/fr-FR.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Module" +ACF_MODULE="Champs - Module ACF" +ACF_MODULE_DESC="Rendu de n'importe quel module Joomla! existant" +ACF_MODULE_VALUE_DESC="Sélectionnez un module depuis la liste pour l'afficher sur le front-end" diff --git a/plugins/fields/acfmodule/language/it-IT/it-IT.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/it-IT/it-IT.plg_fields_acfmodule.ini new file mode 100644 index 00000000..d591aa2c --- /dev/null +++ b/plugins/fields/acfmodule/language/it-IT/it-IT.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Modulo" +ACF_MODULE="Campi - ACF Modulo" +ACF_MODULE_DESC="Richiama un qualsiasi modulo joomla esistente" +ACF_MODULE_VALUE_DESC="Seleziona un modulo dall'elenco per eseguire il rendering nel front-end" diff --git a/plugins/fields/acfmodule/language/nl-NL/nl-NL.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/nl-NL/nl-NL.plg_fields_acfmodule.ini new file mode 100644 index 00000000..9aa444c1 --- /dev/null +++ b/plugins/fields/acfmodule/language/nl-NL/nl-NL.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Module" +ACF_MODULE="Velden - ACF Module" +ACF_MODULE_DESC="Toont elke bestaande Joomla! module" +ACF_MODULE_VALUE_DESC="Selecteer een module uit de lijst om op de front-end weer te geven" diff --git a/plugins/fields/acfmodule/language/pt-BR/pt-BR.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/pt-BR/pt-BR.plg_fields_acfmodule.ini new file mode 100644 index 00000000..020b3966 --- /dev/null +++ b/plugins/fields/acfmodule/language/pt-BR/pt-BR.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Módulo" +ACF_MODULE="Campos - ACF Módulo" +ACF_MODULE_DESC="Renderiza qualquer módulo existente do Joomla!" +; ACF_MODULE_VALUE_DESC="Select a Module from the list to render in the front-end" diff --git a/plugins/fields/acfmodule/language/ru-RU/ru-RU.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/ru-RU/ru-RU.plg_fields_acfmodule.ini new file mode 100644 index 00000000..49812b38 --- /dev/null +++ b/plugins/fields/acfmodule/language/ru-RU/ru-RU.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Модуль" +ACF_MODULE="Поля - Модуль ACF" +ACF_MODULE_DESC="Визуализировать любой существующий модуль Joomla!" +ACF_MODULE_VALUE_DESC="Выберите модуль из списка для рендеринга в интерфейсе" diff --git a/plugins/fields/acfmodule/language/sv-SE/sv-SE.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/sv-SE/sv-SE.plg_fields_acfmodule.ini new file mode 100644 index 00000000..5674daf3 --- /dev/null +++ b/plugins/fields/acfmodule/language/sv-SE/sv-SE.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - Module" +ACF_MODULE="Fält - ACF Module" +ACF_MODULE_DESC="Rendera befintlig Joomla! modul" +ACF_MODULE_VALUE_DESC="Välj en modul från listan att rendera i frontend." diff --git a/plugins/fields/acfmodule/language/uk-UA/uk-UA.plg_fields_acfmodule.ini b/plugins/fields/acfmodule/language/uk-UA/uk-UA.plg_fields_acfmodule.ini new file mode 100644 index 00000000..b91f435f --- /dev/null +++ b/plugins/fields/acfmodule/language/uk-UA/uk-UA.plg_fields_acfmodule.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFMODULE_LABEL="ACF - модуль" +ACF_MODULE="Поля - модуль ACF" +ACF_MODULE_DESC="Надати будь-який існуючий модуль Joomla!" +ACF_MODULE_VALUE_DESC="Вибрати модуль зі списку для візуалізації на сайті" diff --git a/plugins/fields/acfmodule/script.install.helper.php b/plugins/fields/acfmodule/script.install.helper.php new file mode 100644 index 00000000..a2f608e3 --- /dev/null +++ b/plugins/fields/acfmodule/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfmoduleInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfmodule/script.install.php b/plugins/fields/acfmodule/script.install.php new file mode 100644 index 00000000..3ca3177f --- /dev/null +++ b/plugins/fields/acfmodule/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFModuleInstallerScript extends PlgFieldsACFModuleInstallerScriptHelper +{ + public $alias = 'acfmodule'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfmodule/tmpl/acfmodule.php b/plugins/fields/acfmodule/tmpl/acfmodule.php new file mode 100644 index 00000000..6c7f2752 --- /dev/null +++ b/plugins/fields/acfmodule/tmpl/acfmodule.php @@ -0,0 +1,15 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +echo $field->value; \ No newline at end of file diff --git a/plugins/fields/acfmodule/version.php b/plugins/fields/acfmodule/version.php new file mode 100644 index 00000000..8e294844 --- /dev/null +++ b/plugins/fields/acfmodule/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfpaypal/acfpaypal.php b/plugins/fields/acfpaypal/acfpaypal.php new file mode 100644 index 00000000..589d2ad6 --- /dev/null +++ b/plugins/fields/acfpaypal/acfpaypal.php @@ -0,0 +1,65 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFPayPal extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'ACFPayPal'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + $optionData = $option->getData(); + + $value = is_string($optionData->value) ? json_decode($optionData->value, true) : $optionData->value; + + if (!is_array($value) || !isset($value['item_name']) || empty($value['item_name'])) + { + continue; + } + + $option->setLabel($value['item_name']); + } + + return $options; + } +} diff --git a/plugins/fields/acfpaypal/acfpaypal.xml b/plugins/fields/acfpaypal/acfpaypal.xml new file mode 100644 index 00000000..2df37492 --- /dev/null +++ b/plugins/fields/acfpaypal/acfpaypal.xml @@ -0,0 +1,26 @@ + + + ACF_PAYPAL + ACF_PAYPAL_DESC + Tassos Marinos + April 2020 + Copyright (C) 2020 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfpaypal.php + script.install.helper.php + version.php + fields + language + params + tmpl + + + css + img + + diff --git a/plugins/fields/acfpaypal/fields/acfpaypal.php b/plugins/fields/acfpaypal/fields/acfpaypal.php new file mode 100644 index 00000000..30b7a418 --- /dev/null +++ b/plugins/fields/acfpaypal/fields/acfpaypal.php @@ -0,0 +1,57 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\Field\TextField; +use Joomla\CMS\Language\Text; + +class JFormFieldACFPayPal extends TextField +{ + /** + * Renders the PayPal settings when viewing a specific item + * + * @return string The field input markup. + */ + protected function getInput() + { + $required = (bool) $this->required; + $requiredAtt = ($required) ? ' required="required"' : ''; + + $value = is_string($this->value) ? json_decode($this->value) : $this->value; + + // get overriden values or fall back to default + $itemName = isset($value->item_name) ? $value->item_name : ''; + $price = isset($value->price) ? $value->price : ''; + + return ' +
+
+
+ +
+
+ disabled ? ' disabled' : '') . ' /> +
+
+
+
+ +
+
+ disabled ? ' disabled' : '') . ' /> +
+
+
+ '; + } +} diff --git a/plugins/fields/acfpaypal/language/ca-ES/ca-ES.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/ca-ES/ca-ES.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..e5c88db5 --- /dev/null +++ b/plugins/fields/acfpaypal/language/ca-ES/ca-ES.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Camps - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Estableix el nom i el preu del producte o servei que vens." +ACF_PAYPAL_PAYMENT_TYPE="Tipus de pagament" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Estableix un tipus de pagament.
Pagament: Pagament únic.
Donatiu: Una donació voluntària.
Subscripció: Un pagament recurrent." +ACF_PAYPAL_CHECKOUT="Pagament" +ACF_PAYPAL_DONATION="Donació" +ACF_PAYPAL_SUBSCRIPTION="Subscripció" +ACF_PAYPAL_ACCOUNT="Adreça de correu electrònic Paypal" +ACF_PAYPAL_ACCOUNT_DESC="Estableix la teva adreça de correu electrònic PayPal on rebràs els diners. Aquesta adreça ha de ser d'un compte PayPal de negoci." +ACF_PAYPAL_PRICE="Preu" +ACF_PAYPAL_PRICE_DESC="Estableix el preu del producte o el servei que vens." +ACF_PAYPAL_PRICE_HINT="Preu de producte" +ACF_PAYPAL_ITEM_NAME="Nom de l'ítem" +ACF_PAYPAL_ITEM_NAME_DESC="Establei el nom de l'ítem que es mostrarà a la pàgina de pagament Paypal." +ACF_PAYPAL_ITEM_NAME_HINT="Nom del producte" +ACF_PAYPAL_CURRENCY="Divisa" +ACF_PAYPAL_CURRENCY_DESC="Escull la divisa en què els usuaris pagaran els productes o serveis que subministres." +ACF_PAYPAL_BILLING_INTERVAL="Interval de facturació" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Estableix la duració de la subscripció en dies." +ACF_PAYPAL_LANGUAGE="Idioma" +ACF_PAYPAL_LANGUAGE_DESC="Estableix si mantenir l'idioma per defecte o aplicar un idioma específic a la pàgina de pagament PayPal." +ACF_PAYPAL_LANGUAGE_LOCALE="Idioma local" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Estableix l'idioma local a utilitzar a la pàgina de pagament PayPal" +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Codis d'idioma local suportats" +ACF_PAYPAL_FIXED="Fixat" +ACF_PAYPAL_RETURN_URL="URL de retorn" +ACF_PAYPAL_RETURN_URL_DESC="Estableix l'URL on es redireccionarà l'usuari en completar la transacció." +ACF_PAYPAL_CANCEL_URL="URL de cancel·lació" +ACF_PAYPAL_CANCEL_URL_DESC="Estableix l'URL on es redireccionarà l'usuari en cas de cancel·lar la transacció." +ACF_PAYPAL_BUTTON_TYPE="Tipus de botó" +ACF_PAYPAL_BUTTON_TYPE_DESC="Escull el tipus de botó, pot ser un estil del llistat predefinit o utilitzar una imatge personalitzada com botó PayPal" +ACF_PAYPAL_STYLE="Estil" +ACF_PAYPAL_STYLE_SELECTOR="Estil de botó" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Escull un botó del nostre llistat predefinit." +ACF_PAYPAL_BUTTON_IMAGE="Imatge de botó" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Estableix una imatge com el teu botó PayPal." +ACF_PAYPAL_NEW_TAB="Obre PayPal a una nova pestanya" +ACF_PAYPAL_NEW_TAB_DESC="Activa obrir Paypal en una nova pestanya." +ACF_PAYPAL_SANDBOX_MODE="Mode calaix de sorra" +ACF_PAYPAL_SANDBOX_MODE_DESC="Activa el mode calaix de sorra, utilitzat per depurar el botó PayPal durant el desenvolupament." +ACF_PAYPAL_SANDBOX_ACCOUNT="Adreçá de correu electrònic calaix de sorra" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Escriu la teva adreça de correu electrònic PayPal calaix de sorra. Si es deixa en blanc, s'utilitzarà l'adreça de correu electrònic PayPal." diff --git a/plugins/fields/acfpaypal/language/da-DK/da-DK.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/da-DK/da-DK.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..4e7750ec --- /dev/null +++ b/plugins/fields/acfpaypal/language/da-DK/da-DK.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Felter - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Angiv navnet og prisen på produktet eller servicen som du sælger." +ACF_PAYPAL_PAYMENT_TYPE="Betalingstype" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Vælg en betalingstype.
Tjek ud: En engangsbetaling.
Donation: En frivillig donation.
Abonnement: En gentagende betaling." +ACF_PAYPAL_CHECKOUT="Tjek ud" +ACF_PAYPAL_DONATION="Donation" +ACF_PAYPAL_SUBSCRIPTION="Abonnement" +ACF_PAYPAL_ACCOUNT="PayPal e-mailadresse" +ACF_PAYPAL_ACCOUNT_DESC="Sæt din PayPal E-mailadresse hvortil du vil modtage betalingerne. Denne e-mail skal tilhøre en forretnings PayPal konto." +ACF_PAYPAL_PRICE="Pris" +ACF_PAYPAL_PRICE_DESC="Angiv prisen for produktet eller servicen som du sælger." +ACF_PAYPAL_PRICE_HINT="Produktpris" +ACF_PAYPAL_ITEM_NAME="Elementnavn" +ACF_PAYPAL_ITEM_NAME_DESC="Sæt elementnavnet der skal vises på PayPal udtjeningssiden." +ACF_PAYPAL_ITEM_NAME_HINT="Produktnavn" +ACF_PAYPAL_CURRENCY="Valuta" +ACF_PAYPAL_CURRENCY_DESC="Vælg valutaen som dine brugere skal betale i for dit produkt eller servicen som du leverer." +ACF_PAYPAL_BILLING_INTERVAL="Faktureringsinterval" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Angiv varigheden af abonnementet i dage." +ACF_PAYPAL_LANGUAGE="Sprog" +ACF_PAYPAL_LANGUAGE_DESC="Angiv om standardsproget skal bibeholdes eller om et specifikt sprog skal anvendes på PayPal tjek ud siden." +ACF_PAYPAL_LANGUAGE_LOCALE="Sprog" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Sæt sproget der skal anvendes på PayPal tjek ud siden." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Understøttede lokaliseringskoderLocale Codes" +ACF_PAYPAL_FIXED="Fast" +ACF_PAYPAL_RETURN_URL="Returnerings URL" +ACF_PAYPAL_RETURN_URL_DESC="Angiv URL'en hvortil brugeren vil blive omdirigeret til efter en gennemført transaktion." +ACF_PAYPAL_CANCEL_URL="Annullerings URL" +ACF_PAYPAL_CANCEL_URL_DESC="Angiv URL'en som brugeren vil blive omdirigeret til efter annullering af transaktionen." +ACF_PAYPAL_BUTTON_TYPE="Knaptype" +ACF_PAYPAL_BUTTON_TYPE_DESC="Vælg knaptypen. Enten en stil fra vores præ-definerede lists med knapper, eller anvend et brugerdefineret billede som din PayPal knap." +ACF_PAYPAL_STYLE="Stil" +ACF_PAYPAL_STYLE_SELECTOR="Knapstil" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Vælg en knap fra vores prædefinerede liste." +ACF_PAYPAL_BUTTON_IMAGE="Knapbillede" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Angiv et billede som din PayPal knap." +ACF_PAYPAL_NEW_TAB="Åben PayPal i en ny fanen new tab" +ACF_PAYPAL_NEW_TAB_DESC="Aktiver at åbne PayPal i en ny fane." +ACF_PAYPAL_SANDBOX_MODE="Sandkassetilstandbox Mode" +ACF_PAYPAL_SANDBOX_MODE_DESC="Angiv om sandkassetilstand skal aktiveres. Anvendes til at fejlsøge PayPal knappen under udvililng." +ACF_PAYPAL_SANDBOX_ACCOUNT="Sandkasse e-mailadresse" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Angiv din sandkasse PayPal e-mailadresse. hvis efterladt tom, så vil PayPal e-mailadressen blive anvendt." diff --git a/plugins/fields/acfpaypal/language/de-DE/de-DE.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/de-DE/de-DE.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..b23cdc57 --- /dev/null +++ b/plugins/fields/acfpaypal/language/de-DE/de-DE.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Felder - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Legen Sie den Namen und den Preis des Produkts oder der Dienstleistung fest, die Sie verkaufen." +ACF_PAYPAL_PAYMENT_TYPE="Zahlungsart" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Wählen Sie eine Zahlungsart aus.
Checkout : Eine einmalige Zahlung.
Spende : Eine freiwillige Spende.
Abonnement : Eine wiederkehrende Zahlung. " +ACF_PAYPAL_CHECKOUT="Kasse" +ACF_PAYPAL_DONATION="Spende" +ACF_PAYPAL_SUBSCRIPTION="Abonnement" +ACF_PAYPAL_ACCOUNT="PayPal-E-Mail-Adresse" +ACF_PAYPAL_ACCOUNT_DESC="Legen Sie Ihre PayPal-E-Mail-Adresse fest, an die Sie das Geld erhalten. Diese E-Mail muss zu einem geschäftlichen PayPal-Konto gehören." +ACF_PAYPAL_PRICE="Preis" +ACF_PAYPAL_PRICE_DESC="Legen Sie den Preis für das Produkt oder die Dienstleistung fest, die Sie verkaufen." +ACF_PAYPAL_PRICE_HINT="Produktpreis" +ACF_PAYPAL_ITEM_NAME="Elementname" +ACF_PAYPAL_ITEM_NAME_DESC="Legen Sie den Artikelnamen fest, der auf der PayPal-Checkout-Seite angezeigt werden soll." +ACF_PAYPAL_ITEM_NAME_HINT="Produktname" +ACF_PAYPAL_CURRENCY="Währung" +ACF_PAYPAL_CURRENCY_DESC="Wählen Sie die Währung aus, die Ihre Benutzer für Ihre von Ihnen angebotenen Produkte oder Dienstleistungen bezahlen." +ACF_PAYPAL_BILLING_INTERVAL="Abrechnungsintervall" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Legen Sie die Dauer des Abonnements in Tagen fest." +ACF_PAYPAL_LANGUAGE="Sprache" +ACF_PAYPAL_LANGUAGE_DESC="Legen Sie fest, ob die Standardsprache beibehalten oder eine bestimmte Sprache auf der PayPal-Checkout-Seite angewendet werden soll." +ACF_PAYPAL_LANGUAGE_LOCALE="Sprachgebietsschema" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Legen Sie das Gebietsschema der Sprache fest, die auf der PayPal-Checkout-Seite verwendet werden soll." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Unterstützte Gebietsschema-Codes" +ACF_PAYPAL_FIXED="Behoben" +ACF_PAYPAL_RETURN_URL="Abschluss URL " +ACF_PAYPAL_RETURN_URL_DESC="Legen Sie die URL fest, unter der der Benutzer nach erfolgreichem Abschluss der Transaktion umgeleitet wird." +ACF_PAYPAL_CANCEL_URL="URL abbrechen" +ACF_PAYPAL_CANCEL_URL_DESC="Legen Sie die URL fest, unter der der Benutzer nach Abbruch der Transaktion umgeleitet wird." +ACF_PAYPAL_BUTTON_TYPE="Schaltflächentyp" +ACF_PAYPAL_BUTTON_TYPE_DESC="Wählen Sie den Schaltflächentyp aus, entweder einen Stil aus unserer vordefinierten Liste von Schaltflächen oder verwenden Sie ein benutzerdefiniertes Bild als PayPal-Schaltfläche." +ACF_PAYPAL_STYLE="Stil" +ACF_PAYPAL_STYLE_SELECTOR="Schaltflächenstil" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Wählen Sie eine Schaltfläche aus unserer vordefinierten Liste aus." +ACF_PAYPAL_BUTTON_IMAGE="Schaltflächenbild" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Legen Sie ein Bild als PayPal-Schaltfläche fest." +ACF_PAYPAL_NEW_TAB="PayPal in neuem Tab öffnen" +ACF_PAYPAL_NEW_TAB_DESC="Aktivieren Sie das Öffnen von PayPal in einem neuen Tab." +ACF_PAYPAL_SANDBOX_MODE="Sandbox-Modus" +ACF_PAYPAL_SANDBOX_MODE_DESC="Legen Sie fest, ob der Sandbox-Modus aktiviert werden soll, der zum Debuggen der PayPal-Schaltfläche während der Entwicklung verwendet wird." +ACF_PAYPAL_SANDBOX_ACCOUNT="Sandbox-E-Mail-Adresse" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Geben Sie Ihre Sandbox-PayPal-E-Mail-Adresse ein. Wenn diese Option leer bleibt, wird die PayPal-E-Mail-Adresse verwendet." diff --git a/plugins/fields/acfpaypal/language/en-GB/en-GB.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/en-GB/en-GB.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..b5149594 --- /dev/null +++ b/plugins/fields/acfpaypal/language/en-GB/en-GB.plg_fields_acfpaypal.ini @@ -0,0 +1,52 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Fields - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Set the name and the price of the product or the service you are selling." +ACF_PAYPAL_PAYMENT_TYPE="Payment Type" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Select a payment type.
Checkout: A one time payment.
Donation: A voluntary donation.
Subscription: A recurring payment." +ACF_PAYPAL_CHECKOUT="Checkout" +ACF_PAYPAL_DONATION="Donation" +ACF_PAYPAL_SUBSCRIPTION="Subscription" +ACF_PAYPAL_ACCOUNT="PayPal Email Address" +ACF_PAYPAL_ACCOUNT_DESC="Set your PayPal Email Address where you will receive the funds. This email needs to belong to a business PayPal account." +ACF_PAYPAL_PRICE="Price" +ACF_PAYPAL_PRICE_DESC="Set the price for the product or the service you are selling." +ACF_PAYPAL_PRICE_HINT="Product Price" +ACF_PAYPAL_ITEM_NAME="Item Name" +ACF_PAYPAL_ITEM_NAME_DESC="Set the item name to display on the PayPal checkout page." +ACF_PAYPAL_ITEM_NAME_HINT="Product Name" +ACF_PAYPAL_CURRENCY="Currency" +ACF_PAYPAL_CURRENCY_DESC="Select the currency which your users will pay for your products or services you are providing." +ACF_PAYPAL_BILLING_INTERVAL="Billing Interval" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Set the duration of the subscription in days." +ACF_PAYPAL_LANGUAGE="Language" +ACF_PAYPAL_LANGUAGE_DESC="Set whether to keep the default language or apply a specific language on the PayPal checkout page." +ACF_PAYPAL_LANGUAGE_LOCALE="Language Locale" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Set the locale of the language to be used in the PayPal checkout page." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Supported Locale Codes" +ACF_PAYPAL_FIXED="Fixed" +ACF_PAYPAL_RETURN_URL="Return URL" +ACF_PAYPAL_RETURN_URL_DESC="Set the URL where the user will be redirected after successfully completing the transaction." +ACF_PAYPAL_CANCEL_URL="Cancel URL" +ACF_PAYPAL_CANCEL_URL_DESC="Set the URL where the user will be redirected after cancelling the transaction." +ACF_PAYPAL_BUTTON_TYPE="Button Type" +ACF_PAYPAL_BUTTON_TYPE_DESC="Select the button type, either a style from our pre-defined list of buttons or use a custom image as your PayPal button." +ACF_PAYPAL_STYLE="Style" +ACF_PAYPAL_STYLE_SELECTOR="Button Style" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Select a button from our pre-defined list." +ACF_PAYPAL_BUTTON_IMAGE="Button Image" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Set an image as your PayPal button." +ACF_PAYPAL_NEW_TAB="Open PayPal in new tab" +ACF_PAYPAL_NEW_TAB_DESC="Enable to open PayPal in a new tab." +ACF_PAYPAL_SANDBOX_MODE="Sandbox Mode" +ACF_PAYPAL_SANDBOX_MODE_DESC="Set whether to enable sandbox mode, used for debugging the PayPal button during development." +ACF_PAYPAL_SANDBOX_ACCOUNT="Sandbox Email Address" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Enter your sandbox PayPal Email Address. If left empty, the PayPal Email Address will be used." +ACF_PAYPAL_ALLOW_EMPTY_VALUE="Allow User to set price" +ACF_PAYPAL_ALLOW_EMPTY_VALUE_DESC="By enabling this option, you can leave the item price empty and let the user set a custom price on PayPal, during checkout." \ No newline at end of file diff --git a/plugins/fields/acfpaypal/language/en-GB/en-GB.plg_fields_acfpaypal.sys.ini b/plugins/fields/acfpaypal/language/en-GB/en-GB.plg_fields_acfpaypal.sys.ini new file mode 100644 index 00000000..d58b6ca8 --- /dev/null +++ b/plugins/fields/acfpaypal/language/en-GB/en-GB.plg_fields_acfpaypal.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_PAYPAL="Fields - ACF PayPal" +ACF_PAYPAL_DESC="Collect donations from around the world, allow your shoppers to buy products or subscribe to your services with a PayPal button." \ No newline at end of file diff --git a/plugins/fields/acfpaypal/language/es-ES/es-ES.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/es-ES/es-ES.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..89b0bdaf --- /dev/null +++ b/plugins/fields/acfpaypal/language/es-ES/es-ES.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Campos - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Establezca el nombre y el precio del producto o servicio que está vendiendo." +ACF_PAYPAL_PAYMENT_TYPE="Tipo de pago" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Seleccione un tipo de pago.
Verificado: Un pago único.
Donación: Una donación voluntaria.
Suscripción: Un pago recurrente." +ACF_PAYPAL_CHECKOUT="Verificado" +ACF_PAYPAL_DONATION="Donación" +ACF_PAYPAL_SUBSCRIPTION="Suscripción" +ACF_PAYPAL_ACCOUNT="Dirección Email de Paypal" +ACF_PAYPAL_ACCOUNT_DESC="Establezca su dirección de correo electrónico de PayPal donde recibirá los fondos. Este correo electrónico debe pertenecer a una cuenta comercial de PayPal." +ACF_PAYPAL_PRICE="Precio" +ACF_PAYPAL_PRICE_DESC="Establezca el precio del producto o servicio que está vendiendo." +ACF_PAYPAL_PRICE_HINT="Precio del Producto" +ACF_PAYPAL_ITEM_NAME="Nombre del árticulo" +ACF_PAYPAL_ITEM_NAME_DESC="Configure el nombre del artículo para que se muestre en la página de pago de PayPal." +ACF_PAYPAL_ITEM_NAME_HINT="Nombre del Producto" +ACF_PAYPAL_CURRENCY="Moneda" +ACF_PAYPAL_CURRENCY_DESC="Seleccione la moneda en la que sus usuarios pagarán por los productos o servicios que ofrece." +ACF_PAYPAL_BILLING_INTERVAL="Intervalo de Facturación" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Establece la duración de la suscripción en días." +ACF_PAYPAL_LANGUAGE="Idioma" +ACF_PAYPAL_LANGUAGE_DESC="Establezca si desea mantener el idioma predeterminado o aplicar un idioma específico en la página de pago de PayPal." +ACF_PAYPAL_LANGUAGE_LOCALE="Idioma Local" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Establezca la configuración regional del idioma que se utilizará en la página de pago de PayPal." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Códigos de configuración regional admitidos" +ACF_PAYPAL_FIXED="Fijo" +ACF_PAYPAL_RETURN_URL="URL de retorno" +ACF_PAYPAL_RETURN_URL_DESC="Establezca la URL donde se redirigirá al usuario después de completar con éxito la transacción." +ACF_PAYPAL_CANCEL_URL="Cancelar URL" +ACF_PAYPAL_CANCEL_URL_DESC="Establezca la URL donde se redirigirá al usuario después de cancelar la transacción." +ACF_PAYPAL_BUTTON_TYPE="Tipo de Botón" +ACF_PAYPAL_BUTTON_TYPE_DESC="Seleccione el tipo de botón, ya sea un estilo de nuestra lista predefinida de botones o use una imagen personalizada como su botón de PayPal." +ACF_PAYPAL_STYLE="Estilo" +ACF_PAYPAL_STYLE_SELECTOR="Estilo del Botón" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Seleccione un botón de nuestra lista predefinida." +ACF_PAYPAL_BUTTON_IMAGE="Imagen del Botón" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Establezca una imagen como su botón de PayPal." +ACF_PAYPAL_NEW_TAB="Abrir PayPal en una pestaña nueva" +ACF_PAYPAL_NEW_TAB_DESC="Habilite para abrir PayPal en una nueva pestaña." +ACF_PAYPAL_SANDBOX_MODE="Modo Pruebas (Sandbox)" +ACF_PAYPAL_SANDBOX_MODE_DESC="Establezca si desea habilitar el modo sandbox, que se usa para depurar el botón de PayPal durante el desarrollo." +ACF_PAYPAL_SANDBOX_ACCOUNT="Dirección de correo electrónico de la zona de pruebas" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Ingrese su dirección de correo electrónico de PayPal de sandbox. Si se deja en blanco, se utilizará la dirección de correo electrónico de PayPal." diff --git a/plugins/fields/acfpaypal/language/es-ES/es-ES.plg_fields_acfpaypal.sys.ini b/plugins/fields/acfpaypal/language/es-ES/es-ES.plg_fields_acfpaypal.sys.ini new file mode 100644 index 00000000..136c5a0a --- /dev/null +++ b/plugins/fields/acfpaypal/language/es-ES/es-ES.plg_fields_acfpaypal.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_PAYPAL="Campos - ACF PayPal" +ACF_PAYPAL_DESC="Recopile donaciones de todo el mundo, permita que sus compradores compren productos o se suscriban a sus servicios con un botón de PayPal." diff --git a/plugins/fields/acfpaypal/language/fr-FR/fr-FR.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/fr-FR/fr-FR.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..1839149c --- /dev/null +++ b/plugins/fields/acfpaypal/language/fr-FR/fr-FR.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Champs - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Définissez le nom et le prix du produit ou du service que vous vendez" +ACF_PAYPAL_PAYMENT_TYPE="Type de paiement" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Sélectionnez un type de paiement.
Achat: un paiement unique.
Don: un don volontaire.
Souscription: un paiement périodique." +ACF_PAYPAL_CHECKOUT="Achat" +ACF_PAYPAL_DONATION="Don" +ACF_PAYPAL_SUBSCRIPTION="Souscription" +ACF_PAYPAL_ACCOUNT="Adresse électronique PayPal" +ACF_PAYPAL_ACCOUNT_DESC="Saisissez l'adresse e-mail PayPal sur laquelle vous recevez les fonds. Cela nécessite un compte PayPal Business" +ACF_PAYPAL_PRICE="Prix" +ACF_PAYPAL_PRICE_DESC="Saisissez le prix du produit ou du service que vous vendez" +ACF_PAYPAL_PRICE_HINT="Prix du produit" +ACF_PAYPAL_ITEM_NAME="Nom de l'élément" +ACF_PAYPAL_ITEM_NAME_DESC="Notez le nom de l'élément qui sera affiché sur la page de paiement PayPal" +ACF_PAYPAL_ITEM_NAME_HINT="Nom du produit" +ACF_PAYPAL_CURRENCY="Devise" +ACF_PAYPAL_CURRENCY_DESC="Saisissez la devise dans laquelle vos acheteurs paieront les produits ou services que vous proposez" +ACF_PAYPAL_BILLING_INTERVAL="Intervalle de paiement" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Saisissez la durée de souscription en jours" +ACF_PAYPAL_LANGUAGE="Langue" +ACF_PAYPAL_LANGUAGE_DESC="Définissez si vous préférez utiliser la langue par défaut ou une langue personnalisée sur la page de paiement PayPal" +ACF_PAYPAL_LANGUAGE_LOCALE="Langue locale" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Définissez la langue locale à utiliser sur la page de paiement PayPal" +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Codes de langue locale supportés" +ACF_PAYPAL_FIXED="Définie" +ACF_PAYPAL_RETURN_URL="URL de retour" +ACF_PAYPAL_RETURN_URL_DESC="Saisissez l'URL vers laquelle rediriger l'acheteur après validation de la transaction" +ACF_PAYPAL_CANCEL_URL="URL d'annulation" +ACF_PAYPAL_CANCEL_URL_DESC="Saisissez l'adresse où rediriger l'acheteur en cas d'annulation de la transaction" +ACF_PAYPAL_BUTTON_TYPE="Type de bouton" +ACF_PAYPAL_BUTTON_TYPE_DESC="Sélectionnez le type de bouton, soit un des styles prédéfinis de la liste, soit une image personnalisée pour votre bouton PayPal" +ACF_PAYPAL_STYLE="Style" +ACF_PAYPAL_STYLE_SELECTOR="Style de bouton" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Sélectionnez un bouton dans cete liste" +ACF_PAYPAL_BUTTON_IMAGE="Image du bouton" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Définissez une image personnalisée comme bouton PayPal" +ACF_PAYPAL_NEW_TAB="Ouvrir PayPal dans un nouvel onglet" +ACF_PAYPAL_NEW_TAB_DESC="Permet d'ouvrir la page de paiement PayPal dans un nouvel onglet" +ACF_PAYPAL_SANDBOX_MODE="Mode Sandbox" +ACF_PAYPAL_SANDBOX_MODE_DESC="Choisissez d'activer le mode test Sandbox afin de déboguer l'usage du bouton PayPal pendant la phase de développement" +ACF_PAYPAL_SANDBOX_ACCOUNT="Adresse e-mail pour Sandbox" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Saisissez votre adresse e-mail Sandbox. Si ce champ est laissé vide, l'adresse standard PayPal sera utilisée" diff --git a/plugins/fields/acfpaypal/language/it-IT/it-IT.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/it-IT/it-IT.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..815dd6fb --- /dev/null +++ b/plugins/fields/acfpaypal/language/it-IT/it-IT.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Campi - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Imposta il nome e il prezzo del prodotto che stai vendendo." +ACF_PAYPAL_PAYMENT_TYPE="Tipo di Pagamento" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Seleziona un tipo di pagamento.
Checkout: Un pagamento unico.
Donazione: Una donazione volontaria.
Iscrizione: Un pagamento periodico." +ACF_PAYPAL_CHECKOUT="Checkout" +ACF_PAYPAL_DONATION="Donazione" +ACF_PAYPAL_SUBSCRIPTION="Iscrizione" +ACF_PAYPAL_ACCOUNT="Indirizzo Email PayPal" +ACF_PAYPAL_ACCOUNT_DESC="Imposta l'indirizzo email di PayPal dove riceverai il denaro. Questa email deve appartenere a un account PayPal business." +ACF_PAYPAL_PRICE="Prezzo" +ACF_PAYPAL_PRICE_DESC="Imposta il prezzo per il prodotto o il servizio che stai vendendo." +ACF_PAYPAL_PRICE_HINT="Prezzo Prodotto" +ACF_PAYPAL_ITEM_NAME="Nome dell'articolo" +ACF_PAYPAL_ITEM_NAME_DESC="Imposta il nome dell'articolo da mostrare sulla pagine di checkout di PayPal." +ACF_PAYPAL_ITEM_NAME_HINT="Nome Prodotto" +ACF_PAYPAL_CURRENCY="Moneta" +ACF_PAYPAL_CURRENCY_DESC="Seleziona la valuta con cui i tuoi utenti pagheranno per i prodotti o servizi che stai offrendo." +ACF_PAYPAL_BILLING_INTERVAL="Intervallo di fatturazione" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Imposta la durata dell'iscrizione in giorni." +ACF_PAYPAL_LANGUAGE="Lingua" +ACF_PAYPAL_LANGUAGE_DESC="Imposta se mantenere la lingua predefinita o applicare una lingua specifica nella pagina di pagamento di PayPal." +ACF_PAYPAL_LANGUAGE_LOCALE="Locale della lingua" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Imposta il locale della lingua da usare nella pagina di checkout di PayPal." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Codice di locale supportati" +ACF_PAYPAL_FIXED="Fissato" +ACF_PAYPAL_RETURN_URL="URL di restituzione" +ACF_PAYPAL_RETURN_URL_DESC="Imposta l'URL a cui verrà reindirizzato l'utente dopo aver completato con successo la transazione." +ACF_PAYPAL_CANCEL_URL="URL di cancellazione" +ACF_PAYPAL_CANCEL_URL_DESC="Imposta l'URL a cui verrà reindirizzato l'utente dopo aver annullato la transazione." +ACF_PAYPAL_BUTTON_TYPE="Tipo di pulsante" +ACF_PAYPAL_BUTTON_TYPE_DESC="Seleziona il tipo di pulsante: utilizza per il pulsante PayPal uno stile dal nostro elenco predefinito di pulsanti o un'immagine personalizzata." +ACF_PAYPAL_STYLE="Stile" +ACF_PAYPAL_STYLE_SELECTOR="Stile del pulsante" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Seleziona un pulsante dal nostro elenco predefinito." +ACF_PAYPAL_BUTTON_IMAGE="Immagine del pulsante" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Imposta un'immagine come pulsante PayPal." +ACF_PAYPAL_NEW_TAB="Apri PayPal in una nuova scheda" +ACF_PAYPAL_NEW_TAB_DESC="Abilita per aprire PayPal in una nuova scheda." +ACF_PAYPAL_SANDBOX_MODE="Modalità sandbox" +ACF_PAYPAL_SANDBOX_MODE_DESC="Imposta se abilitare la modalità sandbox, utilizzata per il debug del pulsante PayPal durante lo sviluppo." +ACF_PAYPAL_SANDBOX_ACCOUNT="Indirizzo e-mail sandbox" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Inserisci il tuo indirizzo e-mail della sandbox di PayPal. Se lasciato vuoto, verrà utilizzato l'indirizzo e-mail PayPal." diff --git a/plugins/fields/acfpaypal/language/nl-NL/nl-NL.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/nl-NL/nl-NL.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..0e136768 --- /dev/null +++ b/plugins/fields/acfpaypal/language/nl-NL/nl-NL.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Velden - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Stel naam en prijs in van het product of dienst dat je verkoopt." +ACF_PAYPAL_PAYMENT_TYPE="Betalingswijze" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Selecteer een betalingswijze.
Afrekenen: Een eenmalige betaling.
Donatie: Een vrijwillige donatie.
Abonnement: Een terugkerende betaling." +ACF_PAYPAL_CHECKOUT="Afrekenen" +ACF_PAYPAL_DONATION="Donatie" +ACF_PAYPAL_SUBSCRIPTION="Abonnement" +ACF_PAYPAL_ACCOUNT="Paypal E-mailadres" +ACF_PAYPAL_ACCOUNT_DESC="Stel het PayPal-e-mailadres in waar het geld op zal worden ontvangen. Dit e-mailadres moet bij een zakelijke PayPal-rekening horen." +ACF_PAYPAL_PRICE="Prijs" +ACF_PAYPAL_PRICE_DESC="Stel de prijs in voor het product of de dienst dat je verkoopt." +ACF_PAYPAL_PRICE_HINT="Product Prijs" +ACF_PAYPAL_ITEM_NAME="Item Naam" +ACF_PAYPAL_ITEM_NAME_DESC="Stel de naam van het item in dat op de PayPal-afrekenpagina zal worden weergegeven." +ACF_PAYPAL_ITEM_NAME_HINT="ProductNaam" +ACF_PAYPAL_CURRENCY="Muntsoort" +ACF_PAYPAL_CURRENCY_DESC="Selecteer de muntsoort waarmee jouw klanten zullen gaan betalen voor de producten of services die je levert." +ACF_PAYPAL_BILLING_INTERVAL="Factureringsinterval" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Stel in dagen de duur van het abonnement in." +ACF_PAYPAL_LANGUAGE="Taal" +ACF_PAYPAL_LANGUAGE_DESC="Stel in of je de standaardtaal wil houden of een specifieke taal wilt gebruiken op de PayPal-afrekenpagina." +ACF_PAYPAL_LANGUAGE_LOCALE="Taalinstelling" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Stel de taal in van de taal die moet worden gebruikt op de PayPal-betaalpagina." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Ondersteunde landcodes" +ACF_PAYPAL_FIXED="Vastgezet" +ACF_PAYPAL_RETURN_URL="Terugkerende URL" +ACF_PAYPAL_RETURN_URL_DESC="Stel de URL in waarnaar de klant wordt omgeleid nadat de transactie is voltooid." +ACF_PAYPAL_CANCEL_URL="URL bij Annulering" +ACF_PAYPAL_CANCEL_URL_DESC="Stel de URL in waarnaar de klant wordt omgeleid nadat de transactie is geannuleerd." +ACF_PAYPAL_BUTTON_TYPE="Knoptype" +ACF_PAYPAL_BUTTON_TYPE_DESC="Selecteer een knoptype. Gebruik een stijl uit onze vooraf gedefinieerde lijst met knoppen of gebruik een andere afbeelding als PayPal-knop." +ACF_PAYPAL_STYLE="Stijl" +ACF_PAYPAL_STYLE_SELECTOR="Knopstijl" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Selecteer een knop uit onze vooraf gedefinieerde lijst." +ACF_PAYPAL_BUTTON_IMAGE="Knop Afbeelding" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Stel een afbeelding in als PayPal knop." +ACF_PAYPAL_NEW_TAB="Laat Paypal in een nieuw tabblad" +ACF_PAYPAL_NEW_TAB_DESC="Schakel in om PayPal in een nieuw tabblad te openen." +ACF_PAYPAL_SANDBOX_MODE="Sandbox-modus" +ACF_PAYPAL_SANDBOX_MODE_DESC="Zet de sandbox-modus aan of uit. Voor het opsporen van eventuele fouten tijdens het instellen van de PayPal-knop." +ACF_PAYPAL_SANDBOX_ACCOUNT="E-mailadres sandbox" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Voer uw sandbox PayPal-e-mailadres in. Indien leeg gelaten, wordt het PayPal-e-mailadres gebruikt." diff --git a/plugins/fields/acfpaypal/language/ru-RU/ru-RU.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/ru-RU/ru-RU.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..559e9644 --- /dev/null +++ b/plugins/fields/acfpaypal/language/ru-RU/ru-RU.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Поля - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Установите название и цену продукта или услуги, которую вы продаете." +ACF_PAYPAL_PAYMENT_TYPE="Тип оплаты" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Выберите тип платежа.
Оформить заказ : одноразовый платеж.
Пожертвование : добровольное пожертвование.
Подписка : регулярный платеж. " +ACF_PAYPAL_CHECKOUT="Оформить заказ" +ACF_PAYPAL_DONATION="Пожертвование" +ACF_PAYPAL_SUBSCRIPTION="Подписка" +ACF_PAYPAL_ACCOUNT="Адрес электронной почты PayPal" +ACF_PAYPAL_ACCOUNT_DESC="Установите свой адрес электронной почты PayPal, на который вы будете получать средства. Это письмо должно принадлежать бизнес-счету PayPal." +ACF_PAYPAL_PRICE="Цена" +ACF_PAYPAL_PRICE_DESC="Установите цену на продукт или услугу, которую вы продаете." +ACF_PAYPAL_PRICE_HINT="Цена продукта" +ACF_PAYPAL_ITEM_NAME="Имя элемента" +ACF_PAYPAL_ITEM_NAME_DESC="Установить имя элемента для отображения на странице оформления платежей PayPal." +ACF_PAYPAL_ITEM_NAME_HINT="Название продукта" +ACF_PAYPAL_CURRENCY="Валюта" +ACF_PAYPAL_CURRENCY_DESC="Выберите валюту, которую ваши пользователи будут платить за ваши продукты или услуги, которые вы предоставляете." +ACF_PAYPAL_BILLING_INTERVAL="Платежный интервал" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Установить продолжительность подписки в днях." +ACF_PAYPAL_LANGUAGE="Язык" +ACF_PAYPAL_LANGUAGE_DESC="Установить, сохранять ли язык по умолчанию или применять определенный язык на странице оплаты PayPal." +ACF_PAYPAL_LANGUAGE_LOCALE="Языковой стандарт" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Установите языковой стандарт для использования на странице оформления платежей PayPal." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Поддерживаемые коды локалей" +ACF_PAYPAL_FIXED="Фиксированный" +ACF_PAYPAL_RETURN_URL="Обратный URL" +ACF_PAYPAL_RETURN_URL_DESC="Установить URL, куда пользователь будет перенаправлен после успешного завершения транзакции." +ACF_PAYPAL_CANCEL_URL="Отменить URL" +ACF_PAYPAL_CANCEL_URL_DESC="Установить URL, куда пользователь будет перенаправлен после отмены транзакции." +ACF_PAYPAL_BUTTON_TYPE="Тип кнопки" +ACF_PAYPAL_BUTTON_TYPE_DESC="Выберите тип кнопки, либо стиль из нашего предварительно определенного списка кнопок, либо используйте пользовательское изображение в качестве кнопки PayPal." +ACF_PAYPAL_STYLE="Стиль" +ACF_PAYPAL_STYLE_SELECTOR="Стиль кнопки" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Выберите кнопку из нашего предварительно определенного списка." +ACF_PAYPAL_BUTTON_IMAGE="Изображение кнопки" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Установить изображение в качестве кнопки PayPal." +ACF_PAYPAL_NEW_TAB="Открыть PayPal в новой вкладке" +ACF_PAYPAL_NEW_TAB_DESC="Включить, чтобы открыть PayPal в новой вкладке." +ACF_PAYPAL_SANDBOX_MODE="Режим песочницы" +ACF_PAYPAL_SANDBOX_MODE_DESC="Установить, включить ли режим песочницы, используемый для отладки кнопки PayPal во время разработки." +ACF_PAYPAL_SANDBOX_ACCOUNT="Адрес электронной почты песочницы" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Введите свой адрес электронной почты PayPal для песочницы. Если оставить его пустым, будет использован адрес электронной почты PayPal." diff --git a/plugins/fields/acfpaypal/language/sv-SE/sv-SE.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/sv-SE/sv-SE.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..9334e84a --- /dev/null +++ b/plugins/fields/acfpaypal/language/sv-SE/sv-SE.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Fält - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Ange namn och pris på produkten eller tjänsten du säljer." +ACF_PAYPAL_PAYMENT_TYPE="Betalningstyp" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Välj betalningstyp.
Checka ut: Engångsbetalning.
Donation: En frivillig donation.
Prenumeration: En återkommande betalning." +ACF_PAYPAL_CHECKOUT="Checka ut" +ACF_PAYPAL_DONATION="Donation" +ACF_PAYPAL_SUBSCRIPTION="Prenumeration" +ACF_PAYPAL_ACCOUNT="PayPal E-postadress" +ACF_PAYPAL_ACCOUNT_DESC="Ange din PayPal e-postadress där du får pengarna. Det här e-postmeddelandet måste tillhöra ett PayPal företagskonto." +ACF_PAYPAL_PRICE="Pris" +ACF_PAYPAL_PRICE_DESC="Ange pris på produkten eller tjänsten du säljer." +ACF_PAYPAL_PRICE_HINT="Produktens pris" +ACF_PAYPAL_ITEM_NAME="Artikelnamn" +ACF_PAYPAL_ITEM_NAME_DESC="Ange artikelnamnet som ska visas på PayPal kassasida." +ACF_PAYPAL_ITEM_NAME_HINT="Produktnamn" +ACF_PAYPAL_CURRENCY="Valuta" +ACF_PAYPAL_CURRENCY_DESC="Välj i vilken valuta användarna ska betala för dina produkter eller tjänster som du tillhandahåller." +ACF_PAYPAL_BILLING_INTERVAL="Betalningsintervall" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Ställ in prenumerationens varaktighet i dagar." +ACF_PAYPAL_LANGUAGE="Språk" +ACF_PAYPAL_LANGUAGE_DESC="Ange om du vill behålla standardspråket eller tillämpa ett visst språk på PayPal-kassans sida." +ACF_PAYPAL_LANGUAGE_LOCALE="Språk" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Ange språk som ska användas på PayPal kassasida." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Språkkoder som stöds" +ACF_PAYPAL_FIXED="Fast" +ACF_PAYPAL_RETURN_URL="Retur URL" +ACF_PAYPAL_RETURN_URL_DESC="Ange URL dit användaren kommer att omdirigeras efter att transaktionen har slutförts." +ACF_PAYPAL_CANCEL_URL="Avbryt URL" +ACF_PAYPAL_CANCEL_URL_DESC="Ange URL dit användaren kommer att omdirigeras efter att transaktionen har avbrutits." +ACF_PAYPAL_BUTTON_TYPE="Knapp typ" +ACF_PAYPAL_BUTTON_TYPE_DESC="Välj knapptyp, antingen en stil från vår fördefinierade lista med knappar eller använd en anpassad bild som din PayPal-knapp." +ACF_PAYPAL_STYLE="Stil" +ACF_PAYPAL_STYLE_SELECTOR="Knapp stil" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Välj en knapp från vår fördefinierade lista." +ACF_PAYPAL_BUTTON_IMAGE="Knapp bild" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Ange en bild som din PayPal-knapp." +ACF_PAYPAL_NEW_TAB="Öppna PayPal i en ny flik." +ACF_PAYPAL_NEW_TAB_DESC="Aktivera för att öppna PayPal i en ny flik." +ACF_PAYPAL_SANDBOX_MODE="Testläge" +ACF_PAYPAL_SANDBOX_MODE_DESC="Ange om du vill aktivera testläge, som används för att felsöka PayPal-knappen under utveckling." +ACF_PAYPAL_SANDBOX_ACCOUNT="Test e-postadress" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Ange din test e-postadress PayPal. Om fältet lämnas tomt kommer PayPal e-postadressen att användas." diff --git a/plugins/fields/acfpaypal/language/uk-UA/uk-UA.plg_fields_acfpaypal.ini b/plugins/fields/acfpaypal/language/uk-UA/uk-UA.plg_fields_acfpaypal.ini new file mode 100644 index 00000000..a6d10919 --- /dev/null +++ b/plugins/fields/acfpaypal/language/uk-UA/uk-UA.plg_fields_acfpaypal.ini @@ -0,0 +1,50 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPAYPAL_LABEL="ACF - PayPal" +ACF_PAYPAL="Поля - ACF PayPal" +ACF_PAYPAL_VALUE_DESC="Встановіть назву та ціну товару чи послуги, яку ви продаєте." +ACF_PAYPAL_PAYMENT_TYPE="Тип оплати" +ACF_PAYPAL_PAYMENT_TYPE_DESC="Вибір способу оплати. strong>: Повторний платіж. " +ACF_PAYPAL_CHECKOUT="Оформити замовлення" +ACF_PAYPAL_DONATION="Пожертвування" +ACF_PAYPAL_SUBSCRIPTION="Підписка" +ACF_PAYPAL_ACCOUNT="Адреса електронної пошти PayPal" +ACF_PAYPAL_ACCOUNT_DESC="Встановіть свою електронну адресу PayPal, де ви отримаєте кошти. Цей електронний лист повинен належати до ділового облікового запису PayPal." +ACF_PAYPAL_PRICE="Ціна" +ACF_PAYPAL_PRICE_DESC="Встановити ціну на товар або послугу, яку ви продаєте." +ACF_PAYPAL_PRICE_HINT="Ціна продукту" +ACF_PAYPAL_ITEM_NAME="Назва елемента" +ACF_PAYPAL_ITEM_NAME_DESC="Встановити ім'я товару для відображення на сторінці оформлення замовлення PayPal." +ACF_PAYPAL_ITEM_NAME_HINT="Назва продукту" +ACF_PAYPAL_CURRENCY="Валюта" +ACF_PAYPAL_CURRENCY_DESC="Виберіть валюту, яку платитимуть ваші користувачі за ваші продукти чи послуги, які ви надаєте." +ACF_PAYPAL_BILLING_INTERVAL="Інтервал виставлення рахунків" +ACF_PAYPAL_BILLING_INTERVAL_DESC="Встановити тривалість передплати в днях." +ACF_PAYPAL_LANGUAGE="Мова" +ACF_PAYPAL_LANGUAGE_DESC="Встановіть, чи потрібно зберігати мову за замовчуванням чи застосовувати певну мову на сторінці оформлення замовлення PayPal." +ACF_PAYPAL_LANGUAGE_LOCALE="Мова" +ACF_PAYPAL_LANGUAGE_LOCALE_DESC="Встановіть мову мови, яка буде використовуватися на сторінці оформлення замовлення PayPal." +ACF_PAYPAL_SUPPORTED_LOCALE_CODES="Підтримувані локальні коди" +ACF_PAYPAL_FIXED="Виправлено" +ACF_PAYPAL_RETURN_URL="URL-адреса повернення" +ACF_PAYPAL_RETURN_URL_DESC="Встановіть URL, куди буде переспрямований користувач після успішного завершення транзакції." +ACF_PAYPAL_CANCEL_URL="Скасувати URL" +ACF_PAYPAL_CANCEL_URL_DESC="Встановіть URL, куди буде переспрямований користувач після скасування транзакції." +ACF_PAYPAL_BUTTON_TYPE="Тип кнопки" +ACF_PAYPAL_BUTTON_TYPE_DESC="Виберіть тип кнопки, або стиль із нашого попередньо визначеного списку кнопок, або використовуйте власне зображення як вашу кнопку PayPal." +ACF_PAYPAL_STYLE="Стиль" +ACF_PAYPAL_STYLE_SELECTOR="Стиль кнопки" +ACF_PAYPAL_STYLE_SELECTOR_DESC="Виберіть кнопку з нашого попередньо визначеного списку." +ACF_PAYPAL_BUTTON_IMAGE="Зображення кнопки" +ACF_PAYPAL_BUTTON_IMAGE_DESC="Встановити зображення як вашу кнопку PayPal." +ACF_PAYPAL_NEW_TAB="Відкрити PayPal у новій вкладці" +ACF_PAYPAL_NEW_TAB_DESC="Увімкнути, щоб відкрити PayPal на новій вкладці." +ACF_PAYPAL_SANDBOX_MODE="Режим пісочниці" +ACF_PAYPAL_SANDBOX_MODE_DESC="Встановити, чи потрібно активувати режим пісочниці, що використовується для налагодження кнопки PayPal під час розробки." +ACF_PAYPAL_SANDBOX_ACCOUNT="Електронна адреса пісочниці" +ACF_PAYPAL_SANDBOX_ACCOUNT_DESC="Введіть електронну адресу PayPal у вашій пісочниці. Якщо залишити порожнім, буде використана електронна адреса PayPal." diff --git a/plugins/fields/acfpaypal/params/acfpaypal.xml b/plugins/fields/acfpaypal/params/acfpaypal.xml new file mode 100644 index 00000000..94989aa3 --- /dev/null +++ b/plugins/fields/acfpaypal/params/acfpaypal.xml @@ -0,0 +1,106 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfpaypal/script.install.helper.php b/plugins/fields/acfpaypal/script.install.helper.php new file mode 100644 index 00000000..9acb8647 --- /dev/null +++ b/plugins/fields/acfpaypal/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfpaypalInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfpaypal/script.install.php b/plugins/fields/acfpaypal/script.install.php new file mode 100644 index 00000000..fd3f887b --- /dev/null +++ b/plugins/fields/acfpaypal/script.install.php @@ -0,0 +1,43 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\Filesystem\File; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFPayPalInstallerScript extends PlgFieldsACFPayPalInstallerScriptHelper +{ + public $alias = 'acfpaypal'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + // If version.php doesn't exist, copy it from the system plugin + if ($this->isInstalled() && !file_exists($this->getMainFolder() . '/version.php')) + { + $systemVersionPath = JPATH_SITE . '/plugins/system/acf/version.php'; + + $result = File::copy($systemVersionPath, $this->getMainFolder() . '/version.php'); + } + + return parent::onBeforeInstall(); + } +} diff --git a/plugins/fields/acfpaypal/tmpl/acfpaypal.php b/plugins/fields/acfpaypal/tmpl/acfpaypal.php new file mode 100644 index 00000000..94cffed7 --- /dev/null +++ b/plugins/fields/acfpaypal/tmpl/acfpaypal.php @@ -0,0 +1,119 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Uri\Uri; + +// Setup variables +if (!$field->value) +{ + return; +} + +$type = $fieldParams->get('type', 'checkout'); +$paypal_account = $fieldParams->get('paypal_account', ''); +$sandbox_mode = (bool) $fieldParams->get('sandbox_mode', ''); +$sandbox_account = $fieldParams->get('sandbox_account', ''); +$paypal_account = (!$sandbox_mode) ? $paypal_account : $sandbox_account; + +// get value data +$data = isset($field->value) && is_string($field->value) ? json_decode($field->value) : (object) $field->value; + +// Setup variables +$item_name = (isset($data->item_name) && !empty($data->item_name)) ? $data->item_name : ''; +$price = (isset($data->price) && !empty($data->price)) ? $data->price : ''; + +// Do not render if name and price are empty except if we are rendering a checkout or donation button, +// which do not require a fixed amount +if ((empty($item_name) || empty($price)) && !in_array($type, ['checkout', 'donation'])) +{ + return; +} + +/** + * Checkout button types can be rendered with no price set (users can then define a custom price on PayPal). + * + * However, some users do not want their checkout button to appear when no price is set. + */ +if ($type === 'checkout' && empty($price) && ! (bool) $fieldParams->get('allow_empty_value')) +{ + return; +} + +// Setup variables +$currency = $fieldParams->get('currency', 'USD'); +$billing_interval = $fieldParams->get('billing_interval', ''); +$language = $fieldParams->get('language', 'auto'); +$language_locale = $fieldParams->get('language_locale', ''); +$return_url = $fieldParams->get('return_url', ''); +$cancel_url = $fieldParams->get('cancel_url', ''); +$button_style = $fieldParams->get('button_style', 'style'); +$button_style_selector = $fieldParams->get('button_style_selector', ''); +$button_style_image = $fieldParams->get('button_style_image', ''); +$new_tab = (bool) $fieldParams->get('new_tab', ''); +$new_tab = ($new_tab) ? ' target="_blank"' : ''; + +// base url +$base_url = (!$sandbox_mode) ? 'https://www.paypal.com/cgi-bin/webscr' : 'https://www.sandbox.paypal.com/cgi-bin/webscr'; + +$image_name = ($button_style == 'style') ? $button_style_selector : $button_style_image; +$image_url = ($button_style == 'style') ? Uri::root() . $image_name : $button_style_image; + +// command +$command = '_xclick'; +switch ($type) { + case 'donation': + $command = '_donations'; + break; + case 'subscription': + $command = '_xclick-subscriptions'; + break; +} +?> +
> + + + + + + + + +
\ No newline at end of file diff --git a/plugins/fields/acfpaypal/version.php b/plugins/fields/acfpaypal/version.php new file mode 100644 index 00000000..fb08e33e --- /dev/null +++ b/plugins/fields/acfpaypal/version.php @@ -0,0 +1,14 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; \ No newline at end of file diff --git a/plugins/fields/acfphp/acfphp.php b/plugins/fields/acfphp/acfphp.php new file mode 100644 index 00000000..5d195423 --- /dev/null +++ b/plugins/fields/acfphp/acfphp.php @@ -0,0 +1,91 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFPHP extends ACF_Field +{ + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return; + } + + $fieldNode->setAttribute('type', 'textarea'); + $fieldNode->setAttribute('filter', 'raw'); + $fieldNode->setAttribute('class', 'span12 w-100'); + $fieldNode->setAttribute('rows', '10'); + $fieldNode->setAttribute('hint', '$name = "John Doe";\nreturn $name;'); + + return $fieldNode; + } + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + // Get plugin params + $plugin = PluginHelper::getPlugin('fields', 'acfphp'); + $params = new Registry($plugin->params); + + $payload = [ + 'field' => $field, + 'item' => $item + ]; + + // Enable buffer output + $executer = new \NRFramework\Executer($field->value, $payload); + + $field->value = $executer + ->setForbiddenPHPFunctions($params->get('forbidden_php_functions')) + ->run(); + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } +} diff --git a/plugins/fields/acfphp/acfphp.xml b/plugins/fields/acfphp/acfphp.xml new file mode 100644 index 00000000..793936fa --- /dev/null +++ b/plugins/fields/acfphp/acfphp.xml @@ -0,0 +1,34 @@ + + + ACF_PHP + ACF_PHP_DESC + Tassos Marinos + May 2019 + Copyright (C) 2017 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfphp.php + script.install.helper.php + version.php + tmpl + language + params + + + +
+ +
+
+
+
diff --git a/plugins/fields/acfphp/language/en-GB/en-GB.plg_fields_acfphp.ini b/plugins/fields/acfphp/language/en-GB/en-GB.plg_fields_acfphp.ini new file mode 100644 index 00000000..d50f4713 --- /dev/null +++ b/plugins/fields/acfphp/language/en-GB/en-GB.plg_fields_acfphp.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPHP_LABEL="ACF - PHP" +ACF_PHP="Fields - ACF PHP" +ACF_PHP_DESC="Execute PHP in a Joomla! Custom Field and display a dynamic value in the front-end." +ACF_PHP_VALUE_DESC="The PHP code added here is executed just before the field is rendered in the front-end and must return a value using the return; statement.

Example 1:
$name = 'John Doe';
return $name;

Example 2:
return ($user->guest) ? 'Guest' : $user->name;

To access field settings use $field (Object) variable." +ACF_PHP_FORBIDDEN_PHP_FUNCTIONS="Forbidden PHP Functions" +ACF_PHP_FORBIDDEN_PHP_FUNCTIONS_DESC="A comma separated list of PHP functions that are forbidden. The whole PHP block of code will not be executed if it contains any of these functions." \ No newline at end of file diff --git a/plugins/fields/acfphp/language/en-GB/en-GB.plg_fields_acfphp.sys.ini b/plugins/fields/acfphp/language/en-GB/en-GB.plg_fields_acfphp.sys.ini new file mode 100644 index 00000000..da0b71b4 --- /dev/null +++ b/plugins/fields/acfphp/language/en-GB/en-GB.plg_fields_acfphp.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_PHP="Fields - ACF PHP" +ACF_PHP_DESC="Execute PHP in a Joomla! Custom Field and display a dynamic value in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfphp/language/es-ES/es-ES.plg_fields_acfphp.ini b/plugins/fields/acfphp/language/es-ES/es-ES.plg_fields_acfphp.ini new file mode 100644 index 00000000..5ca7b8cd --- /dev/null +++ b/plugins/fields/acfphp/language/es-ES/es-ES.plg_fields_acfphp.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPHP_LABEL="ACF - PHP" +ACF_PHP="Campos - ACF PHP" +ACF_PHP_DESC="Ejecute PHP en un campo personalizado de Joomla y muestre un valor dinámico en el front-end." +ACF_PHP_VALUE_DESC="El código PHP agregado aquí se ejecuta justo antes de que el campo se represente en el front-end y debe devolver un valor usando el retorno; declaración.

Ejemplo 1:
$name = 'John Doe';
return $name;

Ejemplo 2:
return ($user->guest) ? 'Guest' : $user->name;

Para acceder a la configuración de campo, use la variable $field (Object) ." +ACF_PHP_FORBIDDEN_PHP_FUNCTIONS="Funciones PHP prohibidas" +ACF_PHP_FORBIDDEN_PHP_FUNCTIONS_DESC="Una lista separada por comas de las funciones de PHP que están prohibidas. El bloque completo de código PHP no se ejecutará si contiene alguna de estas funciones." diff --git a/plugins/fields/acfphp/language/es-ES/es-ES.plg_fields_acfphp.sys.ini b/plugins/fields/acfphp/language/es-ES/es-ES.plg_fields_acfphp.sys.ini new file mode 100644 index 00000000..a753f41b --- /dev/null +++ b/plugins/fields/acfphp/language/es-ES/es-ES.plg_fields_acfphp.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_PHP="Campos - ACF PHP" +ACF_PHP_DESC="Ejecute PHP en un campo personalizado de Joomla y muestre un valor dinámico en el front-end." diff --git a/plugins/fields/acfphp/params/acfphp.xml b/plugins/fields/acfphp/params/acfphp.xml new file mode 100644 index 00000000..5cbac3b7 --- /dev/null +++ b/plugins/fields/acfphp/params/acfphp.xml @@ -0,0 +1,11 @@ + +
+ + \ No newline at end of file diff --git a/plugins/fields/acfphp/script.install.helper.php b/plugins/fields/acfphp/script.install.helper.php new file mode 100644 index 00000000..ffc85adb --- /dev/null +++ b/plugins/fields/acfphp/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfphpInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfphp/script.install.php b/plugins/fields/acfphp/script.install.php new file mode 100644 index 00000000..401fce50 --- /dev/null +++ b/plugins/fields/acfphp/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFPHPInstallerScript extends PlgFieldsACFPHPInstallerScriptHelper +{ + public $alias = 'acfphp'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfphp/tmpl/acfphp.php b/plugins/fields/acfphp/tmpl/acfphp.php new file mode 100644 index 00000000..065272a0 --- /dev/null +++ b/plugins/fields/acfphp/tmpl/acfphp.php @@ -0,0 +1,15 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +echo $field->value; \ No newline at end of file diff --git a/plugins/fields/acfphp/version.php b/plugins/fields/acfphp/version.php new file mode 100644 index 00000000..8e294844 --- /dev/null +++ b/plugins/fields/acfphp/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfprogressbar/acfprogressbar.php b/plugins/fields/acfprogressbar/acfprogressbar.php new file mode 100644 index 00000000..f6042b21 --- /dev/null +++ b/plugins/fields/acfprogressbar/acfprogressbar.php @@ -0,0 +1,86 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFProgressBar extends ACF_Field +{ + protected $validate = 'number'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = '70'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + $option->setLabel($option->getValue() . '%'); + } + + return $options; + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + // percentage must be a number with values between 0 and 100 + $fieldNode->setAttribute('type', 'nrnumber'); + $fieldNode->setAttribute('min', 0); + $fieldNode->setAttribute('max', 100); + $fieldNode->setAttribute('addon', '%'); + $fieldNode->setAttribute('class', 'input-mini'); + + return $fieldNode; + } +} diff --git a/plugins/fields/acfprogressbar/acfprogressbar.xml b/plugins/fields/acfprogressbar/acfprogressbar.xml new file mode 100644 index 00000000..dfe54636 --- /dev/null +++ b/plugins/fields/acfprogressbar/acfprogressbar.xml @@ -0,0 +1,24 @@ + + + ACF_PROGRESSBAR + ACF_PROGRESSBAR_DESC + Tassos Marinos + April 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfprogressbar.php + script.install.helper.php + version.php + language + params + tmpl + + + css + + diff --git a/plugins/fields/acfprogressbar/language/ca-ES/ca-ES.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/ca-ES/ca-ES.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..41c86201 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/ca-ES/ca-ES.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Progress Bar" +ACF_PROGRESSBAR="camps - ACF Progress Bar" +ACF_PROGRESSBAR_DESC="El connector camp barra de progrés mostra una barra de progrés." +ACF_PROGRESSBAR_VALUE_DESC="Estableix el valor del percentatge per la barra de progrés." +ACF_PROGRESSBAR_HEIGHT_DESC="L'alçada de la barra de progrés" +ACF_PROGRESSBAR_COLOR_DESC="Estableix el color de la barra de progrés." +ACF_PROGRESSBAR_STRIPPED="Ratllada" +ACF_PROGRESSBAR_STRIPPED_DESC="Mostra bandes a la barra de progrés." +ACF_PROGRESSBAR_ANIMATED="Animada" +ACF_PROGRESSBAR_ANIMATED_DESC="Anima la barra de progrés." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Vores arrodonides" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Arrodoneix les vores." +ACF_PROGRESSBAR_SHADOW="Ombra" +ACF_PROGRESSBAR_SHADOW_DESC="Activa l'ombra interior." +ACF_PROGRESSBAR_SHOW_LABEL="Mostrar etiqueta" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Activa-ho per mostrar el text del percentatge dins la barra de progrés." diff --git a/plugins/fields/acfprogressbar/language/da-DK/da-DK.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/da-DK/da-DK.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..b0358089 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/da-DK/da-DK.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Fremskridtsbjælke" +ACF_PROGRESSBAR="Felter - ACF Fremskridtsbjælke" +ACF_PROGRESSBAR_DESC="Plugin'et fremskridtsbjælke viser en fremskridtsbjælke." +ACF_PROGRESSBAR_VALUE_DESC="Sæt procentsatsen for fremskridtsbjælken." +ACF_PROGRESSBAR_HEIGHT_DESC="Højden på fremskditsbjælken." +ACF_PROGRESSBAR_COLOR_DESC="Sæt farven på fremskridtsbjælken." +ACF_PROGRESSBAR_STRIPPED="Stribet" +ACF_PROGRESSBAR_STRIPPED_DESC="Vis striber på fremskdridtsbjælken." +ACF_PROGRESSBAR_ANIMATED="Animeret" +ACF_PROGRESSBAR_ANIMATED_DESC="Animer fremskridtsbjælken." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Afrundede hjørner" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Aktiver afrundede hjørner." +ACF_PROGRESSBAR_SHADOW="Skygge" +ACF_PROGRESSBAR_SHADOW_DESC="Aktiver indre skygge." +ACF_PROGRESSBAR_SHOW_LABEL="Vis label" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Aktiver for at vise procenttekst i fremskridtsbjælken." diff --git a/plugins/fields/acfprogressbar/language/de-DE/de-DE.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/de-DE/de-DE.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..d3fd0df5 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/de-DE/de-DE.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Fortschrittsbalken" +ACF_PROGRESSBAR="Felder - ACF-Fortschrittsanzeige" +ACF_PROGRESSBAR_DESC="Das Fortschrittsbalken-Feld-Plugin zeigt einen Fortschrittsbalken an." +ACF_PROGRESSBAR_VALUE_DESC="Legen Sie die Prozentzahl für den Fortschrittsbalken fest." +ACF_PROGRESSBAR_HEIGHT_DESC="Die Höhe des Fortschrittsbalkens." +ACF_PROGRESSBAR_COLOR_DESC="Legen Sie die Farbe des Fortschrittsbalkens fest." +ACF_PROGRESSBAR_STRIPPED="Stripped" +ACF_PROGRESSBAR_STRIPPED_DESC="Streifen auf dem Fortschrittsbalken anzeigen." +ACF_PROGRESSBAR_ANIMATED="Animiert" +ACF_PROGRESSBAR_ANIMATED_DESC="Animieren Sie den Fortschrittsbalken." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Abgerundete Ecken" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Abgerundete Ecken aktivieren." +ACF_PROGRESSBAR_SHADOW="Schatten" +ACF_PROGRESSBAR_SHADOW_DESC="Inneren Schatten aktivieren." +ACF_PROGRESSBAR_SHOW_LABEL="Label anzeigen" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Prozentualen Text in der Fortschrittsleiste anzeigen lassen." diff --git a/plugins/fields/acfprogressbar/language/en-GB/en-GB.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/en-GB/en-GB.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..30588f90 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/en-GB/en-GB.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Progress Bar" +ACF_PROGRESSBAR="Fields - ACF Progress Bar" +ACF_PROGRESSBAR_DESC="Enter a completion percentage in the back-end and display the respective progress bar in the front-end." +ACF_PROGRESSBAR_VALUE_DESC="Set the percentage number for the progress bar." +ACF_PROGRESSBAR_HEIGHT_DESC="The height of the progress bar." +ACF_PROGRESSBAR_COLOR_DESC="Set the progress bar color." +ACF_PROGRESSBAR_STRIPPED="Stripped" +ACF_PROGRESSBAR_STRIPPED_DESC="Display stripes on progress bar." +ACF_PROGRESSBAR_ANIMATED="Animated" +ACF_PROGRESSBAR_ANIMATED_DESC="Animate the progress bar." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Rounded Corners" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Enable rounded corners." +ACF_PROGRESSBAR_SHADOW="Shadow" +ACF_PROGRESSBAR_SHADOW_DESC="Enable inner shadow." +ACF_PROGRESSBAR_SHOW_LABEL="Show Label" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Enable to display percentage text within the progress bar." \ No newline at end of file diff --git a/plugins/fields/acfprogressbar/language/en-GB/en-GB.plg_fields_acfprogressbar.sys.ini b/plugins/fields/acfprogressbar/language/en-GB/en-GB.plg_fields_acfprogressbar.sys.ini new file mode 100644 index 00000000..8d89cf91 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/en-GB/en-GB.plg_fields_acfprogressbar.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_PROGRESSBAR="Fields - ACF Progress Bar" +ACF_PROGRESSBAR_DESC="Enter a completion percentage in the back-end and display the respective progress bar in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfprogressbar/language/es-ES/es-ES.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/es-ES/es-ES.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..5a20814f --- /dev/null +++ b/plugins/fields/acfprogressbar/language/es-ES/es-ES.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Barra de progreso" +ACF_PROGRESSBAR="Campos - ACF Barra de progreso" +ACF_PROGRESSBAR_DESC="El complemento de campo Barra de progreso muestra una barra de progreso." +ACF_PROGRESSBAR_VALUE_DESC="Establezca el número de porcentaje para la barra de progreso." +ACF_PROGRESSBAR_HEIGHT_DESC="La altura de la barra de progreso." +ACF_PROGRESSBAR_COLOR_DESC="Establecer el color de la barra de progreso." +ACF_PROGRESSBAR_STRIPPED="Despojada" +ACF_PROGRESSBAR_STRIPPED_DESC="Mostrar rayas en la barra de progreso." +ACF_PROGRESSBAR_ANIMATED="Animado" +ACF_PROGRESSBAR_ANIMATED_DESC="Animar la barra de progreso." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Esquinas Redondeadas" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Habilitar esquinas redondeadas." +ACF_PROGRESSBAR_SHADOW="Sombra" +ACF_PROGRESSBAR_SHADOW_DESC="Habilitar sombra interior." +ACF_PROGRESSBAR_SHOW_LABEL="Mostrar Etiqueta" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Habilite para mostrar texto de porcentaje dentro de la barra de progreso." diff --git a/plugins/fields/acfprogressbar/language/es-ES/es-ES.plg_fields_acfprogressbar.sys.ini b/plugins/fields/acfprogressbar/language/es-ES/es-ES.plg_fields_acfprogressbar.sys.ini new file mode 100644 index 00000000..a53f53c7 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/es-ES/es-ES.plg_fields_acfprogressbar.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_PROGRESSBAR="Campos - ACF Barra de progreso" +ACF_PROGRESSBAR_DESC="Ingrese un porcentaje de finalización en el back-end y muestre la barra de progreso respectiva en el front-end." diff --git a/plugins/fields/acfprogressbar/language/fr-FR/fr-FR.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/fr-FR/fr-FR.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..3e31fcb9 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/fr-FR/fr-FR.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Progress Bar" +ACF_PROGRESSBAR="Champs - Progress Bar ACF" +ACF_PROGRESSBAR_DESC="Le plugin de champ Progress Bar affiche une barre de progression." +ACF_PROGRESSBAR_VALUE_DESC="Définir un nombre en pourcentage pour la barre de progression" +ACF_PROGRESSBAR_HEIGHT_DESC="La hauteur de la barre de progression." +ACF_PROGRESSBAR_COLOR_DESC="Définir la couleur de la barre de progression." +ACF_PROGRESSBAR_STRIPPED="Zébré" +ACF_PROGRESSBAR_STRIPPED_DESC="Affichage des bandes sur la barre de progression." +ACF_PROGRESSBAR_ANIMATED="Animé" +ACF_PROGRESSBAR_ANIMATED_DESC="Animer la barre de progression." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Coins Arrondis" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Active les coins arrondis" +ACF_PROGRESSBAR_SHADOW="Ombre" +ACF_PROGRESSBAR_SHADOW_DESC="Active l'ombre interne" +ACF_PROGRESSBAR_SHOW_LABEL="Afficher Label" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Activez pour afficher le pourcentage en texte dans la barre de progression." diff --git a/plugins/fields/acfprogressbar/language/it-IT/it-IT.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/it-IT/it-IT.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..d8985772 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/it-IT/it-IT.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - barra di avanzamento" +ACF_PROGRESSBAR="Campi - barra di avanzamento ACF" +ACF_PROGRESSBAR_DESC="Il plugin del campo barra di avanzamento visualizza una barra di avanzamento." +ACF_PROGRESSBAR_VALUE_DESC="Imposta il numero percentuale per la barra di avanzamento." +ACF_PROGRESSBAR_HEIGHT_DESC="L'altezza della barra di avanzamento." +ACF_PROGRESSBAR_COLOR_DESC="Imposta il colore della barra di avanzamento." +ACF_PROGRESSBAR_STRIPPED="Stripped" +ACF_PROGRESSBAR_STRIPPED_DESC="Visualizza strisce sulla barra di avanzamento." +ACF_PROGRESSBAR_ANIMATED="Animata" +ACF_PROGRESSBAR_ANIMATED_DESC="Anima la barra di avanzamento." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Angoli arrotondati" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Abilita angoli arrotondati" +ACF_PROGRESSBAR_SHADOW="Ombra" +ACF_PROGRESSBAR_SHADOW_DESC="Abilita l'ombra interna." +ACF_PROGRESSBAR_SHOW_LABEL="Mostra etichetta" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Abilita per visualizzare la percentuale di testo all'interno della barra di avanzamento." diff --git a/plugins/fields/acfprogressbar/language/nl-NL/nl-NL.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/nl-NL/nl-NL.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..3833601a --- /dev/null +++ b/plugins/fields/acfprogressbar/language/nl-NL/nl-NL.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Progress Bar" +ACF_PROGRESSBAR="Velden - ACF Progress Bar" +ACF_PROGRESSBAR_DESC="De Progress Bar field plugin geeft een voortgangsbalk weer." +ACF_PROGRESSBAR_VALUE_DESC="Set the percentage number for the progress bar." +ACF_PROGRESSBAR_HEIGHT_DESC="De hoogte van de voortgangsbalk." +ACF_PROGRESSBAR_COLOR_DESC="Stel de kleur van de voortgangsbalk in." +ACF_PROGRESSBAR_STRIPPED="Gestreept" +ACF_PROGRESSBAR_STRIPPED_DESC="Geef strepen weer op de voortgangsbalk." +ACF_PROGRESSBAR_ANIMATED="Geanimeerd" +ACF_PROGRESSBAR_ANIMATED_DESC="Animeer de voortgangsbalk" +ACF_PROGRESSBAR_ROUNDED_CORNERS="Ronde Hoeken" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Schakel ronde hoeken in" +ACF_PROGRESSBAR_SHADOW="Schaduw" +ACF_PROGRESSBAR_SHADOW_DESC="Schakel binnen schaduw in" +ACF_PROGRESSBAR_SHOW_LABEL="Toon Etiket" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Schakel in om procentuele tekst binnen de voortgangsbalk weer te geven." diff --git a/plugins/fields/acfprogressbar/language/ru-RU/ru-RU.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/ru-RU/ru-RU.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..dac2e536 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/ru-RU/ru-RU.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - индикатор выполнения" +ACF_PROGRESSBAR="Поля - индикатор выполнения ACF" +ACF_PROGRESSBAR_DESC="Плагин поля Progress Bar отображает индикатор выполнения." +ACF_PROGRESSBAR_VALUE_DESC="Установить процентное значение для индикатора выполнения." +ACF_PROGRESSBAR_HEIGHT_DESC="Высота индикатора выполнения." +ACF_PROGRESSBAR_COLOR_DESC="Установить цвет индикатора выполнения." +ACF_PROGRESSBAR_STRIPPED="Отображать полосы" +ACF_PROGRESSBAR_STRIPPED_DESC="Отображать полосы на индикаторе выполнения." +ACF_PROGRESSBAR_ANIMATED="Анимировать" +ACF_PROGRESSBAR_ANIMATED_DESC="Анимировать индикатор выполнения." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Закругленные углы" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Включить закругленные углы." +ACF_PROGRESSBAR_SHADOW="Тень" +ACF_PROGRESSBAR_SHADOW_DESC="Включить внутреннюю тень." +ACF_PROGRESSBAR_SHOW_LABEL="Показать метку" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Включить отображение процентного текста на индикаторе выполнения." diff --git a/plugins/fields/acfprogressbar/language/sv-SE/sv-SE.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/sv-SE/sv-SE.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..f154bd0c --- /dev/null +++ b/plugins/fields/acfprogressbar/language/sv-SE/sv-SE.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - Progress Bar" +ACF_PROGRESSBAR="Fält - ACF Progress Bar" +ACF_PROGRESSBAR_DESC="Fält-pluginen Progress Bar visar en förloppsindikator." +ACF_PROGRESSBAR_VALUE_DESC="Ställ in procenttal för förloppsindikatorn." +ACF_PROGRESSBAR_HEIGHT_DESC="Förloppsindikatorns höjd." +ACF_PROGRESSBAR_COLOR_DESC="Ställ in förloppsindikatorns färg." +ACF_PROGRESSBAR_STRIPPED="Randig" +ACF_PROGRESSBAR_STRIPPED_DESC="Visa ränder i förloppsindikatorn." +ACF_PROGRESSBAR_ANIMATED="Animerad" +ACF_PROGRESSBAR_ANIMATED_DESC="Animera förloppsindikatorn." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Rundade hörn." +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Aktivera rundade hörn." +ACF_PROGRESSBAR_SHADOW="Skugga" +ACF_PROGRESSBAR_SHADOW_DESC="Aktivera inre skugga." +ACF_PROGRESSBAR_SHOW_LABEL="Visa etikett" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Gör det möjligt att visa procentuell text i förloppsindikatorn." diff --git a/plugins/fields/acfprogressbar/language/uk-UA/uk-UA.plg_fields_acfprogressbar.ini b/plugins/fields/acfprogressbar/language/uk-UA/uk-UA.plg_fields_acfprogressbar.ini new file mode 100644 index 00000000..a70d4172 --- /dev/null +++ b/plugins/fields/acfprogressbar/language/uk-UA/uk-UA.plg_fields_acfprogressbar.ini @@ -0,0 +1,23 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFPROGRESSBAR_LABEL="ACF - панель ходу" +ACF_PROGRESSBAR="Поля - панель ходу ACF" +ACF_PROGRESSBAR_DESC="Плагін поля"_QQ_" Прогрес "_QQ_"відображає панель прогресу." +ACF_PROGRESSBAR_VALUE_DESC="Встановити число відсотків для рядка прогресу." +ACF_PROGRESSBAR_HEIGHT_DESC="Висота смуги прогресу." +ACF_PROGRESSBAR_COLOR_DESC="Встановити колір смужки прогресу." +ACF_PROGRESSBAR_STRIPPED="Знятий" +ACF_PROGRESSBAR_STRIPPED_DESC="Відображення смуг на панелі прогресу." +ACF_PROGRESSBAR_ANIMATED="Анімовані" +ACF_PROGRESSBAR_ANIMATED_DESC="Анімація панелі прогресу." +ACF_PROGRESSBAR_ROUNDED_CORNERS="Закруглені кути" +ACF_PROGRESSBAR_ROUNDED_CORNERS_DESC="Увімкнути закруглені кути." +ACF_PROGRESSBAR_SHADOW="Тінь" +ACF_PROGRESSBAR_SHADOW_DESC="Увімкнути внутрішню тінь." +ACF_PROGRESSBAR_SHOW_LABEL="Показати мітку" +ACF_PROGRESSBAR_SHOW_LABEL_DESC="Увімкнути для відображення відсоткового тексту в рядку прогресу." diff --git a/plugins/fields/acfprogressbar/params/acfprogressbar.xml b/plugins/fields/acfprogressbar/params/acfprogressbar.xml new file mode 100644 index 00000000..44d227df --- /dev/null +++ b/plugins/fields/acfprogressbar/params/acfprogressbar.xml @@ -0,0 +1,47 @@ + +
+ +
+ + + + + + + + + + + + +
+
+
diff --git a/plugins/fields/acfprogressbar/script.install.helper.php b/plugins/fields/acfprogressbar/script.install.helper.php new file mode 100644 index 00000000..daf4c0aa --- /dev/null +++ b/plugins/fields/acfprogressbar/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfprogressbarInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfprogressbar/script.install.php b/plugins/fields/acfprogressbar/script.install.php new file mode 100644 index 00000000..37ca87a6 --- /dev/null +++ b/plugins/fields/acfprogressbar/script.install.php @@ -0,0 +1,43 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\Filesystem\File; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFProgressBarInstallerScript extends PlgFieldsACFProgressBarInstallerScriptHelper +{ + public $alias = 'acfprogressbar'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + // If version.php doesn't exist, copy it from the system plugin + if ($this->isInstalled() && !file_exists($this->getMainFolder() . '/version.php')) + { + $systemVersionPath = JPATH_SITE . '/plugins/system/acf/version.php'; + + $result = File::copy($systemVersionPath, $this->getMainFolder() . '/version.php'); + } + + return parent::onBeforeInstall(); + } +} diff --git a/plugins/fields/acfprogressbar/tmpl/acfprogressbar.php b/plugins/fields/acfprogressbar/tmpl/acfprogressbar.php new file mode 100644 index 00000000..c6e31b21 --- /dev/null +++ b/plugins/fields/acfprogressbar/tmpl/acfprogressbar.php @@ -0,0 +1,59 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\HTML\HTMLHelper; + +$percentage = floatval($field->value); + +if (empty($percentage)) +{ + return; +} + +// Add Media Files +HTMLHelper::stylesheet('plg_fields_acfprogressbar/style.css', ['relative' => true, 'version' => 'auto']); + +$height = $fieldParams->get('height', '18'); +$color = $fieldParams->get('color', '#007bff'); +$stripped = $fieldParams->get('stripped', '0'); +$animated = $fieldParams->get('animated', '0'); +$roundedcorners = $fieldParams->get('roundedcorners', '1'); +$shadow = $fieldParams->get('shadow', '1'); +$show_label = $fieldParams->get('show_label', 'center'); + +$progressbar_atts = 'height: '.$height.'px;'; +$progressbar_cls = ($roundedcorners == '1') ? ' acf_progressbar_rounded' : ''; +$progressbar_cls .= ($shadow == '1') ? ' acf_progressbar_shadow' : ''; + +$color_att = 'background-color: '.$color.';'; + +$bar_cls = ''; +$bar_cls .= ($stripped == '1') ? ' acf_progressbar_stripes' : ''; +$bar_cls .= ($animated == '1') ? ' acf_progressbar_animated' : ''; +$bar_cls .= ($roundedcorners == '1') ? ' acf_progressbar_rounded' : ''; + +$buffer = ' +
+
+
+'; + +if ($show_label != '0') +{ + $buffer .= '
' . $percentage . '%
'; +} + +$buffer .= '
'; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfprogressbar/version.php b/plugins/fields/acfprogressbar/version.php new file mode 100644 index 00000000..8e294844 --- /dev/null +++ b/plugins/fields/acfprogressbar/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2018 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfqrcode/acfqrcode.php b/plugins/fields/acfqrcode/acfqrcode.php new file mode 100644 index 00000000..017a96d4 --- /dev/null +++ b/plugins/fields/acfqrcode/acfqrcode.php @@ -0,0 +1,40 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFQRCode extends ACF_Field +{ + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'ACF_QRCODE_HINT'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; +} diff --git a/plugins/fields/acfqrcode/acfqrcode.xml b/plugins/fields/acfqrcode/acfqrcode.xml new file mode 100644 index 00000000..32e5fc26 --- /dev/null +++ b/plugins/fields/acfqrcode/acfqrcode.xml @@ -0,0 +1,21 @@ + + + ACF_QRCODE + ACF_QRCODE_DESC + Tassos Marinos + May 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfqrcode.php + script.install.helper.php + tmpl + params + language + version.php + + diff --git a/plugins/fields/acfqrcode/language/ca-ES/ca-ES.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/ca-ES/ca-ES.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..ab310b47 --- /dev/null +++ b/plugins/fields/acfqrcode/language/ca-ES/ca-ES.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - Codi QR" +ACF_QRCODE="Camps - ACF Codi QR" +ACF_QRCODE_DESC="Genera un Codi Qr colorejat que emmagatzemi text, un enllaç, un correu electrònic, un telèfon i més!" +ACF_QRCODE_VALUE_DESC="Escriu el text del Codi QR" +ACF_QRCODE_SIZE="Mida" +ACF_QRCODE_SIZE_DESC="Escriu la mida en píxels del Codi QR. (Exemple: 300)" +ACF_QRCODE_COLOR="Color" +ACF_QRCODE_COLOR_DESC="Escriu el color principal" +ACF_QRCODE_BGCOLOR="Color de fons" +ACF_QRCODE_BGCOLOR_DESC="Escull el color de fons" +ACF_QRCODE_HINT="Escriu el text del teu Codi QR" diff --git a/plugins/fields/acfqrcode/language/da-DK/da-DK.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/da-DK/da-DK.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..ea1af2b9 --- /dev/null +++ b/plugins/fields/acfqrcode/language/da-DK/da-DK.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR kode" +ACF_QRCODE="Felter - ACF QR kode" +ACF_QRCODE_DESC="Generer en farvet QR kode til at gemme tekst, link, e-mail, telefonnummer og mere!" +ACF_QRCODE_VALUE_DESC="Angiv QR kode teksten." +ACF_QRCODE_SIZE="Størrelse" +ACF_QRCODE_SIZE_DESC="Angiv QR kode størrelsen i pixels. (Eksempel: 300)" +ACF_QRCODE_COLOR="Farve" +ACF_QRCODE_COLOR_DESC="Vælg forgrundsfarventhe foreground color" +ACF_QRCODE_BGCOLOR="BaggrundsfarveColor" +ACF_QRCODE_BGCOLOR_DESC="Vælg baggrundsfarventhe background color" +ACF_QRCODE_HINT="Angiv din QR kode tekst." diff --git a/plugins/fields/acfqrcode/language/de-DE/de-DE.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/de-DE/de-DE.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..4ad715f6 --- /dev/null +++ b/plugins/fields/acfqrcode/language/de-DE/de-DE.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR Code" +ACF_QRCODE="Felder - ACF QR Code" +ACF_QRCODE_DESC="Erstellen Sie einen farbigen QR-Code zum Speichern von Text, Link, E-Mail, Telefonnummer und vielem mehr!" +ACF_QRCODE_VALUE_DESC="Geben Sie den QR-Code ein." +ACF_QRCODE_SIZE="Größe" +ACF_QRCODE_SIZE_DESC="Geben Sie die Größe des QR-Codes in Pixel ein. (Beispiel: 300)" +ACF_QRCODE_COLOR="Farbe" +ACF_QRCODE_COLOR_DESC="Vordergrundfarbe auswählen" +ACF_QRCODE_BGCOLOR="Hintergrundfarbe" +ACF_QRCODE_BGCOLOR_DESC="Hintergrundfarbe auswählen" +ACF_QRCODE_HINT="Geben Sie Ihren QR-Code ein." diff --git a/plugins/fields/acfqrcode/language/en-GB/en-GB.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/en-GB/en-GB.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..030018ee --- /dev/null +++ b/plugins/fields/acfqrcode/language/en-GB/en-GB.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR Code" +ACF_QRCODE="Fields - ACF QR Code" +ACF_QRCODE_DESC="Generate a colored QR Code to store text, link, email, telephone number and more!" +ACF_QRCODE_VALUE_DESC="Enter the QR Code text." +ACF_QRCODE_SIZE="Size" +ACF_QRCODE_SIZE_DESC="Enter the QR Code size in pixels. (Example: 300)" +ACF_QRCODE_COLOR="Color" +ACF_QRCODE_COLOR_DESC="Select the foreground color" +ACF_QRCODE_BGCOLOR="Background Color" +ACF_QRCODE_BGCOLOR_DESC="Select the background color" +ACF_QRCODE_HINT="Enter your QR Code text." \ No newline at end of file diff --git a/plugins/fields/acfqrcode/language/en-GB/en-GB.plg_fields_acfqrcode.sys.ini b/plugins/fields/acfqrcode/language/en-GB/en-GB.plg_fields_acfqrcode.sys.ini new file mode 100644 index 00000000..51d01b25 --- /dev/null +++ b/plugins/fields/acfqrcode/language/en-GB/en-GB.plg_fields_acfqrcode.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_QRCODE="Fields - ACF QR Code" +ACF_QRCODE_DESC="Generate a colored QR Code to store text, link, email, telephone number and more!" \ No newline at end of file diff --git a/plugins/fields/acfqrcode/language/es-ES/es-ES.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/es-ES/es-ES.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..e6feb3b1 --- /dev/null +++ b/plugins/fields/acfqrcode/language/es-ES/es-ES.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - Código QR" +ACF_QRCODE="Campos - ACF Código QR" +ACF_QRCODE_DESC="¡Genera un código QR de color para almacenar texto, enlace, correo electrónico, número de teléfono y más!" +ACF_QRCODE_VALUE_DESC="Ingrese el texto del Código QR." +ACF_QRCODE_SIZE="Tamaño" +ACF_QRCODE_SIZE_DESC="Ingrese el tamaño del código QR en píxeles. (Ejemplo: 300)" +ACF_QRCODE_COLOR="Color" +ACF_QRCODE_COLOR_DESC="Seleccione el color de primer plano" +ACF_QRCODE_BGCOLOR="Color de Fondo" +ACF_QRCODE_BGCOLOR_DESC="Seleccione el color de fondo" +ACF_QRCODE_HINT="Ingrese el texto de su Código QR." diff --git a/plugins/fields/acfqrcode/language/es-ES/es-ES.plg_fields_acfqrcode.sys.ini b/plugins/fields/acfqrcode/language/es-ES/es-ES.plg_fields_acfqrcode.sys.ini new file mode 100644 index 00000000..7147d160 --- /dev/null +++ b/plugins/fields/acfqrcode/language/es-ES/es-ES.plg_fields_acfqrcode.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_QRCODE="Campos - ACF QR Code" +ACF_QRCODE_DESC="¡Genera un código QR de color para almacenar texto, enlace, correo electrónico, número de teléfono y más!" diff --git a/plugins/fields/acfqrcode/language/it-IT/it-IT.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/it-IT/it-IT.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..3b60913e --- /dev/null +++ b/plugins/fields/acfqrcode/language/it-IT/it-IT.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - codice QR" +ACF_QRCODE="Campi - codice QR ACF" +ACF_QRCODE_DESC="Genera un codice QR colorato per memorizzare testo, link, e-mail, numero di telefono e altro!" +ACF_QRCODE_VALUE_DESC="Inserisci il tuo testo del codice QR" +ACF_QRCODE_SIZE="Dimensioni" +ACF_QRCODE_SIZE_DESC="Immettere la dimensione del codice QR in pixel. (Esempio: 300)" +ACF_QRCODE_COLOR="Colore" +ACF_QRCODE_COLOR_DESC="Seleziona il colore in primo piano" +ACF_QRCODE_BGCOLOR="Colore di sfondo" +ACF_QRCODE_BGCOLOR_DESC="Seleziona il colore di sfondo" +ACF_QRCODE_HINT="Inserisci il tuo testo del codice QR" diff --git a/plugins/fields/acfqrcode/language/nl-NL/nl-NL.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/nl-NL/nl-NL.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..d32961ac --- /dev/null +++ b/plugins/fields/acfqrcode/language/nl-NL/nl-NL.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR Code" +ACF_QRCODE="Velden - ACF QR Code" +ACF_QRCODE_DESC="Genereer een gekleurde QR-code om tekst, link, e-mail, telefoonnummer en meer op te slaan!" +ACF_QRCODE_VALUE_DESC="Voer de QR-codetekst in." +ACF_QRCODE_SIZE="Grootte" +ACF_QRCODE_SIZE_DESC="Voer de grootte van de QR-code in pixels in. (Bijvoorbeeld: 300)" +ACF_QRCODE_COLOR="Kleur" +ACF_QRCODE_COLOR_DESC="Selecteer de voorgrond kleur" +ACF_QRCODE_BGCOLOR="Achtergrond kleur" +ACF_QRCODE_BGCOLOR_DESC="Selecteer de achtergrond kleur" +ACF_QRCODE_HINT="Vul je QR Code tekst in." diff --git a/plugins/fields/acfqrcode/language/ru-RU/ru-RU.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/ru-RU/ru-RU.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..c99cfefc --- /dev/null +++ b/plugins/fields/acfqrcode/language/ru-RU/ru-RU.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR Code" +ACF_QRCODE="Поля - QR-код ACF" +ACF_QRCODE_DESC="Создать цветной QR-код для хранения текста, ссылки, электронной почты, номера телефона и т. Д.!" +ACF_QRCODE_VALUE_DESC="Введите текст QR-кода." +ACF_QRCODE_SIZE="Размер" +ACF_QRCODE_SIZE_DESC="Введите размер QR-кода в пикселях. (Пример: 300)" +ACF_QRCODE_COLOR="Цвет" +ACF_QRCODE_COLOR_DESC="Выберите цвет переднего плана" +ACF_QRCODE_BGCOLOR="Цвет фона" +ACF_QRCODE_BGCOLOR_DESC="Выберите цвет фона" +ACF_QRCODE_HINT="Введите текст своего QR-кода." diff --git a/plugins/fields/acfqrcode/language/sv-SE/sv-SE.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/sv-SE/sv-SE.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..e68b4ab5 --- /dev/null +++ b/plugins/fields/acfqrcode/language/sv-SE/sv-SE.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR-kod" +ACF_QRCODE="Fält - ACF QR-kod" +ACF_QRCODE_DESC="Generera en färgad QR-kod för att lagra text, länk, e-post, telefonnummer och mer!" +ACF_QRCODE_VALUE_DESC="Ange QR-kodens text." +ACF_QRCODE_SIZE="Storlek" +ACF_QRCODE_SIZE_DESC="Ange QR-kodens storlek i pixlar. (Exempel: 300)" +ACF_QRCODE_COLOR="Färg" +ACF_QRCODE_COLOR_DESC="Välj förgrundsfärg" +ACF_QRCODE_BGCOLOR="Bakgrundsfärg" +ACF_QRCODE_BGCOLOR_DESC="Välj bakgrundsfärg" +ACF_QRCODE_HINT="Ange din QR-kodtext." diff --git a/plugins/fields/acfqrcode/language/uk-UA/uk-UA.plg_fields_acfqrcode.ini b/plugins/fields/acfqrcode/language/uk-UA/uk-UA.plg_fields_acfqrcode.ini new file mode 100644 index 00000000..ba4934a4 --- /dev/null +++ b/plugins/fields/acfqrcode/language/uk-UA/uk-UA.plg_fields_acfqrcode.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFQRCODE_LABEL="ACF - QR-код" +ACF_QRCODE="Поля - QR-код ACF" +ACF_QRCODE_DESC="Створіть кольоровий QR-код для зберігання тексту, посилання, електронної пошти, номера телефону та іншого!" +ACF_QRCODE_VALUE_DESC="Введіть текст QR-коду." +ACF_QRCODE_SIZE="Розмір" +ACF_QRCODE_SIZE_DESC="Введіть розмір QR-коду у пікселях. (Приклад: 300)" +ACF_QRCODE_COLOR="Колір" +ACF_QRCODE_COLOR_DESC="Виберіть колір переднього плану" +ACF_QRCODE_BGCOLOR="Колір фону" +ACF_QRCODE_BGCOLOR_DESC="Виберіть колір фону" +ACF_QRCODE_HINT="Введіть текст свого QR-коду." diff --git a/plugins/fields/acfqrcode/params/acfqrcode.xml b/plugins/fields/acfqrcode/params/acfqrcode.xml new file mode 100644 index 00000000..f0c78a31 --- /dev/null +++ b/plugins/fields/acfqrcode/params/acfqrcode.xml @@ -0,0 +1,24 @@ + +
+ +
+ + + +
+
+
diff --git a/plugins/fields/acfqrcode/script.install.helper.php b/plugins/fields/acfqrcode/script.install.helper.php new file mode 100644 index 00000000..a37afbdc --- /dev/null +++ b/plugins/fields/acfqrcode/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfqrcodeInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfqrcode/script.install.php b/plugins/fields/acfqrcode/script.install.php new file mode 100644 index 00000000..8abc4a88 --- /dev/null +++ b/plugins/fields/acfqrcode/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFQRCodeInstallerScript extends PlgFieldsACFQRCodeInstallerScriptHelper +{ + public $alias = 'acfqrcode'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfqrcode/tmpl/acfqrcode.php b/plugins/fields/acfqrcode/tmpl/acfqrcode.php new file mode 100644 index 00000000..2aa95101 --- /dev/null +++ b/plugins/fields/acfqrcode/tmpl/acfqrcode.php @@ -0,0 +1,36 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +$qrcode_text = htmlspecialchars($field->value, ENT_COMPAT, 'UTF-8'); + +if ($qrcode_text == '') +{ + return; +} + +// QR Code Label to be used as `alt` +$label = $field->label; + +// size, color and bg color +$size = $fieldParams->get('size', '100'); +$size = str_replace('px', '', $size); +$color = ltrim($fieldParams->get('color', '#000000'), '#'); +$bgcolor = ltrim($fieldParams->get('bgcolor', '#ffffff'), '#'); + +// create size, ex. 50x50 +$size_att = $size . 'x' . $size; + +$buffer = '' . $label . ''; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfqrcode/version.php b/plugins/fields/acfqrcode/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfqrcode/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfsoundcloud/acfsoundcloud.php b/plugins/fields/acfsoundcloud/acfsoundcloud.php new file mode 100644 index 00000000..1a572d07 --- /dev/null +++ b/plugins/fields/acfsoundcloud/acfsoundcloud.php @@ -0,0 +1,27 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFSoundCloud extends ACF_Field +{ +} diff --git a/plugins/fields/acfsoundcloud/acfsoundcloud.xml b/plugins/fields/acfsoundcloud/acfsoundcloud.xml new file mode 100644 index 00000000..1ee01600 --- /dev/null +++ b/plugins/fields/acfsoundcloud/acfsoundcloud.xml @@ -0,0 +1,22 @@ + + + ACF_SOUNDCLOUD + ACF_SOUNDCLOUD_DESC + Tassos Marinos + May 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfsoundcloud.php + script.install.helper.php + version.php + params + tmpl + language + fields + + diff --git a/plugins/fields/acfsoundcloud/fields/acfsoundcloud.php b/plugins/fields/acfsoundcloud/fields/acfsoundcloud.php new file mode 100644 index 00000000..01021654 --- /dev/null +++ b/plugins/fields/acfsoundcloud/fields/acfsoundcloud.php @@ -0,0 +1,80 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Form\Form; + +class JFormFieldACFSoundCloud extends FormField +{ + /** + * Method to attach a JForm object to the field. + * + * @param SimpleXMLElement $element The SimpleXMLElement object representing the tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. + * + * @return boolean True on success. + * + * @since 3.6 + */ + public function setup(SimpleXMLElement $element, $value, $group = null) + { + if (!parent::setup($element, $value, $group)) + { + return false; + } + + if ($this->value && is_string($this->value)) + { + // Support old values + if (is_numeric($this->value)) + { + $this->value = [ + 'id' => $this->value + ]; + } else + { + // Guess here is the JSON string from 'default' attribute + $this->value = json_decode($this->value, true); + } + } + + return true; + } + + protected function getInput() + { + $form_source = new SimpleXMLElement(' +
+
+ + +
+
+ '); + + $control = $this->name; + $formname = 'acfsoundcloud.' . str_replace(['jform[', '[', ']'], ['', '.', ''], $control); + + $form = Form::getInstance($formname, $form_source->asXML(), ['control' => $control]); + $form->bind($this->value); + + return $form->renderFieldset('acfsoundcloud'); + } +} \ No newline at end of file diff --git a/plugins/fields/acfsoundcloud/language/ca-ES/ca-ES.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/ca-ES/ca-ES.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..0ea94c57 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/ca-ES/ca-ES.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Camps - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Crea un camp on mostrar fàcilment un reproductor SoundCloud per la teva pista d'elecció. A més, pots personalitzar el reproductor amb opcions com la reproducció automàtica, mostrar/amagar els comentaris, qui ha pujat la pista i més." +ACF_SOUNDCLOUD_WIDTH="Amplada del reproductor" +ACF_SOUNDCLOUD_WIDTH_DESC="L'amplada del reproductor en píxels.

Pots utilitzar un percentatge afegint el signe % al final" +ACF_SOUNDCLOUD_HEIGHT="Alçada del reproductor" +ACF_SOUNDCLOUD_HEIGHT_DESC="L'alçada del reproductor en píxels.

Pots utilitzar un percentatge afegint el signe % al final" +ACF_SOUNDCLOUD_AUTOPLAY="Reproducció automàtica" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Estableix si vols que la pista es reprodueixi automàticament en carregar la pàgina" +ACF_SOUNDCLOUD_HIDERELATED="Amagar relacionats" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Estableix si vols amagar les pistes relacionades del reproductor" +ACF_SOUNDCLOUD_SHOWCOMMENTS="mostrar comentaris" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Estableix si vols mostrar els comentaris a la pista" +ACF_SOUNDCLOUD_SHOWUSER="Mostrar usuari" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Estableix si mostrar el nom d'usuari que ha pujat la pista" +ACF_SOUNDCLOUD_SHOWREPOSTS="Mostrar republicacions" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Estableix si vols mostrar republicacions de la mateixa pista al reproductor" +ACF_SOUNDCLOUD_VISUAL="Utilitza incrustat visual" +ACF_SOUNDCLOUD_VISUAL_DESC="Estableix si vols el reproductor visual SoundCloud que mostra l'art de la pista" +ACF_SOUNDCLOUD_COLOR="Color de la IU" +ACF_SOUNDCLOUD_COLOR_DESC="Escull el color dels controls del reproductor i de la informació de pista" +ACF_SOUNDCLOUD_VALUE_DESC="Escriu la ID de la pista SoundCloud" diff --git a/plugins/fields/acfsoundcloud/language/da-DK/da-DK.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/da-DK/da-DK.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..8a8364a2 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/da-DK/da-DK.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Felter - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Opret et felt til let at vise en SoundCloud afspiller der afspiller dit foretrukne spor. Oveni det kan du brugerdefinere afspilleren med indstillinger såsom auto-afspil, vis/skjul kommentarer, vis/skjul uploaderens info og mere." +ACF_SOUNDCLOUD_WIDTH="Afspillerbredde" +ACF_SOUNDCLOUD_WIDTH_DESC="Afspillerens bredde i pixels.

Hvis du foretrækker en procentsats, så tilføj et procentegn % i slutningen" +ACF_SOUNDCLOUD_HEIGHT="Højde på afspillerHeight" +ACF_SOUNDCLOUD_HEIGHT_DESC="Afspillerens højde i pixels.

Hvis du foretrækker en procentsats, så tilføj et procenttegn % i slutningen" +ACF_SOUNDCLOUD_AUTOPLAY="Autoafspil" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="OM du ønsker at sporet skal starte afspilning når siden indlæsesyou want the track to start playing once the page loads" +ACF_SOUNDCLOUD_HIDERELATED="Skjul relateret" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Om du ønsker at skjule relaterede tracks fra afspilleren" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Vis kommentarer" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Om du ønsker at vise kommentarer på sporet" +ACF_SOUNDCLOUD_SHOWUSER="Vis bruger" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Om du ønsker at vise uploaderens brugernavn" +ACF_SOUNDCLOUD_SHOWREPOSTS="Vis rapporter" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Om du ønsker at vise genopslag af det samme track i afspillerenin the player" +ACF_SOUNDCLOUD_VISUAL="Anvend visuel indlejring" +ACF_SOUNDCLOUD_VISUAL_DESC="Om du ønsker SoundCloud's visuelle afspiller med en prominent visning af sporets kunst" +ACF_SOUNDCLOUD_COLOR="UI farve" +ACF_SOUNDCLOUD_COLOR_DESC="Vælg farven på afspillerens knapper og sporinfothe color of the player's controls and track info" +ACF_SOUNDCLOUD_VALUE_DESC="Angiv SoundCloud track ID'et" diff --git a/plugins/fields/acfsoundcloud/language/de-DE/de-DE.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/de-DE/de-DE.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..d1c90d13 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/de-DE/de-DE.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Felder - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Erstellen Sie ein Feld, um auf einfache Weise einen SoundCloud-Player anzuzeigen, der den gewünschten Titel wiedergibt. Darüber hinaus können Sie den Player mit Optionen wie automatischer Wiedergabe, Ein- / Ausblenden der Kommentare, Ein- / Ausblenden der Informationen des Uploaders und mehr anpassen." +ACF_SOUNDCLOUD_WIDTH="Spielerbreite" +ACF_SOUNDCLOUD_WIDTH_DESC="Die Breite des Players in Pixel.

Wenn Sie Prozent bevorzugen, fügen Sie das Prozentzeichen% am Ende hinzu." +ACF_SOUNDCLOUD_HEIGHT="Spielergröße" +ACF_SOUNDCLOUD_HEIGHT_DESC="Die Größe des Players in Pixel.

Wenn Sie den Prozentsatz bevorzugen, fügen Sie das Prozentzeichen% am Ende hinzu." +ACF_SOUNDCLOUD_AUTOPLAY="Autoplay" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Soll der Titel nach dem Laden der Seite abgespielt werden?" +ACF_SOUNDCLOUD_HIDERELATED="Verwandte ausblenden" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Möchten Sie die zugehörigen Titel vor dem Player verbergen?" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Kommentare anzeigen" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Möchten Sie die Kommentare zum Track anzeigen?" +ACF_SOUNDCLOUD_SHOWUSER="Benutzer anzeigen" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Möchten Sie den Benutzernamen des Uploaders anzeigen?" +ACF_SOUNDCLOUD_SHOWREPOSTS="Reposts anzeigen" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Möchten Sie Reposts desselben Titels im Player anzeigen?" +ACF_SOUNDCLOUD_VISUAL="Visual Embed verwenden" +ACF_SOUNDCLOUD_VISUAL_DESC="Möchten Sie den Visual Player von SoundCloud mit einer herausragenden Darstellung der Titelkunst?" +ACF_SOUNDCLOUD_COLOR="UI-Farbe" +ACF_SOUNDCLOUD_COLOR_DESC="Wählen Sie die Farbe der Steuerelemente und der Titelinformationen des Players." +ACF_SOUNDCLOUD_VALUE_DESC="Geben Sie die SoundCloud-Track-ID ein" diff --git a/plugins/fields/acfsoundcloud/language/en-GB/en-GB.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/en-GB/en-GB.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..0bdcd647 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/en-GB/en-GB.plg_fields_acfsoundcloud.ini @@ -0,0 +1,33 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Fields - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Enter a SoundCloud Track or Playlist ID in the back-end and display a SoundCloud Player in the front-end." +ACF_SOUNDCLOUD_WIDTH="Player Width" +ACF_SOUNDCLOUD_WIDTH_DESC="The Player's Width in pixels.

If you prefer percentage add the percentage sign % at the end" +ACF_SOUNDCLOUD_HEIGHT="Player Height" +ACF_SOUNDCLOUD_HEIGHT_DESC="The Player's Height in pixels.

If you prefer percentage add the percentage sign % at the end" +ACF_SOUNDCLOUD_AUTOPLAY="Autoplay" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Whether you want the track to start playing once the page loads" +ACF_SOUNDCLOUD_HIDERELATED="Hide Related" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Whether you want to hide the related tracks from the player" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Show Comments" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Whether you want to show the comments on the track" +ACF_SOUNDCLOUD_SHOWUSER="Show User" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Whether you want to show the uploader's username" +ACF_SOUNDCLOUD_SHOWREPOSTS="Show Reposts" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Whether you want to show reposts of the same track in the player" +ACF_SOUNDCLOUD_VISUAL="Use Visual Embed" +ACF_SOUNDCLOUD_VISUAL_DESC="Whether you want SoundCloud's Visual Player with a prominent display of the track's art" +ACF_SOUNDCLOUD_COLOR="UI Color" +ACF_SOUNDCLOUD_COLOR_DESC="Choose the color of the player's controls and track info" +ACF_SOUNDCLOUD_VALUE_DESC="Enter the SoundCloud track ID" +ACF_SOUNDCLOUD_PLAYLIST="Playlist Mode" +ACF_SOUNDCLOUD_PLAYLIST_DESC="Enable this, if the ID above concerns a Playlist instead of single Track." +ACF_SOUNDCLOUD_ID_DESC="Enter the Track or the Playlist ID" +ACF_SOUNDCLOUD_ID="SoundCloud ID" \ No newline at end of file diff --git a/plugins/fields/acfsoundcloud/language/en-GB/en-GB.plg_fields_acfsoundcloud.sys.ini b/plugins/fields/acfsoundcloud/language/en-GB/en-GB.plg_fields_acfsoundcloud.sys.ini new file mode 100644 index 00000000..4d6148c3 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/en-GB/en-GB.plg_fields_acfsoundcloud.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_SOUNDCLOUD="Fields - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Enter a SoundCloud Track or Playlist ID in the back-end and display a SoundCloud Player in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfsoundcloud/language/es-ES/es-ES.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/es-ES/es-ES.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..2fbfc54b --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/es-ES/es-ES.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Campos - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Cree un campo para mostrar fácilmente un reproductor de SoundCloud que reproduzca la pista deseada. Además de eso, puedes personalizar el reproductor con opciones como reproducción automática, mostrar/ocultar los comentarios, mostrar/ocultar la información de quien subió y más." +ACF_SOUNDCLOUD_WIDTH="Ancho del Reproductor" +ACF_SOUNDCLOUD_WIDTH_DESC="El ancho del reproductor en píxeles.

Si prefiere porcentaje agregue el signo de porcentaje % al final" +ACF_SOUNDCLOUD_HEIGHT="Alto del Reproductor" +ACF_SOUNDCLOUD_HEIGHT_DESC="El alto del reproductor en píxeles.

Si prefiere porcentaje agregue el signo de porcentaje % al final" +ACF_SOUNDCLOUD_AUTOPLAY="Auto-reproducción" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Si desea que la pista comience a reproducirse una vez que se carga la página" +ACF_SOUNDCLOUD_HIDERELATED="Ocultar Relacionado" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Si desea ocultar las pistas relacionadas del reproductor" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Mostrar Comentarios" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Si desea mostrar los comentarios en la pista" +ACF_SOUNDCLOUD_SHOWUSER="Mostrar Usuario" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Si desea mostrar el nombre de usuario de quien lo subió" +ACF_SOUNDCLOUD_SHOWREPOSTS="Mostrar Reenvíos" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Si desea mostrar reposiciones de la misma pista en el reproductor" +ACF_SOUNDCLOUD_VISUAL="Usar Inserción Visual" +ACF_SOUNDCLOUD_VISUAL_DESC="Si desea Visual Player de SoundCloud con una visualización destacada del arte de la pista" +ACF_SOUNDCLOUD_COLOR="Color de UI" +ACF_SOUNDCLOUD_COLOR_DESC="Elija el color de los controles del reproductor y la información de la pista" +ACF_SOUNDCLOUD_VALUE_DESC="Introduce el ID de la pista de SoundCloud" diff --git a/plugins/fields/acfsoundcloud/language/es-ES/es-ES.plg_fields_acfsoundcloud.sys.ini b/plugins/fields/acfsoundcloud/language/es-ES/es-ES.plg_fields_acfsoundcloud.sys.ini new file mode 100644 index 00000000..b94a19fb --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/es-ES/es-ES.plg_fields_acfsoundcloud.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_SOUNDCLOUD="Campos - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Ingrese una ID de pista o lista de reproducción de SoundCloud en el back-end y muestre un reproductor de SoundCloud en el front-end." diff --git a/plugins/fields/acfsoundcloud/language/fr-FR/fr-FR.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/fr-FR/fr-FR.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..e0e32951 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/fr-FR/fr-FR.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Champs - SoundCloud ACF" +ACF_SOUNDCLOUD_DESC="Créé un champ affichant un lecteur SoundClound qui jouera la piste désirée. De plus, vous pouvez personnaliser le lecteur avec des options telles que la lecture automatique, afficher/masquer les commentaires, afficher/masquer les informations de l'uploadeur et plus encore." +ACF_SOUNDCLOUD_WIDTH="Largeur du lecteur" +ACF_SOUNDCLOUD_WIDTH_DESC="La largeur du lecteur en pixels.

Si vous préférez un pourcentage, ajoutez le signe % à la fin" +ACF_SOUNDCLOUD_HEIGHT="Hauteur du lecteur" +ACF_SOUNDCLOUD_HEIGHT_DESC="La hauteur du lecteur en pixels.

Si vous préférez un pourcentage, ajoutez le signe % à la fin" +ACF_SOUNDCLOUD_AUTOPLAY="Lecture automatique" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Si vous souhaitez que la piste commence à se jouer une fois la page chargée" +ACF_SOUNDCLOUD_HIDERELATED="Masquer Similaires" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Si vous souhaitez masquer les pistes similaires depuis le lecteur" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Afficher commentaires" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Si vous souhaitez afficher les commentaires sur la piste" +ACF_SOUNDCLOUD_SHOWUSER="Voir utilisateur" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Si vous souhaitez afficher le nom d'utilisateur du téléverseur" +ACF_SOUNDCLOUD_SHOWREPOSTS="Afficher les reposts" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Si vous souhaitez afficher les reposts de la piste dans le lecteur" +ACF_SOUNDCLOUD_VISUAL="Utiliser l'intégration visuelle" +ACF_SOUNDCLOUD_VISUAL_DESC="Si vous souhaitez que le lecteur visuel de SoundCloud affiche en évidence la pochette de la piste" +ACF_SOUNDCLOUD_COLOR="Couleur de l'interface" +ACF_SOUNDCLOUD_COLOR_DESC="Choisissez une couleur pour les contrôles du lecteur et des infos de la piste" +ACF_SOUNDCLOUD_VALUE_DESC="Entrez l'ID de la piste SoundCloud" diff --git a/plugins/fields/acfsoundcloud/language/it-IT/it-IT.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/it-IT/it-IT.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..f4f3bbca --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/it-IT/it-IT.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Campi - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Creare un campo per visualizzare facilmente il lettore SoundCloud che riproduce la traccia desiderata. Oltre a questo, è possibile personalizzare il lettore con opzioni come la riproduzione automatica, mostrare/nascondere i commenti, mostrare/nascondere le informazioni del proprietario e altro ancora." +ACF_SOUNDCLOUD_WIDTH="Larghezza lettore" +ACF_SOUNDCLOUD_WIDTH_DESC="Larghezza del lettore in pixel.

Se prefirisci la percentuale inserisci il simobolo % alla fine. " +ACF_SOUNDCLOUD_HEIGHT="Altezza lettore" +ACF_SOUNDCLOUD_HEIGHT_DESC="Altezza del lettore in pixel.

Se prefirisci la percentuale inserisci il simbolo % alla fine." +ACF_SOUNDCLOUD_AUTOPLAY="Autoplay" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Se si desidera che la traccia cominci a riprodurre una volta che la pagina è stata caricata" +ACF_SOUNDCLOUD_HIDERELATED="Nascondi Correlati" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Se desideri nascondere le tracce correlate dal lettore" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Mostra Commenti" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Se desideri mostrare i commenti sulla traccia" +ACF_SOUNDCLOUD_SHOWUSER="Mostra Utente" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Se desideri mostrare il nome utente di chi ha caricato il file" +ACF_SOUNDCLOUD_SHOWREPOSTS="Mostra ripubblicazioni" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Se desideri mostrare i post della stessa traccia nel lettore" +ACF_SOUNDCLOUD_VISUAL="Usa incorporamento visuale" +ACF_SOUNDCLOUD_VISUAL_DESC="Se desideri utilizzare il lettore grafico di SoundCloud al posto della visualizzazione classica." +ACF_SOUNDCLOUD_COLOR="Colore interfaccia" +ACF_SOUNDCLOUD_COLOR_DESC="Scegliere il colore dei controlli del lettore e le informazioni della traccia." +ACF_SOUNDCLOUD_VALUE_DESC="Inserisci l'ID della traccia di SoundCloud" diff --git a/plugins/fields/acfsoundcloud/language/nl-NL/nl-NL.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/nl-NL/nl-NL.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..47c60daf --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/nl-NL/nl-NL.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Velden - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Maak een veld om eenvoudig een SoundCloud-speler weer te geven die het gewenste nummer afspeelt. Bovendien kun je de speler aanpassen met opties zoals automatisch afspelen, de opmerkingen tonen / verbergen, de informatie van de uploader tonen / verbergen en meer." +ACF_SOUNDCLOUD_WIDTH="Speler breedte" +ACF_SOUNDCLOUD_WIDTH_DESC="De breedte van de speler in pixels.

Als liever percentage gebruikt, voeg dan het procentteken% aan het einde toe" +ACF_SOUNDCLOUD_HEIGHT="Speler Hoogte" +ACF_SOUNDCLOUD_HEIGHT_DESC="De hoogte van de speler in pixels. Als liever een percentage gebruikt, voeg dan het einde het procentteken% toe" +ACF_SOUNDCLOUD_AUTOPLAY="Automatisch Afspelen" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Kies of je wilt dat de track zodra de pagina is geladen begint te spelen" +ACF_SOUNDCLOUD_HIDERELATED="Verberg gerelateerde" +ACF_SOUNDCLOUD_HIDERELATED_DESC="KIes of je de gerelateerde tracks in de speler wilt verbergen" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Toon Commentaren" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Kies of je reacties op de track wilt laten zien" +ACF_SOUNDCLOUD_SHOWUSER="Toon Gebruiker" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Kies of de gebruikersnaam van de uploaders wilt tonen" +ACF_SOUNDCLOUD_SHOWREPOSTS="Toon Reposts" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Kies of je reposts van hetzelfde nummer in de speler wilt tonen" +ACF_SOUNDCLOUD_VISUAL="Gebruik Visuele Embed" +ACF_SOUNDCLOUD_VISUAL_DESC="Kies of je SoundCloud Visual Player wilt met een prominente weergave van de track's art" +ACF_SOUNDCLOUD_COLOR="UI-kleur" +ACF_SOUNDCLOUD_COLOR_DESC="Kies de kleur van de bedieningselementen van de speler en trackinformatie" +ACF_SOUNDCLOUD_VALUE_DESC="Vul het SoundCloud Track-ID in " diff --git a/plugins/fields/acfsoundcloud/language/ru-RU/ru-RU.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/ru-RU/ru-RU.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..918a3fd8 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/ru-RU/ru-RU.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Поля - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Создайте поле для удобного отображения проигрывателя SoundCloud, воспроизводящего желаемую дорожку. Кроме того, вы можете настроить плеер с такими параметрами, как автозапуск, показать / скрыть комментарии, показать / скрыть информацию о загрузчике и многое другое." +ACF_SOUNDCLOUD_WIDTH="Ширина игрока" +ACF_SOUNDCLOUD_WIDTH_DESC="Ширина проигрывателя в пикселях.

Если вы предпочитаете процент, добавьте знак процента в конце" +ACF_SOUNDCLOUD_HEIGHT="Высота игрока" +ACF_SOUNDCLOUD_HEIGHT_DESC="Высота игрока в пикселях.

Если вы предпочитаете процент, добавьте знак процента в конце" +ACF_SOUNDCLOUD_AUTOPLAY="Автозапуск" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Хотите ли вы, чтобы дорожка начала воспроизводиться после загрузки страницы" +ACF_SOUNDCLOUD_HIDERELATED="Скрыть похожие" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Хотите ли вы скрыть связанные треки от игрока" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Показать комментарии" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Хотите ли вы показать комментарии на треке" +ACF_SOUNDCLOUD_SHOWUSER="Показать пользователя" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Хотите ли вы показать имя пользователя, добавившего видео" +ACF_SOUNDCLOUD_SHOWREPOSTS="Показать репосты" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Хотите ли вы показать репосты того же трека в плеере" +ACF_SOUNDCLOUD_VISUAL="Использовать Visual Embed" +ACF_SOUNDCLOUD_VISUAL_DESC="Хотите ли вы визуальный проигрыватель SoundCloud с ярким отображением искусства дорожки" +ACF_SOUNDCLOUD_COLOR="Цвет интерфейса" +ACF_SOUNDCLOUD_COLOR_DESC="Выберите цвет элементов управления игрока и информацию о треке" +ACF_SOUNDCLOUD_VALUE_DESC="Введите идентификатор дорожки SoundCloud" diff --git a/plugins/fields/acfsoundcloud/language/sv-SE/sv-SE.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/sv-SE/sv-SE.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..02a52bb3 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/sv-SE/sv-SE.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Fält - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Skapa ett fält för att enkelt visa en SoundCloud Player som spelar önskat spår. Utöver det kan du anpassa spelaren med alternativ som autoplay, visa / dölja kommentarerna, visa / dölja uppladdarens information och mer." +ACF_SOUNDCLOUD_WIDTH="Spelarens bredd" +ACF_SOUNDCLOUD_WIDTH_DESC="Spelarens bredd i pixlar.

Om du föredrar procent lägg till procentsatsen% i slutet." +ACF_SOUNDCLOUD_HEIGHT="Spelarens höjg" +ACF_SOUNDCLOUD_HEIGHT_DESC="Spelarens höjd i pixlar.

Om du föredrar procent lägg till procentsatsen% i slutet." +ACF_SOUNDCLOUD_AUTOPLAY="Autoplay" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Ange om du vill att spåret ska börja spelas när sidan laddas" +ACF_SOUNDCLOUD_HIDERELATED="Dölj relaterade" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Ange om du vill dölja relaterade spår från spelaren" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Visa kommentarer" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Ange om du vill visa spårens kommentarer." +ACF_SOUNDCLOUD_SHOWUSER="Visa användare" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Ange om du vill visa uppladdarens användarnamn." +ACF_SOUNDCLOUD_SHOWREPOSTS="Visa Reposts" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Ange om du vill visa reposts av samma spår i spelaren" +ACF_SOUNDCLOUD_VISUAL="Använd visuell inbäddning" +ACF_SOUNDCLOUD_VISUAL_DESC="Ange om du vill ha SoundClouds Visual Player med en framträdande visning av spårets grafik" +ACF_SOUNDCLOUD_COLOR="UI färg" +ACF_SOUNDCLOUD_COLOR_DESC="Välj färg på spelarens kontroller och spårinformation." +ACF_SOUNDCLOUD_VALUE_DESC="Ange the SoundCloud track ID" diff --git a/plugins/fields/acfsoundcloud/language/uk-UA/uk-UA.plg_fields_acfsoundcloud.ini b/plugins/fields/acfsoundcloud/language/uk-UA/uk-UA.plg_fields_acfsoundcloud.ini new file mode 100644 index 00000000..61eb5f56 --- /dev/null +++ b/plugins/fields/acfsoundcloud/language/uk-UA/uk-UA.plg_fields_acfsoundcloud.ini @@ -0,0 +1,29 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFSOUNDCLOUD_LABEL="ACF - SoundCloud" +ACF_SOUNDCLOUD="Поля - ACF SoundCloud" +ACF_SOUNDCLOUD_DESC="Створіть поле, щоб легко відобразити програвач SoundCloud Player, який відтворює потрібний трек. Крім цього, ви можете налаштувати плеєр з такими параметрами, як автоматичне відтворення, показ / приховання коментарів, показ / приховування інформації про завантажувача тощо"_QQ_"" +ACF_SOUNDCLOUD_WIDTH="Ширина програвача" +ACF_SOUNDCLOUD_WIDTH_DESC="Ширина програвача в пікселях.

Якщо ви віддаєте перевагу відсотку, додайте знак відсотка% в кінці" +ACF_SOUNDCLOUD_HEIGHT="Висота гравця" +ACF_SOUNDCLOUD_HEIGHT_DESC="Висота гравця в пікселях.

Якщо ви віддаєте перевагу відсотку, додайте знак відсотка% в кінці" +ACF_SOUNDCLOUD_AUTOPLAY="Автовідтворення" +ACF_SOUNDCLOUD_AUTOPLAY_DESC="Ви хочете, щоб композиція починала відтворюватися після завантаження сторінки" +ACF_SOUNDCLOUD_HIDERELATED="Сховати пов'язане" +ACF_SOUNDCLOUD_HIDERELATED_DESC="Чи хочете ви приховати відповідні треки від програвача" +ACF_SOUNDCLOUD_SHOWCOMMENTS="Показати коментарі" +ACF_SOUNDCLOUD_SHOWCOMMENTS_DESC="Ви хочете показати коментарі до треку" +ACF_SOUNDCLOUD_SHOWUSER="Показати користувача" +ACF_SOUNDCLOUD_SHOWUSER_DESC="Ви хочете показати ім'я користувача завантажувача" +ACF_SOUNDCLOUD_SHOWREPOSTS="Показати записи" +ACF_SOUNDCLOUD_SHOWREPOSTS_DESC="Чи бажаєте ви показувати репости одного і того ж треку в програвачі" +ACF_SOUNDCLOUD_VISUAL="Використовувати візуальне вставлення" +ACF_SOUNDCLOUD_VISUAL_DESC="Чи хочете ви, щоб визуальний плеєр SoundCloud мав видатний показ мистецтва треку" +ACF_SOUNDCLOUD_COLOR="Колір інтерфейсу" +ACF_SOUNDCLOUD_COLOR_DESC="Виберіть колір керування програвача та інформацію про трек" +ACF_SOUNDCLOUD_VALUE_DESC="Введіть ідентифікатор доріжки SoundCloud" diff --git a/plugins/fields/acfsoundcloud/params/acfsoundcloud.xml b/plugins/fields/acfsoundcloud/params/acfsoundcloud.xml new file mode 100644 index 00000000..7dfc2174 --- /dev/null +++ b/plugins/fields/acfsoundcloud/params/acfsoundcloud.xml @@ -0,0 +1,51 @@ + +
+ +
+ + + + + + + + + + + + +
+
+
diff --git a/plugins/fields/acfsoundcloud/script.install.helper.php b/plugins/fields/acfsoundcloud/script.install.helper.php new file mode 100644 index 00000000..c44821e3 --- /dev/null +++ b/plugins/fields/acfsoundcloud/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfsoundcloudInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfsoundcloud/script.install.php b/plugins/fields/acfsoundcloud/script.install.php new file mode 100644 index 00000000..344bfd1b --- /dev/null +++ b/plugins/fields/acfsoundcloud/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFSoundCloudInstallerScript extends PlgFieldsACFSoundCloudInstallerScriptHelper +{ + public $alias = 'acfsoundcloud'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acfsoundcloud/tmpl/acfsoundcloud.php b/plugins/fields/acfsoundcloud/tmpl/acfsoundcloud.php new file mode 100644 index 00000000..13035647 --- /dev/null +++ b/plugins/fields/acfsoundcloud/tmpl/acfsoundcloud.php @@ -0,0 +1,70 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +if (!$field->value) +{ + return; +} + +// Support old value +if (is_numeric($field->value)) +{ + $value = (object) [ + 'id' => $field->value, + 'playlist' => false + ]; +} +else if (is_string($field->value)) +{ + $value = json_decode($field->value); +} +else +{ + $value = (object) $field->value; +} + +if (empty($value->id)) +{ + return; +} + +// Setup Variables +$width = $fieldParams->get('width', '100%'); +$height = $fieldParams->get('height', '166'); +$mode = (bool) $value->playlist ? 'playlists' : 'tracks'; +$query = $value->id; + + +$autoplay = ($fieldParams->get('autoplay', 0)) ? 'true' : 'false'; +$hideRelated = ($fieldParams->get('hideRelated', 0)) ? 'true' : 'false'; +$showComments = ($fieldParams->get('showComments', 1)) ? 'true' : 'false'; +$showUser = ($fieldParams->get('showUser', 1)) ? 'true' : 'false'; +$showReposts = ($fieldParams->get('showReposts', 0)) ? 'true' : 'false'; +$visual = ($fieldParams->get('visual', 0)) ? 'true' : 'false'; +$color = substr($fieldParams->get('color', '#00cc11'), 1); +$query = $value->id . '&auto_play=' . $autoplay . '&hide_related=' . $hideRelated . '&show_comments=' . $showComments . '&show_user=' . $showUser . '&show_reposts=' . $showReposts . '&visual=' . $visual . '&color=' . $color; + + +// Output +$buffer = ' + +'; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfsoundcloud/version.php b/plugins/fields/acfsoundcloud/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfsoundcloud/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acftelephone/acftelephone.php b/plugins/fields/acftelephone/acftelephone.php new file mode 100644 index 00000000..146d98fd --- /dev/null +++ b/plugins/fields/acftelephone/acftelephone.php @@ -0,0 +1,236 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; +use NRFramework\Countries; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFTelephone extends ACF_Field +{ + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = '+123 456 789'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + $value = $option->getValue(); + + if (is_string($value) && json_decode($value, true)) + { + $value = json_decode($value, true); + } + + if (is_array($value)) + { + $countryCode = isset($value['code']) ? $value['code'] : ''; + $phoneNumber = isset($value['value']) ? $value['value'] : ''; + + if ($phoneNumber) + { + $calling_code = Countries::getCallingCodeByCountryCode($countryCode); + $calling_code = $calling_code !== '' ? '+' . $calling_code : ''; + + $value = $calling_code . $phoneNumber; + } + else + { + $value = ''; + } + } + + $option->setLabel($value); + } + + return $options; + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $inputmask = $field->fieldparams->get('tel_mask', ''); + + // Set custom class and type + $fieldNode->setAttribute('class', $this->class); + $fieldNode->setAttribute('type', 'tftel'); + + // Get the Input Mask entered on Field settings + $fieldNode->setAttribute('inputmask', $inputmask); + + $fieldValue = isset($field->value) ? $field->value : $field->default_value; + $fieldValue = json_decode($fieldValue, true) ? json_decode($fieldValue, true) : $fieldValue; + + $countryCodeSelectorEnabled = $field->fieldparams->get('enable_country_selector', '0') === '1'; + + + /** + * This is required the first the field has enabled Country Code Selector, + * and the field was previously just a phone number (saved as string -- without having selected a country code). + * + * We make it required so that the user is forced to select a country code. + */ + if ($countryCodeSelectorEnabled && is_scalar($fieldValue) && !empty($fieldValue)) + { + $fieldNode->setAttribute('required', true); + } + + + $fieldValue = is_array($fieldValue) && isset($fieldValue['value']) ? $fieldValue['value'] : $fieldValue; + + + if ($countryCodeSelectorEnabled) + { + $fieldNode->setAttribute('type', 'TFPhoneControl'); + $fieldNode->setAttribute('class', $this->class . ' form-control'); + + $default_country_option = $field->fieldparams->get('default_country_option', 'detect'); + + $code = ''; + + switch ($default_country_option) + { + case 'custom': + $code = $field->fieldparams->get('default_country_custom', ''); + break; + + case 'detect': + $code = \NRFramework\Helpers\Geo::getVisitorCountryCode(); + break; + + default: + $code = $field->fieldparams->get('default_country', ''); + break; + } + + $fieldNode->setAttribute('default', json_encode([ + 'code' => $code ? strtoupper($code) : '', + 'value' => $fieldValue + ])); + } + + + // Load input mask script + if ($inputmask) + { + if (!$countryCodeSelectorEnabled) + { + $fieldNode->setAttribute('class', 'acf-input-mask'); + } + + $fieldNode->setAttribute('input_class', 'acf-input-mask'); + $fieldNode->setAttribute('data-imask', $inputmask); + + HTMLHelper::script('plg_system_nrframework/vendor/inputmask.min.js', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_fields_acftelephone/script.js', ['relative' => true, 'version' => 'auto']); + } + + return $fieldNode; + } + + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + // Check if the field should be processed by us + if (!$this->isTypeSupported($field->type)) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + $value = $field->value; + + if (is_string($value) && json_decode($value, true)) + { + $value = json_decode($value, true); + } + + if (is_array($value)) + { + $countryCode = isset($value['code']) ? $value['code'] : ''; + $phoneNumber = isset($value['value']) ? $value['value'] : ''; + + if ($phoneNumber) + { + $calling_code = Countries::getCallingCodeByCountryCode($countryCode); + $calling_code = $calling_code !== '' ? '+' . $calling_code : ''; + + $value = $calling_code . $phoneNumber; + } + else + { + $value = ''; + } + } + + $field->value = $value; + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + +} \ No newline at end of file diff --git a/plugins/fields/acftelephone/acftelephone.xml b/plugins/fields/acftelephone/acftelephone.xml new file mode 100644 index 00000000..4133adc0 --- /dev/null +++ b/plugins/fields/acftelephone/acftelephone.xml @@ -0,0 +1,24 @@ + + + ACF_TELEPHONE + ACF_TELEPHONE_DESC + Tassos Marinos + October 2018 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acftelephone.php + script.install.helper.php + version.php + language + params + tmpl + + + js + + diff --git a/plugins/fields/acftelephone/language/en-GB/en-GB.plg_fields_acftelephone.ini b/plugins/fields/acftelephone/language/en-GB/en-GB.plg_fields_acftelephone.ini new file mode 100644 index 00000000..1d6075cb --- /dev/null +++ b/plugins/fields/acftelephone/language/en-GB/en-GB.plg_fields_acftelephone.ini @@ -0,0 +1,26 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTELEPHONE_LABEL="ACF - Telephone" +ACF_TELEPHONE="Fields - ACF Telephone" +ACF_TELEPHONE_DESC="Ensure a valid telephone number in a predefined format in the back-end and display it in the front-end." +ACF_TELEPHONE_VALUE_DESC="Enter a Telephone Number." +ACF_TELEPHONE_MASK="Telephone Mask" +ACF_TELEPHONE_MASK_DESC="Help user with the input by ensuring a predefined format.

Syntax:
9: Numeric (0-9)
a: Alphabetical (a-z or A-Z)
A: Uppercase alphabetical (A-Z)
*: Alphanumeric (0-9, a-z, or A-Z)
&: Uppercase alphanumeric (0-9 or A-Z)

Examples:
Phone: +1 (999)-9999
Date: 99/99/9999
Zip Code: 99999?-9999" +ACF_TELEPHONE_CLICK_TO_CALL="Click-to-Call Link" +ACF_TELEPHONE_CLICK_TO_CALL_DESC="Enable to create a clickable telephone link for your users." +ACF_TELEPHONE_DISPLAY_COUNTRY_CODE_SELECTOR="Display Country Code Selector" +ACF_TELEPHONE_DISPLAY_COUNTRY_CODE_SELECTOR_DESC="Enable to display a country code selector where users can select their country and display its calling code." +ACF_TELEPHONE_COUNTRY_CODE="Country Code" +ACF_TELEPHONE_COUNTRY_CODE_DESC="Enter a 2-letter ISO 3166 country code. Eg: GR or US" +ACF_TELEPHONE_DEFAULT_COUNTRY_CODE="Default Country Code" +ACF_TELEPHONE_DEFAULT_COUNTRY_CODE_DESC="Select whether the default country code will be automatically detected by the visitor's location or a custom country will be used.

Detect Visitor Country requires the TGeoIP plugin to be installed." +ACF_TELEPHONE_SELECT_COUNTRY="Select Country" +ACF_TELEPHONE_SELECT_COUNTRY_DESC="Select which will be the default country on the country code selector." +ACF_TELEPHONE_SET_COUNTRY_CODE="Set Country Code" +ACF_TELEPHONE_FIELD_COUNTRY_DETECT="Detect Visitor Country" +ACF_TELEPHONE_FIELD_COUNTRY_DETECT_DESC="If enabled, the field will try to detect and prefill the visitor's country. Requires the TGeoIP plugin to be installed." \ No newline at end of file diff --git a/plugins/fields/acftelephone/language/en-GB/en-GB.plg_fields_acftelephone.sys.ini b/plugins/fields/acftelephone/language/en-GB/en-GB.plg_fields_acftelephone.sys.ini new file mode 100644 index 00000000..94a6b6ed --- /dev/null +++ b/plugins/fields/acftelephone/language/en-GB/en-GB.plg_fields_acftelephone.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TELEPHONE="Fields - ACF Telephone" +ACF_TELEPHONE_DESC="Ensure a valid telephone number in a predefined format in the back-end and display it in the front-end." \ No newline at end of file diff --git a/plugins/fields/acftelephone/language/es-ES/es-ES.plg_fields_acftelephone.ini b/plugins/fields/acftelephone/language/es-ES/es-ES.plg_fields_acftelephone.ini new file mode 100644 index 00000000..cb562432 --- /dev/null +++ b/plugins/fields/acftelephone/language/es-ES/es-ES.plg_fields_acftelephone.ini @@ -0,0 +1,26 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTELEPHONE_LABEL="ACF - Teléfono" +ACF_TELEPHONE="Campos - ACF Teléfono" +ACF_TELEPHONE_DESC="Asegure un número de teléfono válido en un formato predefinido en el back-end y muéstrelo en el front-end." +ACF_TELEPHONE_VALUE_DESC="Introduzca un Número de Teléfono" +ACF_TELEPHONE_MASK="Máscara de Teléfono" +ACF_TELEPHONE_MASK_DESC="Ayude al usuario con la entrada asegurando un formato predefinido.

Sintaxis:
9: Numérico (0-9)
a: Alfabético (a-z o A-Z)
A: Mayúsculas alfabéticas (A-Z)
*: Alfanumérico (0-9, a-z, o A-Z)
&: Mayúsculas alfanuméricas (0-9 o A-Z)

Ejemplos:
Teléfono: +1 (999)-9999
Fecha: 99/99/9999
Código Postal: 99999?-9999" +ACF_TELEPHONE_CLICK_TO_CALL="Enlace de Clic para llamar" +ACF_TELEPHONE_CLICK_TO_CALL_DESC="Habilite esta opción para crear un enlace telefónico en el que se pueda hacer clic para sus usuarios." +ACF_TELEPHONE_DISPLAY_COUNTRY_CODE_SELECTOR="Mostrar selector de código de país" +ACF_TELEPHONE_DISPLAY_COUNTRY_CODE_SELECTOR_DESC="Habilite para mostrar un selector de código de país donde los usuarios pueden seleccionar su país y mostrar su código de llamada." +ACF_TELEPHONE_COUNTRY_CODE="Código de país" +ACF_TELEPHONE_COUNTRY_CODE_DESC="Ingrese un código de país ISO 3166 de 2 letras. Por ejemplo: GR, US o ES" +ACF_TELEPHONE_DEFAULT_COUNTRY_CODE="Código de país predeterminado" +ACF_TELEPHONE_DEFAULT_COUNTRY_CODE_DESC="Seleccione si el código de país predeterminado será detectado automáticamente por la ubicación del visitante o si se utilizará un país personalizado.

Detectar país del visitante requiere que esté instalado el complemento TGeoIP." +ACF_TELEPHONE_SELECT_COUNTRY="Seleccionar país" +ACF_TELEPHONE_SELECT_COUNTRY_DESC="Seleccione cuál será el país predeterminado en el selector de código de país." +ACF_TELEPHONE_SET_COUNTRY_CODE="Establecer código de país" +ACF_TELEPHONE_FIELD_COUNTRY_DETECT="Detectar país del visitante" +ACF_TELEPHONE_FIELD_COUNTRY_DETECT_DESC="Si está habilitado, el campo intentará detectar y completar previamente el país del visitante. Requiere la instalación del complemento TGeoIP." diff --git a/plugins/fields/acftelephone/language/es-ES/es-ES.plg_fields_acftelephone.sys.ini b/plugins/fields/acftelephone/language/es-ES/es-ES.plg_fields_acftelephone.sys.ini new file mode 100644 index 00000000..3b9081ec --- /dev/null +++ b/plugins/fields/acftelephone/language/es-ES/es-ES.plg_fields_acftelephone.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TELEPHONE="Campos - ACF Teléfono" +ACF_TELEPHONE_DESC="Asegure un número de teléfono válido en un formato predefinido en el back-end y muéstrelo en el front-end." diff --git a/plugins/fields/acftelephone/params/acftelephone.xml b/plugins/fields/acftelephone/params/acftelephone.xml new file mode 100644 index 00000000..6da0a05a --- /dev/null +++ b/plugins/fields/acftelephone/params/acftelephone.xml @@ -0,0 +1,45 @@ + +
+ +
+ + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acftelephone/script.install.helper.php b/plugins/fields/acftelephone/script.install.helper.php new file mode 100644 index 00000000..96643dbe --- /dev/null +++ b/plugins/fields/acftelephone/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcftelephoneInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acftelephone/script.install.php b/plugins/fields/acftelephone/script.install.php new file mode 100644 index 00000000..35d75d7b --- /dev/null +++ b/plugins/fields/acftelephone/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFTelephoneInstallerScript extends PlgFieldsACFTelephoneInstallerScriptHelper +{ + public $alias = 'acftelephone'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acftelephone/tmpl/acftelephone.php b/plugins/fields/acftelephone/tmpl/acftelephone.php new file mode 100644 index 00000000..b7891c05 --- /dev/null +++ b/plugins/fields/acftelephone/tmpl/acftelephone.php @@ -0,0 +1,36 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$telephone = htmlentities($field->value, ENT_COMPAT, 'UTF-8')) +{ + return; +} + +// Remove underscores +$telephone = str_replace('_', '', $telephone); + +$click_to_call = (bool) $fieldParams->get('click_to_call', true); + +$buffer = $telephone; + +// Output +if ($click_to_call) +{ + // Remove hyphens + $telephoneCode = str_replace('-', '', $telephone); + + $buffer = '' . $telephone . ''; +} + +echo $buffer; diff --git a/plugins/fields/acftelephone/version.php b/plugins/fields/acftelephone/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acftelephone/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acftimepicker/acftimepicker.php b/plugins/fields/acftimepicker/acftimepicker.php new file mode 100644 index 00000000..684f3846 --- /dev/null +++ b/plugins/fields/acftimepicker/acftimepicker.php @@ -0,0 +1,64 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFTimepicker extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'NR_Time'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param JForm $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + HTMLHelper::script('plg_fields_acftimepicker/acftimepicker.js', ['relative' => true, 'version' => 'auto']); + + return $fieldNode; + } +} diff --git a/plugins/fields/acftimepicker/acftimepicker.xml b/plugins/fields/acftimepicker/acftimepicker.xml new file mode 100644 index 00000000..8e116a54 --- /dev/null +++ b/plugins/fields/acftimepicker/acftimepicker.xml @@ -0,0 +1,24 @@ + + + ACF_TIMEPICKER + ACF_TIMEPICKER_DESC + Tassos Marinos + June 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acftimepicker.php + script.install.helper.php + version.php + params + tmpl + language + + + js + + diff --git a/plugins/fields/acftimepicker/language/ca-ES/ca-ES.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/ca-ES/ca-ES.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..a18647f9 --- /dev/null +++ b/plugins/fields/acftimepicker/language/ca-ES/ca-ES.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Selector horari" +ACF_TIMEPICKER="Camps - ACF Selector horari" +ACF_TIMEPICKER_DESC="Crear un selector horari per escollir l'hora fàcilment." +ACF_TIMEPICKER_TIMEFORMAT="Format d'hora" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Escull el format d'hora que t'agradaria veure a la part píblica. Utilitza els caràcters del format d'hora PHP." +ACF_TIMEPICKER_VALUE_DESC="Escull l'hora amb el selector horari." diff --git a/plugins/fields/acftimepicker/language/da-DK/da-DK.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/da-DK/da-DK.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..557f4264 --- /dev/null +++ b/plugins/fields/acftimepicker/language/da-DK/da-DK.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Tidsvælger" +ACF_TIMEPICKER="Felter - ACF Tidsvælger" +ACF_TIMEPICKER_DESC="Opret et tidsvælgerfelt til let at vise tiden." +ACF_TIMEPICKER_TIMEFORMAT="Tidsformat" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Vælg tidsformatet som du ønsker at se i frontend. Det anvender PHP's tidsformat karakterer." +ACF_TIMEPICKER_VALUE_DESC="Vælg tidspunkt fra tidsvælgeren." diff --git a/plugins/fields/acftimepicker/language/de-DE/de-DE.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/de-DE/de-DE.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..b4299e16 --- /dev/null +++ b/plugins/fields/acftimepicker/language/de-DE/de-DE.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Zeitmesser" +ACF_TIMEPICKER="Felder - ACF Zeitmesser" +ACF_TIMEPICKER_DESC="Erstellen Sie ein Zeitmesser-Feld, um die Zeit einfach zu wählen." +ACF_TIMEPICKER_TIMEFORMAT="Zeitformat" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Wählen Sie das Zeitformat, das im Frontend angezeigt werden soll. Es verwendet die Zeichen des PHP-Zeitformats." +ACF_TIMEPICKER_VALUE_DESC="Zeit aus dem Zeitmesser auswählen." diff --git a/plugins/fields/acftimepicker/language/en-GB/en-GB.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/en-GB/en-GB.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..7b9959a9 --- /dev/null +++ b/plugins/fields/acftimepicker/language/en-GB/en-GB.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Timepicker" +ACF_TIMEPICKER="Fields - ACF Timepicker" +ACF_TIMEPICKER_DESC="Choose a time from an interactive time picker in the back-end and display the selected time in the front-end." +ACF_TIMEPICKER_TIMEFORMAT="Time Format" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Choose the time format you'd like to see on the frontend. It uses PHP's time format characters." +ACF_TIMEPICKER_VALUE_DESC="Select time from the timepicker." \ No newline at end of file diff --git a/plugins/fields/acftimepicker/language/en-GB/en-GB.plg_fields_acftimepicker.sys.ini b/plugins/fields/acftimepicker/language/en-GB/en-GB.plg_fields_acftimepicker.sys.ini new file mode 100644 index 00000000..bdd176d7 --- /dev/null +++ b/plugins/fields/acftimepicker/language/en-GB/en-GB.plg_fields_acftimepicker.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TIMEPICKER="Fields - ACF Timepicker" +ACF_TIMEPICKER_DESC="Choose a time from an interactive time picker in the back-end and display the selected time in the front-end." \ No newline at end of file diff --git a/plugins/fields/acftimepicker/language/es-ES/es-ES.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/es-ES/es-ES.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..cbddd11d --- /dev/null +++ b/plugins/fields/acftimepicker/language/es-ES/es-ES.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Selector de tiempo" +ACF_TIMEPICKER="Campos - Selector de tiempo de ACF" +ACF_TIMEPICKER_DESC="Cree un campo de selección de tiempo para elegir fácilmente la hora." +ACF_TIMEPICKER_TIMEFORMAT="Formato de tiempo" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Elija el formato de hora que le gustaría ver en la interfaz. Utilice los caracteres de formato de hora de PHP." +ACF_TIMEPICKER_VALUE_DESC="Seleccione el tiempo del selector de tiempo." diff --git a/plugins/fields/acftimepicker/language/es-ES/es-ES.plg_fields_acftimepicker.sys.ini b/plugins/fields/acftimepicker/language/es-ES/es-ES.plg_fields_acftimepicker.sys.ini new file mode 100644 index 00000000..80e3948d --- /dev/null +++ b/plugins/fields/acftimepicker/language/es-ES/es-ES.plg_fields_acftimepicker.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TIMEPICKER="Campos - ACF Timepicker" +ACF_TIMEPICKER_DESC="Elija un tiempo de un selector de tiempo interactivo en el back-end y muestre el tiempo seleccionado en el front-end." diff --git a/plugins/fields/acftimepicker/language/fr-FR/fr-FR.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/fr-FR/fr-FR.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..1edfea76 --- /dev/null +++ b/plugins/fields/acftimepicker/language/fr-FR/fr-FR.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Minuteur" +ACF_TIMEPICKER="Champs - Minuteur ACF" +ACF_TIMEPICKER_DESC="Créez un champ minuteur pour choisir facilement une durée." +ACF_TIMEPICKER_TIMEFORMAT="Format du temps" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Choisissez le format de l'heure que vous souhaitez voir sur le front-end. Utilisez le format temporel PHP." +ACF_TIMEPICKER_VALUE_DESC="Sélectionnez une durée depuis le sélecteur de temps" diff --git a/plugins/fields/acftimepicker/language/it-IT/it-IT.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/it-IT/it-IT.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..0abcb0cb --- /dev/null +++ b/plugins/fields/acftimepicker/language/it-IT/it-IT.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Scelta Orario" +ACF_TIMEPICKER="Fields - ACF Timepicker" +ACF_TIMEPICKER_DESC="Crea un campo per scegliere facilmente l'ora." +ACF_TIMEPICKER_TIMEFORMAT="Formato Ora" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Scegli il formato di tempo che desideri vedere sul frontend. Utilizza i caratteri di formato temporale di PHP." +ACF_TIMEPICKER_VALUE_DESC="Seleziona il tempo dal selettore" diff --git a/plugins/fields/acftimepicker/language/nl-NL/nl-NL.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/nl-NL/nl-NL.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..a3ed65ee --- /dev/null +++ b/plugins/fields/acftimepicker/language/nl-NL/nl-NL.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Timepicker" +ACF_TIMEPICKER="Velden - ACF Timepicker" +ACF_TIMEPICKER_DESC="Maak een tijdkies-veld om eenvoudig de tijd te kiezen." +ACF_TIMEPICKER_TIMEFORMAT="Tijd formaat" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Kies het tijdformaat dat je aan de frontend wilt zien. Maak gebruik van tekens in PHP-tijdformaat." +ACF_TIMEPICKER_VALUE_DESC="Selecteer tijd uit de tijdpicker." diff --git a/plugins/fields/acftimepicker/language/ru-RU/ru-RU.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/ru-RU/ru-RU.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..669f0d8d --- /dev/null +++ b/plugins/fields/acftimepicker/language/ru-RU/ru-RU.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Таймер" +ACF_TIMEPICKER="Поля - ACF Таймер" +ACF_TIMEPICKER_DESC="Создать поле для выбора времени, чтобы легко выбрать время." +ACF_TIMEPICKER_TIMEFORMAT="Формат времени" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Выберите формат времени, который вы хотели бы видеть на веб-интерфейсе. Он использует символы формата времени PHP." +ACF_TIMEPICKER_VALUE_DESC="Выберите время из таймера"_QQ_"" diff --git a/plugins/fields/acftimepicker/language/sv-SE/sv-SE.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/sv-SE/sv-SE.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..a403b2cb --- /dev/null +++ b/plugins/fields/acftimepicker/language/sv-SE/sv-SE.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Tidsväljare" +ACF_TIMEPICKER="Fält - ACF Tidsväljare" +ACF_TIMEPICKER_DESC="Skapa ett tidsväljar-fält för att enkelt välja tid" +ACF_TIMEPICKER_TIMEFORMAT="Tidsformat" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Välj det tidsformat du vill använda i frontend. Fältet använder PHP: s tidsformatstecken." +ACF_TIMEPICKER_VALUE_DESC="Välj tid från tidsväljaren." diff --git a/plugins/fields/acftimepicker/language/uk-UA/uk-UA.plg_fields_acftimepicker.ini b/plugins/fields/acftimepicker/language/uk-UA/uk-UA.plg_fields_acftimepicker.ini new file mode 100644 index 00000000..4fbfd229 --- /dev/null +++ b/plugins/fields/acftimepicker/language/uk-UA/uk-UA.plg_fields_acftimepicker.ini @@ -0,0 +1,13 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTIMEPICKER_LABEL="ACF - Панель часу" +ACF_TIMEPICKER="Поля - панель часу ACF" +ACF_TIMEPICKER_DESC="Створіть поле для вибору часу, щоб легко вибрати час." +ACF_TIMEPICKER_TIMEFORMAT="Формат часу" +ACF_TIMEPICKER_TIMEFORMAT_DESC="Оберіть формат часу, який ви хочете бачити на фронті. Він використовує символи формату часу PHP." +ACF_TIMEPICKER_VALUE_DESC="Виберіть час із вибору часу." diff --git a/plugins/fields/acftimepicker/params/acftimepicker.xml b/plugins/fields/acftimepicker/params/acftimepicker.xml new file mode 100644 index 00000000..66db9694 --- /dev/null +++ b/plugins/fields/acftimepicker/params/acftimepicker.xml @@ -0,0 +1,12 @@ + +
+ +
+ +
+
+
diff --git a/plugins/fields/acftimepicker/script.install.helper.php b/plugins/fields/acftimepicker/script.install.helper.php new file mode 100644 index 00000000..59d25d9a --- /dev/null +++ b/plugins/fields/acftimepicker/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcftimepickerInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acftimepicker/script.install.php b/plugins/fields/acftimepicker/script.install.php new file mode 100644 index 00000000..814aeb3b --- /dev/null +++ b/plugins/fields/acftimepicker/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFTimepickerInstallerScript extends PlgFieldsACFTimepickerInstallerScriptHelper +{ + public $alias = 'acftimepicker'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acftimepicker/tmpl/acftimepicker.php b/plugins/fields/acftimepicker/tmpl/acftimepicker.php new file mode 100644 index 00000000..1e15e7c3 --- /dev/null +++ b/plugins/fields/acftimepicker/tmpl/acftimepicker.php @@ -0,0 +1,25 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$value = $field->value) +{ + return; +} + +if (!$time = DateTime::createFromFormat('H:i', $value)) +{ + return; +} + +echo $time->format($fieldParams->get('timeformat', 'H:i')); \ No newline at end of file diff --git a/plugins/fields/acftimepicker/version.php b/plugins/fields/acftimepicker/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acftimepicker/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acftruefalse/acftruefalse.php b/plugins/fields/acftruefalse/acftruefalse.php new file mode 100644 index 00000000..603be5d1 --- /dev/null +++ b/plugins/fields/acftruefalse/acftruefalse.php @@ -0,0 +1,83 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class PlgFieldsACFTrueFalse extends FieldsListPlugin +{ + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param JForm $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $fieldNode->setAttribute('default', $field->fieldparams->get('default', '')); + $fieldNode->setAttribute('class', 'chzn-color-state'); + $fieldNode->setAttribute('type', 'list'); + + return $fieldNode; + } + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + * + * @since 3.7.0 + */ + public function onContentPrepareForm(Joomla\CMS\Form\Form $form, $data) + { + // Make sure we are manipulating the right field. + if (isset($data->type) && ($data->type == $this->_name)) + { + $form->removeField('default_value'); + } + + return parent::onContentPrepareForm($form, $data); + } + + /** + * Returns an array of key values to put in a list from the given field. + * + * @param stdClass $field The field. + * + * @return array + * + * @since 3.7.0 + */ + public function getOptionsFromField($field) + { + return array( + '' => '', + '1' => $field->fieldparams->get('true', Text::_('JTRUE')), + '0' => $field->fieldparams->get('false', Text::_('JFALSE')) + ); + } +} diff --git a/plugins/fields/acftruefalse/acftruefalse.xml b/plugins/fields/acftruefalse/acftruefalse.xml new file mode 100644 index 00000000..90a487ba --- /dev/null +++ b/plugins/fields/acftruefalse/acftruefalse.xml @@ -0,0 +1,21 @@ + + + ACF_TRUEFALSE + ACF_TRUEFALSE_DESC + Tassos Marinos + June 2017 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acftruefalse.php + script.install.helper.php + version.php + tmpl + params + language + + diff --git a/plugins/fields/acftruefalse/language/ca-ES/ca-ES.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/ca-ES/ca-ES.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..58d4571e --- /dev/null +++ b/plugins/fields/acftruefalse/language/ca-ES/ca-ES.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Cert/Fals" +ACF_TRUEFALSE="Camps- ACF Cert/Fals" +ACF_TRUEFALSE_DESC="Crear un selector per escollir fàcilment entre cert i fals" +ACF_TRUEFALSE_DEFAULT_CHOICE="Selecció per defecte" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Escull la selecció per defecte entre cert o fals o deixa-la en blanc." +ACF_TRUEFALSE_TRUE_TEXT="Text real" +ACF_TRUEFALSE_TRUE_TEXT_DESC="El text que es mostrarà a la part pública pel valor CERT" +ACF_TRUEFALSE_FALSE_TEXT="Text fals" +ACF_TRUEFALSE_FALSE_TEXT_DESC="El text que es mostrarà a la part pública pel valor FALS" diff --git a/plugins/fields/acftruefalse/language/da-DK/da-DK.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/da-DK/da-DK.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..89113122 --- /dev/null +++ b/plugins/fields/acftruefalse/language/da-DK/da-DK.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Sand/Falsk" +ACF_TRUEFALSE="Felter - ACF Sand/falsk" +ACF_TRUEFALSE_DESC="Opret et skiftefelt til let at vælge mellem sand eller falsk" +ACF_TRUEFALSE_DEFAULT_CHOICE="Standard valg" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC=" Vælg standard valget mellem sand eller falsk eller efterlad tomt" +ACF_TRUEFALSE_TRUE_TEXT="Tekst for sand" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Teksten der skal vises i frontend for værdien SAND" +ACF_TRUEFALSE_FALSE_TEXT="Tekst for falsk" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Teksten der skal vises i frontend for værdien FALSK" diff --git a/plugins/fields/acftruefalse/language/de-DE/de-DE.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/de-DE/de-DE.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..14a06222 --- /dev/null +++ b/plugins/fields/acftruefalse/language/de-DE/de-DE.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Richtig / Falsch" +ACF_TRUEFALSE="Felder - ACF Richtig / Falsch" +ACF_TRUEFALSE_DESC="Erstellen Sie ein Umschaltfeld, um einfach zwischen\" true \"und\" false \"zu wählen." +ACF_TRUEFALSE_DEFAULT_CHOICE="Standardauswahl" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Wählen Sie die Standardauswahl zwischen\" true \"oder\" false \"oder lassen Sie das Feld leer." +ACF_TRUEFALSE_TRUE_TEXT="Text für Richtig" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Der Text, der im Frontend für den TRUE-Wert angezeigt werden soll" +ACF_TRUEFALSE_FALSE_TEXT="Text für Falsch" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Der Text, der im Frontend für den Wert FALSE angezeigt werden soll" diff --git a/plugins/fields/acftruefalse/language/en-GB/en-GB.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/en-GB/en-GB.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..103157c2 --- /dev/null +++ b/plugins/fields/acftruefalse/language/en-GB/en-GB.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - True/False" +ACF_TRUEFALSE="Fields - ACF True/False" +ACF_TRUEFALSE_DESC="Choose between True or False in the back-end and display the selected option value in the front-end." +ACF_TRUEFALSE_DEFAULT_CHOICE="Default Choice" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Choose the default choice to be between true or false or leave it blank" +ACF_TRUEFALSE_TRUE_TEXT="True Text" +ACF_TRUEFALSE_TRUE_TEXT_DESC="The text to be displayed in the frontend for the TRUE value" +ACF_TRUEFALSE_FALSE_TEXT="False Text" +ACF_TRUEFALSE_FALSE_TEXT_DESC="The text to be displayed in the frontend for the FALSE value" \ No newline at end of file diff --git a/plugins/fields/acftruefalse/language/en-GB/en-GB.plg_fields_acftruefalse.sys.ini b/plugins/fields/acftruefalse/language/en-GB/en-GB.plg_fields_acftruefalse.sys.ini new file mode 100644 index 00000000..475fc322 --- /dev/null +++ b/plugins/fields/acftruefalse/language/en-GB/en-GB.plg_fields_acftruefalse.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TRUEFALSE="Fields - ACF True/False" +ACF_TRUEFALSE_DESC="Choose between True or False in the back-end and display the selected option value in the front-end." \ No newline at end of file diff --git a/plugins/fields/acftruefalse/language/es-ES/es-ES.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/es-ES/es-ES.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..06f74461 --- /dev/null +++ b/plugins/fields/acftruefalse/language/es-ES/es-ES.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Verdadero / Falso" +ACF_TRUEFALSE="Campos - ACF Verdadero/Falso" +ACF_TRUEFALSE_DESC="Cree un campo de alternancia para elegir fácilmente entre verdadero o falso" +ACF_TRUEFALSE_DEFAULT_CHOICE="Elección Predeterminada" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Elija la opción predeterminada entre verdadero o falso o déjela en blanco" +ACF_TRUEFALSE_TRUE_TEXT="Texto Verdadero" +ACF_TRUEFALSE_TRUE_TEXT_DESC="El texto que se mostrará en la interfaz para el valor VERDADERO" +ACF_TRUEFALSE_FALSE_TEXT="Texto Falso" +ACF_TRUEFALSE_FALSE_TEXT_DESC="El texto que se mostrará en la interfaz para el valor FALSO" diff --git a/plugins/fields/acftruefalse/language/es-ES/es-ES.plg_fields_acftruefalse.sys.ini b/plugins/fields/acftruefalse/language/es-ES/es-ES.plg_fields_acftruefalse.sys.ini new file mode 100644 index 00000000..a211eb6e --- /dev/null +++ b/plugins/fields/acftruefalse/language/es-ES/es-ES.plg_fields_acftruefalse.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TRUEFALSE="Campos - ACF Verdadero/Falso" +ACF_TRUEFALSE_DESC="Elija entre Verdadero o Falso en el back-end y muestre el valor de la opción seleccionada en el front-end." diff --git a/plugins/fields/acftruefalse/language/fr-FR/fr-FR.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/fr-FR/fr-FR.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..4676098c --- /dev/null +++ b/plugins/fields/acftruefalse/language/fr-FR/fr-FR.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - True/False" +ACF_TRUEFALSE="Champs - True/False ACF" +ACF_TRUEFALSE_DESC="Créer un champ booléen pour choisir facilement entre vrai et faux" +ACF_TRUEFALSE_DEFAULT_CHOICE="Choix par défaut" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Choisissez le choix par défaut entre vrai et faux, ou laissez vide" +ACF_TRUEFALSE_TRUE_TEXT="Texte de la valeur True" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Le texte à afficher en front-end pour la valeur TRUE" +ACF_TRUEFALSE_FALSE_TEXT="Texte de la valeur False" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Le texte à afficher en front-end pour la valeur FALSE" diff --git a/plugins/fields/acftruefalse/language/it-IT/it-IT.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/it-IT/it-IT.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..d7b4dc32 --- /dev/null +++ b/plugins/fields/acftruefalse/language/it-IT/it-IT.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Vero/Falso" +ACF_TRUEFALSE="Campi - ACF Vero/falso" +ACF_TRUEFALSE_DESC="Crea un campo di selezione per scegliere facilmente tra vero e falso" +ACF_TRUEFALSE_DEFAULT_CHOICE="Scelta predefinita" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Imposta la scelta predefinita tra vero o falso o lascia vuoto" +ACF_TRUEFALSE_TRUE_TEXT="Testo vero" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Il testo da visualizzare in frontend per il valore VERO" +ACF_TRUEFALSE_FALSE_TEXT="Testo falso" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Il testo da visualizzare nel frontend per il valore FALSO" diff --git a/plugins/fields/acftruefalse/language/nl-NL/nl-NL.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/nl-NL/nl-NL.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..34aa165d --- /dev/null +++ b/plugins/fields/acftruefalse/language/nl-NL/nl-NL.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - True/False" +ACF_TRUEFALSE="Velden - ACF True/False" +ACF_TRUEFALSE_DESC="Maak een schakelveld om eenvoudig te kiezen tussen waar of onwaar" +ACF_TRUEFALSE_DEFAULT_CHOICE="Standaard Keuze" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Kies de standaardkeuze tussen waar en onwaar of laat leeg" +ACF_TRUEFALSE_TRUE_TEXT="Waar Tekst" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Sw tekst die zal worden weergegeven bij een TRUE waarde" +ACF_TRUEFALSE_FALSE_TEXT="Onwaar Tekst" +ACF_TRUEFALSE_FALSE_TEXT_DESC="De tekst die aan de frontend zal worden weergegeven bij een FALSE waarde" diff --git a/plugins/fields/acftruefalse/language/ru-RU/ru-RU.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/ru-RU/ru-RU.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..06cfbccf --- /dev/null +++ b/plugins/fields/acftruefalse/language/ru-RU/ru-RU.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Верно / Неверно" +ACF_TRUEFALSE="Поля - ACF Верно / Неверно" +ACF_TRUEFALSE_DESC="Создать поле переключения, чтобы легко выбирать между истиной или ложью" +ACF_TRUEFALSE_DEFAULT_CHOICE="Выбор по умолчанию" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Выберите вариант по умолчанию, который будет между истинным или ложным или оставьте это поле пустым" +ACF_TRUEFALSE_TRUE_TEXT="Текст при верно" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Текст, отображаемый во внешнем интерфейсе для значения TRUE" +ACF_TRUEFALSE_FALSE_TEXT="Текст при неверно" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Текст, отображаемый во внешнем интерфейсе для значения FALSE" diff --git a/plugins/fields/acftruefalse/language/sv-SE/sv-SE.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/sv-SE/sv-SE.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..fe4d58ab --- /dev/null +++ b/plugins/fields/acftruefalse/language/sv-SE/sv-SE.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - Sant/Falskt" +ACF_TRUEFALSE="Fält - ACF Sant/Falskt" +ACF_TRUEFALSE_DESC="Skapa ett fält för att enkelt växla mellan sant eller falskt" +ACF_TRUEFALSE_DEFAULT_CHOICE="Standardval" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Välj standardval att vara sant eller falskt, eller lämna fältet tomt." +ACF_TRUEFALSE_TRUE_TEXT="Sant text" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Text att visa i frontend för SANT-värdet" +ACF_TRUEFALSE_FALSE_TEXT="Falskt text" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Text att visa i frontend för FALSKT-värdet" diff --git a/plugins/fields/acftruefalse/language/uk-UA/uk-UA.plg_fields_acftruefalse.ini b/plugins/fields/acftruefalse/language/uk-UA/uk-UA.plg_fields_acftruefalse.ini new file mode 100644 index 00000000..8f41f5d6 --- /dev/null +++ b/plugins/fields/acftruefalse/language/uk-UA/uk-UA.plg_fields_acftruefalse.ini @@ -0,0 +1,16 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2016 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTRUEFALSE_LABEL="ACF - вірно / помилково" +ACF_TRUEFALSE="Поля - ACF вірно / помилково" +ACF_TRUEFALSE_DESC="Створіть поле перемикання, щоб легко вибрати між істинним або помилковим" +ACF_TRUEFALSE_DEFAULT_CHOICE="Вибір за замовчуванням" +ACF_TRUEFALSE_DEFAULT_CHOICE_DESC="Виберіть за замовчуванням вибір між істинним або неправдивим або залиште його порожнім" +ACF_TRUEFALSE_TRUE_TEXT="текст при вірно" +ACF_TRUEFALSE_TRUE_TEXT_DESC="Текст, який повинен відображатись у фронте для значення TRUE" +ACF_TRUEFALSE_FALSE_TEXT="текст при помилково" +ACF_TRUEFALSE_FALSE_TEXT_DESC="Текст, який повинен відображатися у фронтальному для значення FALSE" diff --git a/plugins/fields/acftruefalse/params/acftruefalse.xml b/plugins/fields/acftruefalse/params/acftruefalse.xml new file mode 100644 index 00000000..1afdce7e --- /dev/null +++ b/plugins/fields/acftruefalse/params/acftruefalse.xml @@ -0,0 +1,26 @@ + +
+ +
+ + + + + + + +
+
+
diff --git a/plugins/fields/acftruefalse/script.install.helper.php b/plugins/fields/acftruefalse/script.install.helper.php new file mode 100644 index 00000000..37b4f390 --- /dev/null +++ b/plugins/fields/acftruefalse/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcftruefalseInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acftruefalse/script.install.php b/plugins/fields/acftruefalse/script.install.php new file mode 100644 index 00000000..e12aebf1 --- /dev/null +++ b/plugins/fields/acftruefalse/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFTrueFalseInstallerScript extends PlgFieldsACFTrueFalseInstallerScriptHelper +{ + public $alias = 'acftruefalse'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; +} diff --git a/plugins/fields/acftruefalse/tmpl/acftruefalse.php b/plugins/fields/acftruefalse/tmpl/acftruefalse.php new file mode 100644 index 00000000..0dc6c6b3 --- /dev/null +++ b/plugins/fields/acftruefalse/tmpl/acftruefalse.php @@ -0,0 +1,24 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +$fieldValue = $field->value; + +if ($fieldValue == '') +{ + return; +} + +echo ($fieldValue) ? $fieldParams->get('true', Text::_('JTRUE')) : $fieldParams->get('false', Text::_('JFALSE')); \ No newline at end of file diff --git a/plugins/fields/acftruefalse/version.php b/plugins/fields/acftruefalse/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acftruefalse/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acftwitter/acftwitter.php b/plugins/fields/acftwitter/acftwitter.php new file mode 100644 index 00000000..383325fd --- /dev/null +++ b/plugins/fields/acftwitter/acftwitter.php @@ -0,0 +1,63 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFTwitter extends ACF_Field +{ + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'ACF_TWITTER_TWTR_HANDLE'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + $option->setLabel('@' . $option->getLabel()); + } + + return $options; + } +} diff --git a/plugins/fields/acftwitter/acftwitter.xml b/plugins/fields/acftwitter/acftwitter.xml new file mode 100644 index 00000000..856143bd --- /dev/null +++ b/plugins/fields/acftwitter/acftwitter.xml @@ -0,0 +1,21 @@ + + + ACF_TWITTER + ACF_TWITTER_DESC + Tassos Marinos + July 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acftwitter.php + script.install.helper.php + version.php + language + params + tmpl + + diff --git a/plugins/fields/acftwitter/language/ca-ES/ca-ES.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/ca-ES/ca-ES.plg_fields_acftwitter.ini new file mode 100644 index 00000000..0438b54d --- /dev/null +++ b/plugins/fields/acftwitter/language/ca-ES/ca-ES.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Camps - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Escriu un usuari Twitter.

Exemple: tassosm" +ACF_TWITTER_DESC="Aconsegueix més seguidors creant un botó de seguir i col·locant-lo al teu lloc." +ACF_TWITTER_LB="Botó gran" +ACF_TWITTER_LB_DESC="Mostra un botó seguir gran." +ACF_TWITTER_SU="Mostrar nom d'usuari" +ACF_TWITTER_SU_DESC="Mostra el teu nom d'usuari dins el botó seguir." +ACF_TWITTER_SC="Mostrar recompte" +ACF_TWITTER_SC_DESC="Mostra la quantitat de seguidors." +ACF_TWITTER_TWTR_HANDLE="Escriu un nom d'usuari Twitter" +ACF_TWITTER_WIDGET="Giny:" +ACF_TWITTER_WIDGET_DESC="Escull el giny que vols mostrar." +ACF_TWITTER_FOLLOW="Botó seguir" diff --git a/plugins/fields/acftwitter/language/da-DK/da-DK.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/da-DK/da-DK.plg_fields_acftwitter.ini new file mode 100644 index 00000000..ec1e427f --- /dev/null +++ b/plugins/fields/acftwitter/language/da-DK/da-DK.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Felter - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Angiv et twitter handle.

Eksempel: tassosm" +ACF_TWITTER_DESC="Få flere Twitter følgere ved at oprette en Følg knap og placere den på dit websted." +ACF_TWITTER_LB="Stor knap" +ACF_TWITTER_LB_DESC="Vis en stor knap for Følg knappen." +ACF_TWITTER_SU="Vis brugernavnUsername" +ACF_TWITTER_SU_DESC="Vis dit brugernavn på Følg knappen." +ACF_TWITTER_SC="Vis antal" +ACF_TWITTER_SC_DESC="Vis antallet af følgere." +ACF_TWITTER_TWTR_HANDLE="Angiv et Twitter Handle" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Vælg hvilket widget du ønsker at vise." +ACF_TWITTER_FOLLOW="Følg knap" diff --git a/plugins/fields/acftwitter/language/de-DE/de-DE.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/de-DE/de-DE.plg_fields_acftwitter.ini new file mode 100644 index 00000000..a43582c3 --- /dev/null +++ b/plugins/fields/acftwitter/language/de-DE/de-DE.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Felder - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Geben Sie ein Twitter-Handle ein.

Beispiel: Tassosm" +ACF_TWITTER_DESC="Holen Sie sich mehr Twitter-Follower, indem Sie eine Schaltfläche\" Folgen \"erstellen und auf Ihrer Website platzieren." +ACF_TWITTER_LB="Große Schaltfläche" +ACF_TWITTER_LB_DESC="Große Schaltfläche der Schaltfläche\" Folgen \"anzeigen." +ACF_TWITTER_SU="Benutzername anzeigen" +ACF_TWITTER_SU_DESC="Zeigen Sie Ihren Benutzernamen über die Schaltfläche\" Folgen \"an." +ACF_TWITTER_SC="Anzahl anzeigen" +ACF_TWITTER_SC_DESC="Anzahl der Follower anzeigen." +ACF_TWITTER_TWTR_HANDLE="Twitter-Handle eingeben" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Wählen Sie aus, welches Widget Sie anzeigen möchten." +ACF_TWITTER_FOLLOW="Schaltfläche folgen" diff --git a/plugins/fields/acftwitter/language/en-GB/en-GB.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/en-GB/en-GB.plg_fields_acftwitter.ini new file mode 100644 index 00000000..bb395d18 --- /dev/null +++ b/plugins/fields/acftwitter/language/en-GB/en-GB.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Fields - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Enter a twitter handle.

Example: tassosm" +ACF_TWITTER_DESC="Get more Twitter followers by creating a Follow button and placing it onto your site." +ACF_TWITTER_LB="Large Button" +ACF_TWITTER_LB_DESC="Display a large button of the follow button." +ACF_TWITTER_SU="Show Username" +ACF_TWITTER_SU_DESC="Show your username within the follow button." +ACF_TWITTER_SC="Show Count" +ACF_TWITTER_SC_DESC="Show count of followers." +ACF_TWITTER_TWTR_HANDLE="Enter a Twitter Handle" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Select which widget you want to display." +ACF_TWITTER_FOLLOW="Follow Button" \ No newline at end of file diff --git a/plugins/fields/acftwitter/language/en-GB/en-GB.plg_fields_acftwitter.sys.ini b/plugins/fields/acftwitter/language/en-GB/en-GB.plg_fields_acftwitter.sys.ini new file mode 100644 index 00000000..a18c823a --- /dev/null +++ b/plugins/fields/acftwitter/language/en-GB/en-GB.plg_fields_acftwitter.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TWITTER="Fields - ACF Twitter" +ACF_TWITTER_DESC="Get more Twitter followers by creating a Follow button and placing it onto your site." \ No newline at end of file diff --git a/plugins/fields/acftwitter/language/es-ES/es-ES.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/es-ES/es-ES.plg_fields_acftwitter.ini new file mode 100644 index 00000000..8d2b5c4c --- /dev/null +++ b/plugins/fields/acftwitter/language/es-ES/es-ES.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Campos - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Introduzca un identificador de Twitter.

Ejemplo: tassosm" +ACF_TWITTER_DESC="Obtenga más seguidores en Twitter creando un botón Seguir y colocándolo en su sitio." +ACF_TWITTER_LB="Botón Grande" +ACF_TWITTER_LB_DESC="Mostrar en grande el botón de seguimiento." +ACF_TWITTER_SU="Mostrar Nombre de Usuario" +ACF_TWITTER_SU_DESC="Muestre su nombre de usuario dentro del botón de seguimiento." +ACF_TWITTER_SC="Mostrar Recuento" +ACF_TWITTER_SC_DESC="Mostrar recuento de seguidores." +ACF_TWITTER_TWTR_HANDLE="Ingrese un identificador de Twitter" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Seleccione qué widget desea mostrar." +ACF_TWITTER_FOLLOW="Botón de Seguir." diff --git a/plugins/fields/acftwitter/language/es-ES/es-ES.plg_fields_acftwitter.sys.ini b/plugins/fields/acftwitter/language/es-ES/es-ES.plg_fields_acftwitter.sys.ini new file mode 100644 index 00000000..d862d911 --- /dev/null +++ b/plugins/fields/acftwitter/language/es-ES/es-ES.plg_fields_acftwitter.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_TWITTER="Campos - ACF Twitter" +ACF_TWITTER_DESC="Obtenga más seguidores en Twitter creando un botón Seguir y colocándolo en su sitio." diff --git a/plugins/fields/acftwitter/language/it-IT/it-IT.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/it-IT/it-IT.plg_fields_acftwitter.ini new file mode 100644 index 00000000..e30ecf68 --- /dev/null +++ b/plugins/fields/acftwitter/language/it-IT/it-IT.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Campi - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Inserisci un handle di Twitter.

Esempio: tassosm" +ACF_TWITTER_DESC="Ottieni più follower su Twitter creando un pulsante Segui e inserendolo nel tuo sito." +ACF_TWITTER_LB="Pulsante grande" +ACF_TWITTER_LB_DESC="Visualizza la versione grande del pulsante Segui." +ACF_TWITTER_SU="Mostra nome utente" +ACF_TWITTER_SU_DESC="Mostra il tuo nome utente nel pulsante Segui." +ACF_TWITTER_SC="Mostra conteggio" +ACF_TWITTER_SC_DESC="Mostra il conteggio dei follower." +ACF_TWITTER_TWTR_HANDLE="Inserisci un handle di Twitter" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Seleziona il widget che desideri visualizzare." +ACF_TWITTER_FOLLOW="Pulsante Segui" diff --git a/plugins/fields/acftwitter/language/nl-NL/nl-NL.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/nl-NL/nl-NL.plg_fields_acftwitter.ini new file mode 100644 index 00000000..969d6011 --- /dev/null +++ b/plugins/fields/acftwitter/language/nl-NL/nl-NL.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Velden - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Voer een Twitter-handle in.

Bijvoorbeeld: tassosm" +ACF_TWITTER_DESC="Krijg meer Twitter-volgers door een volgknop te maken en op je website te plaatsen." +ACF_TWITTER_LB="Grote Knop" +ACF_TWITTER_LB_DESC="Toon een grote volg knop " +ACF_TWITTER_SU="Toon Gebruikersnaam" +ACF_TWITTER_SU_DESC="Toon jouw gebruikersnaam in de volg knop" +ACF_TWITTER_SC="Telling weergeven" +ACF_TWITTER_SC_DESC="Telling van volgers weergeven" +ACF_TWITTER_TWTR_HANDLE="Vul een twitter-Handle in" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Selecteer welke plugin je wil tonen" +ACF_TWITTER_FOLLOW="Volg Knop" diff --git a/plugins/fields/acftwitter/language/ru-RU/ru-RU.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/ru-RU/ru-RU.plg_fields_acftwitter.ini new file mode 100644 index 00000000..847f390e --- /dev/null +++ b/plugins/fields/acftwitter/language/ru-RU/ru-RU.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Поля - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Введите дескриптор твиттера.

Пример: tassosm" +ACF_TWITTER_DESC="Получите больше подписчиков в Твиттере, создав кнопку Follow и разместив ее на своем сайте." +ACF_TWITTER_LB="Большая кнопка" +ACF_TWITTER_LB_DESC="Показать большую кнопку следующей кнопки." +ACF_TWITTER_SU="Показать имя пользователя" +ACF_TWITTER_SU_DESC="Показывать ваше имя пользователя в следующей кнопке." +ACF_TWITTER_SC="Показать счетчик" +ACF_TWITTER_SC_DESC="Показать количество подписчиков." +ACF_TWITTER_TWTR_HANDLE="Введите хэндл Twitter" +ACF_TWITTER_WIDGET="Виджет:" +ACF_TWITTER_WIDGET_DESC="Выберите, какой виджет вы хотите отобразить." +ACF_TWITTER_FOLLOW="Кнопка"_QQ_" Follow " diff --git a/plugins/fields/acftwitter/language/sv-SE/sv-SE.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/sv-SE/sv-SE.plg_fields_acftwitter.ini new file mode 100644 index 00000000..3eb02e29 --- /dev/null +++ b/plugins/fields/acftwitter/language/sv-SE/sv-SE.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Fält - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Ange en twitter handle.

Example: tassosm" +ACF_TWITTER_DESC="Få fler Twitter-följare genom att skapa en Follow-knapp och placera den på din webbplats." +ACF_TWITTER_LB="Stor knapp" +ACF_TWITTER_LB_DESC="Visa en stor följ-knapp." +ACF_TWITTER_SU="Visa användarnamn" +ACF_TWITTER_SU_DESC="Visa ditt användarnamn i följ-knappen." +ACF_TWITTER_SC="Visa antal" +ACF_TWITTER_SC_DESC="Visa antal följare." +ACF_TWITTER_TWTR_HANDLE="Ange en Twitter Handle" +ACF_TWITTER_WIDGET="Widget:" +ACF_TWITTER_WIDGET_DESC="Välj vilken widget du vill visa." +ACF_TWITTER_FOLLOW="Följ-knapp" diff --git a/plugins/fields/acftwitter/language/uk-UA/uk-UA.plg_fields_acftwitter.ini b/plugins/fields/acftwitter/language/uk-UA/uk-UA.plg_fields_acftwitter.ini new file mode 100644 index 00000000..fee510dc --- /dev/null +++ b/plugins/fields/acftwitter/language/uk-UA/uk-UA.plg_fields_acftwitter.ini @@ -0,0 +1,21 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFTWITTER_LABEL="ACF - Twitter" +ACF_TWITTER="Поля - ACF Twitter" +ACF_TWITTER_VALUE_DESC="Введіть ім'я користувача.

Приклад: Tassosm" +ACF_TWITTER_DESC="Отримайте більше підписників у Twitter, створивши кнопку"_QQ_" Follow "_QQ_"та розмістивши її на своєму сайті." +ACF_TWITTER_LB="Велика кнопка" +ACF_TWITTER_LB_DESC="Відобразити велику кнопку кнопки"_QQ_". " +ACF_TWITTER_SU="Показати ім'я користувача" +ACF_TWITTER_SU_DESC="Показати своє ім'я користувача в межах кнопки"_QQ_"" +ACF_TWITTER_SC="Показати кількість" +ACF_TWITTER_SC_DESC="Показати кількість підписників." +ACF_TWITTER_TWTR_HANDLE="Введіть ручку Twitter" +ACF_TWITTER_WIDGET="Віджет:" +ACF_TWITTER_WIDGET_DESC="Виберіть, який віджет ви хочете показати." +ACF_TWITTER_FOLLOW="Кнопка Слідкувати за" diff --git a/plugins/fields/acftwitter/params/acftwitter.xml b/plugins/fields/acftwitter/params/acftwitter.xml new file mode 100644 index 00000000..804f6b9e --- /dev/null +++ b/plugins/fields/acftwitter/params/acftwitter.xml @@ -0,0 +1,35 @@ + +
+ +
+ + + + + + + + +
+
+
+ diff --git a/plugins/fields/acftwitter/script.install.helper.php b/plugins/fields/acftwitter/script.install.helper.php new file mode 100644 index 00000000..a409efc0 --- /dev/null +++ b/plugins/fields/acftwitter/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcftwitterInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acftwitter/script.install.php b/plugins/fields/acftwitter/script.install.php new file mode 100644 index 00000000..3f2d9c22 --- /dev/null +++ b/plugins/fields/acftwitter/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFTwitterInstallerScript extends PlgFieldsACFTwitterInstallerScriptHelper +{ + public $alias = 'acftwitter'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acftwitter/tmpl/acftwitter.php b/plugins/fields/acftwitter/tmpl/acftwitter.php new file mode 100644 index 00000000..07633a4c --- /dev/null +++ b/plugins/fields/acftwitter/tmpl/acftwitter.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +// Grab the widget type +$widget_type = $fieldParams->get('widget_type', true); + +$file = __DIR__ . '/plugins/' . $widget_type . '.php'; +if(file_exists($file)) +{ + // Display selected widget + require $file; +} \ No newline at end of file diff --git a/plugins/fields/acftwitter/tmpl/plugins/follow.php b/plugins/fields/acftwitter/tmpl/plugins/follow.php new file mode 100644 index 00000000..f605461f --- /dev/null +++ b/plugins/fields/acftwitter/tmpl/plugins/follow.php @@ -0,0 +1,37 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +if (!$twitter_handle = htmlentities($field->value, ENT_COMPAT, 'UTF-8')) +{ + return; +} + +$large_button = $fieldParams->get('follow.large_button', true); +$show_username = $fieldParams->get('follow.show_username', true); +$show_count = $fieldParams->get('follow.show_count', true); + +// Load twitter's widgets library +$doc = Factory::getDocument(); +$doc->addScript('https://platform.twitter.com/widgets.js'); + +echo ''; + +?> \ No newline at end of file diff --git a/plugins/fields/acftwitter/version.php b/plugins/fields/acftwitter/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acftwitter/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfupload/acfupload.php b/plugins/fields/acfupload/acfupload.php new file mode 100644 index 00000000..dd01a144 --- /dev/null +++ b/plugins/fields/acfupload/acfupload.php @@ -0,0 +1,570 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use NRFramework\File; +use NRFramework\Mimes; +use NRFramework\Image; +use NRFramework\Functions; +use Joomla\Registry\Registry; +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Utility\Utility; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); +JLoader::register('ACFUploadHelper', __DIR__ . '/fields/uploadhelper.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFUpload extends ACF_Field +{ + /** + * The validation rule will be used to validate the field on saving + * + * @var string + */ + protected $validate = 'acfrequired'; + + public function onUserAfterSave($user, $isnew, $success, $msg) + { + // Load Fields Component Helper class + JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + $fields = FieldsHelper::getFields('com_users.user', $user, true); + + if (!$fields) + { + return true; + } + + // Get the fields data + $fieldsData = !empty($user['com_fields']) ? $user['com_fields'] : []; + + $this->processFiles($fields, $fieldsData, (object) $user); + } + + public function onContentAfterSave($context, $item, $isNew, $data = []) + { + if (!is_array($data)) + { + return true; + } + + if (!isset($data['com_fields'])) + { + return true; + } + + // Create correct context for category + if ($context == 'com_categories.category') + { + $context = $item->get('extension') . '.categories'; + } + + // Load Fields Component Helper class + JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + // Check the context + $parts = FieldsHelper::extract($context, $item); + + if (!$parts) + { + return true; + } + + // Compile the right context for the fields + $context = $parts[0] . '.' . $parts[1]; + + // Loading the fields + $fields = FieldsHelper::getFields($context, $item); + + if (!$fields) + { + return true; + } + + // Get the fields data + $fieldsData = !empty($data['com_fields']) ? $data['com_fields'] : []; + + $this->processFiles($fields, $fieldsData, $item); + } + + /** + * Processes the files. + * + * Either duplicates the files or uploads them to final directory. + * + * @param array $fields + * @param array $fieldsData + * @param object $item + * + * @return void + */ + private function processFiles($fields = [], $fieldsData = [], $item = []) + { + if (!$fields || !$fieldsData || !$item) + { + return; + } + + // Whether we should clean up the temp folder at the end of this process + $should_clean = false; + + // Get the Fields Model + if (!defined('nrJ4')) + { + $model = JModelLegacy::getInstance('Field', 'FieldsModel', ['ignore_request' => true]); + } + else + { + $model = Factory::getApplication()->bootComponent('com_fields')->getMVCFactory()->createModel('Field', 'Administrator', ['ignore_request' => true]); + } + + // Cache subform fields + $subform_fields = []; + + // Loop over the fields + foreach ($fields as $field) + { + $field_type = $field->type; + + /** + * Check whether a Gallery field is used within the Subform field. + */ + if ($field_type === 'subform') + { + $submitted_subform_value = array_key_exists($field->name, $fieldsData) ? $fieldsData[$field->name] : null; + + // Ensure it has a value + if (!$submitted_subform_value || !$subform_value = json_decode($field->rawvalue, true)) + { + // Update subform field + $model->setFieldValue($field->id, $item->id, json_encode([])); + + continue; + } + + $update = false; + $is_subform_non_repeatable = false; + + // Make non-repeatable subform fields a multi array so we can parse them + if (Functions::startsWith(array_key_first($subform_value), 'field') && $field->fieldparams->get('repeat', '0') === '0') + { + $is_subform_non_repeatable = true; + $subform_value = [$subform_value]; + } + + foreach ($subform_value as $key => &$value) + { + foreach ($value as $_key => &$_value) + { + // Get Field ID + $field_id = str_replace('field', '', $_key); + + // Get Field by ID + $subform_field = isset($subform_fields[$field_id]) ? $subform_fields[$field_id] : $model->getItem($field_id); + + // Only proceed for this field type + if ($subform_field->type !== $this->_name) + { + continue; + } + + // Cache field + if (!isset($subform_fields[$field_id])) + { + $subformfields[$field_id] = $subform_field; + } + + + // Check if value can be json_decoded + if (is_string($_value)) + { + if ($decoded = json_decode($_value, true)) + { + $_value = $decoded; + } + } + + if (\ACF\Item::isCopying()) + { + // Duplicate files + ACFUploadHelper::duplicateFiles($_value); + } + else + { + // We should run our cleanup routine at the end + $should_clean = true; + + // Move to final folder + $_value = ACFUploadHelper::moveTempItemsToDestination($_value, $subform_field, $item); + } + + $update = true; + } + } + + if ($update) + { + if ($is_subform_non_repeatable) + { + $subform_value = reset($subform_value); + } + + // Update subform field + $model->setFieldValue($field->id, $item->id, json_encode($subform_value)); + } + } + else + { + // Only proceed for this field type + if ($field_type !== $this->_name) + { + continue; + } + + // Determine the value if it is available from the data + $value = array_key_exists($field->name, $fieldsData) ? $fieldsData[$field->name] : null; + + if (!$value) + { + continue; + } + + // Check if value can be json_decoded + if (is_string($value)) + { + if ($decoded = json_decode($value, true)) + { + $value = $decoded; + } + } + + if (\ACF\Item::isCopying()) + { + // Duplicate files + ACFUploadHelper::duplicateFiles($value); + } + else + { + // We should run our cleanup routine at the end + $should_clean = true; + + // Move to final folder + $value = ACFUploadHelper::moveTempItemsToDestination($value, $field, $item); + } + + // Setting the value for the field and the item + $model->setFieldValue($field->id, $item->id, json_encode($value)); + } + } + + if ($should_clean) + { + // Clean old files from temp folder + ACFUploadHelper::clean(); + } + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Joomla\CMS\Form\Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return $fieldNode; + } + + $this->attachEditModal(); + + HTMLHelper::stylesheet('plg_system_acf/acf-backend.css', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_fields_acfupload/edit-modal.js', ['relative' => true, 'version' => 'auto']); + + $fieldNode->setAttribute('field_id', $field->id); + + return $fieldNode; + } + + /** + * Attaches the edit modal to the page. + * + * @return void + */ + private function attachEditModal() + { + $form_source = new SimpleXMLElement(' +
+
+ + +
+
+ '); + + $form = Form::getInstance($this->_name, $form_source->asXML(), ['control' => $this->_name]); + + $content = + '
' . + '
' . Text::_('ACF_UPLOAD_CURRENTLY_EDITING_ITEM') . '
' . + $form->renderFieldset('acfupload_edit_modal') . + '
'; + + echo HTMLHelper::_('bootstrap.renderModal', 'acfUploadItemEditModal', [ + 'title' => Text::_('ACF_UPLOAD_EDIT_ITEM'), + 'modalWidth' => '40', + 'footer' => '' + ], $content); + } + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + * + * @since 3.7.0 + */ + public function onContentPrepareForm(Joomla\CMS\Form\Form $form, $data) + { + // Make sure we are manipulating the right field. + if (isset($data->type) && ($data->type != $this->_name)) + { + return; + } + + $result = parent::onContentPrepareForm($form, $data); + + // Display the server's maximum upload size in the field's description + $max_upload_size_str = HTMLHelper::_('number.bytes', Utility::getMaxUploadSize()); + $field_desc = $form->getFieldAttribute('max_file_size', 'description', null, 'fieldparams'); + $form->setFieldAttribute('max_file_size', 'description', Text::sprintf($field_desc, $max_upload_size_str), 'fieldparams'); + + // If the Fileinfo PHP extension is not installed, display a warning. + if (!extension_loaded('fileinfo') || !function_exists('mime_content_type')) + { + Factory::getApplication()->enqueueMessage(Text::_('ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING'), 'warning'); + } + + return $result; + } + + /** + * Handle AJAX endpoint + * + * @return void + */ + public function onAjaxACFUpload() + { + if (!Session::checkToken('request')) + { + $this->uploadDie(Text::_('JINVALID_TOKEN')); + } + + $taskMethod = 'task' . ucfirst(Factory::getApplication()->input->get('task', 'upload')); + + if (!method_exists($this, $taskMethod)) + { + $this->uploadDie('Invalid endpoint'); + } + + $this->$taskMethod(); + } + + /** + * The Upload task called by the AJAX hanler + * + * @return void + */ + public function taskUpload() + { + $input = Factory::getApplication()->input; + + // Make sure we have a valid form and a field key + if (!$field_id = $input->getInt('id')) + { + $this->uploadDie('ACF_UPLOAD_ERROR'); + } + + // Get Upload Settings + if (!$upload_field_settings = $this->getCustomFieldData($field_id)) + { + $this->uploadDie('ACF_UPLOAD_ERROR_INVALID_FIELD'); + } + + $allow_unsafe = $upload_field_settings->get('allow_unsafe', false); + + // Make sure we have a valid file passed + if (!$file = $input->files->get('file', null, ($allow_unsafe ? 'raw' : 'cmd'))) + { + $this->uploadDie('ACF_UPLOAD_ERROR_INVALID_FILE'); + } + + // In case we allow multiple uploads the file parameter is a 2 levels array. + $first_property = array_pop($file); + if (is_array($first_property)) + { + $file = $first_property; + } + + // Upload temporarily to the default upload folder + $allowed_types = $upload_field_settings->get('upload_types'); + + try { + $randomize_filename = $upload_field_settings->get('randomize_filename', false); + + $upload_folder = implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, ACFUploadHelper::getTempFolder()]); + + $uploaded_filename = File::upload($file, $upload_folder, $allowed_types, $allow_unsafe, $randomize_filename ? '' : null); + $uploaded_filename = str_replace([JPATH_SITE, JPATH_ROOT], '', $uploaded_filename); + + // Resize images + if ($upload_field_settings->get('resize_images', false)) + { + // Get file type + $file_type = Mimes::detectFileType(JPATH_ROOT . $uploaded_filename); + + // Allowed image file types + $allowed_image_file_types = [ + 'image/jpg', + 'image/jpeg', + 'image/pjpeg', + 'image/png', + 'image/x-png', + 'image/webp' + ]; + + // Ensure it is a valid image + if (Mimes::check($allowed_image_file_types, $file_type)) + { + // We require at least width or height to not be null + $resize_width = $upload_field_settings->get('width', null); + $resize_height = $upload_field_settings->get('height', null); + if ($resize_width || $resize_height) + { + Image::resizeByWidthOrHeight(JPATH_ROOT . $uploaded_filename, $resize_width, $resize_height); + } + } + } + + $response = [ + 'file' => $uploaded_filename, + 'file_encode' => base64_encode($uploaded_filename), + 'url' => ACFUploadHelper::absURL($uploaded_filename) + ]; + + header('Content-Type: application/json'); + + echo json_encode($response); + + jexit(); + + } catch (\Throwable $th) + { + $this->uploadDie($th->getMessage()); + } + } + + /** + * The delete task called by the AJAX hanlder + * + * @return void + */ + private function taskDelete() + { + // Make sure we have a valid file passed + if (!$filename = Factory::getApplication()->input->get('file', '', 'BASE64')) + { + $this->uploadDie('ACF_UPLOAD_ERROR_INVALID_FILE'); + } + + // Delete the uploaded file + echo json_encode([ + 'success' => ACFUploadHelper::deleteFile(base64_decode($filename)) + ]); + } + + /** + * Pull Custom Field Data + * + * @param integer $id The Custom Field primary key + * + * @return object + */ + private function getCustomFieldData($id) + { + $db = Factory::getDbo(); + + $query = $db->getQuery(true); + + $query + ->select($db->quoteName(['fieldparams'])) + ->from($db->quoteName('#__fields')) + ->where($db->quoteName('id') . ' = ' . $id) + ->where($db->quoteName('type') . ' = ' . $db->quote('acfupload')) + ->where($db->quoteName('state') . ' = 1'); + + $db->setQuery($query); + + if (!$result = $db->loadResult()) + { + return; + } + + return new Joomla\Registry\Registry($result); + } + + /** + * DropzoneJS detects errors based on the response error code. + * + * @param string $error_message + * + * @return void + */ + private function uploadDie($error_message) + { + http_response_code('500'); + die(Text::_($error_message)); + } +} diff --git a/plugins/fields/acfupload/acfupload.xml b/plugins/fields/acfupload/acfupload.xml new file mode 100644 index 00000000..1befdf3c --- /dev/null +++ b/plugins/fields/acfupload/acfupload.xml @@ -0,0 +1,26 @@ + + + ACF_UPLOAD + ACF_UPLOAD_DESC + Tassos Marinos + February 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfupload.php + script.install.helper.php + version.php + fields + language + params + tmpl + + + js + css + + diff --git a/plugins/fields/acfupload/fields/acfupload.php b/plugins/fields/acfupload/fields/acfupload.php new file mode 100644 index 00000000..561b5a1b --- /dev/null +++ b/plugins/fields/acfupload/fields/acfupload.php @@ -0,0 +1,147 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\Field\HiddenField; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Layout\FileLayout; +use Joomla\Filesystem\Path; + +class JFormFieldACFUpload extends HiddenField +{ + /** + * Method to get the field input markup for a generic list. + * Use the multiple attribute to enable multiselect. + * + * @return string The field input markup. + */ + protected function getInput() + { + if ($this->element['limit_files'] != 1) + { + HTMLHelper::script('plg_fields_acfupload/vendor/sortable.min.js', ['relative' => true, 'version' => 'auto']); + } + + HTMLHelper::script('plg_fields_acfupload/vendor/dropzone.min.js', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_fields_acfupload/acfupload.js', ['relative' => true, 'version' => 'auto']); + HTMLHelper::stylesheet('plg_fields_acfupload/acfupload.css', ['relative' => true, 'version' => 'auto']); + + // Add language strings used by script + Text::script('ACF_UPLOAD_FILETOOBIG'); + Text::script('ACF_UPLOAD_INVALID_FILE'); + Text::script('ACF_UPLOAD_FALLBACK_MESSAGE'); + Text::script('ACF_UPLOAD_RESPONSE_ERROR'); + Text::script('ACF_UPLOAD_CANCEL_UPLOAD'); + Text::script('ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION'); + Text::script('ACF_UPLOAD_REMOVE_FILE'); + Text::script('ACF_UPLOAD_MAX_FILES_EXCEEDED'); + Text::script('ACF_UPLOAD_FILE_MISSING'); + Text::script('ACF_UPLOAD_REMOVE_FILE_CONFIRM'); + + // Render File Upload Field + $data = [ + 'id' => $this->id, + 'value' => $this->prepareValue(), + 'input_name' => $this->element['limit_files'] == 1 ? $this->name : $this->name . '[INDEX]', + 'required' => ((string) $this->element['required'] == 'true' ? true : false), + 'field_id' => $this->element['field_id'], + 'max_file_size' => $this->element['max_file_size'], + 'limit_files' => $this->element['limit_files'], + 'multiple' => $this->element['limit_files'] == 1 ? false : true, + 'upload_types' => $this->element['upload_types'], + 'disabled' => $this->disabled, + 'show_download_links' => isset($this->element['show_download_links']) && $this->element['show_download_links'] == 1 ? true : false, + 'base_url' => Uri::base() // AJAX endpoint works on both site and backend. + ]; + + $layout = new FileLayout('acfuploadlayout', __DIR__); + return $layout->render($data); + } + + /** + * Prepare the value. + * + * @return void + */ + private function prepareValue() + { + if (empty($this->value)) + { + return; + } + + require_once __DIR__ . '/uploadhelper.php'; + + $limit_files = (int) $this->element['limit_files']; + + /** + * This handles backwards compatibility + */ + $files = is_string($this->value) ? json_decode($this->value, true) ?? [['value' => $this->value]] : $this->value; + + // Ensure its an array. If it was saved via a Subform field, it would return us an array of + $files = json_decode(json_encode($files), true); + + // Handle single file + if ($limit_files === 1 && is_array($files)) + { + if (isset($files['value'])) + { + $files = [$files]; + } + + $files = [reset($files)]; + } + + if (!$files) + { + return; + } + + $return = []; + + foreach ($files as $value) + { + if (!$value) + { + continue; + } + + $file = isset($value['value']) ? $value['value'] : $value; + $title = isset($value['title']) ? $value['title'] : ''; + $description = isset($value['description']) ? $value['description'] : ''; + + // Always use framework's pathinfo to fight issues with non latin characters. + $filePathInfo = NRFramework\File::pathinfo($file); + + $file_path = Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $file])); + $exists = is_file($file_path); + $file_size = $exists ? filesize($file_path) : 0; + + $return[] = [ + 'title' => $title, + 'description' => $description, + 'name' => $filePathInfo['basename'], + 'path' => $file, + 'encoded' => base64_encode($file), + 'url' => ACFUploadHelper::absURL($file_path), + 'size' => $file_size, + 'exists' => $exists + ]; + } + + return $return; + } +} \ No newline at end of file diff --git a/plugins/fields/acfupload/fields/acfuploadlayout.php b/plugins/fields/acfupload/fields/acfuploadlayout.php new file mode 100644 index 00000000..0ddb3269 --- /dev/null +++ b/plugins/fields/acfupload/fields/acfuploadlayout.php @@ -0,0 +1,79 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +extract($displayData); + +$wrapper_class = ''; +if ($disabled) +{ + $wrapper_class .= ' disabled'; +} +?> + + + + + + + +
+ data-maxfilesize="" + data-maxfiles="" + data-acceptedfiles="" + data-value='' + data-baseurl='' + + data-disabled='' + + class="acfupload"> +
+ + +
+
+
\ No newline at end of file diff --git a/plugins/fields/acfupload/fields/uploadhelper.php b/plugins/fields/acfupload/fields/uploadhelper.php new file mode 100644 index 00000000..7984f772 --- /dev/null +++ b/plugins/fields/acfupload/fields/uploadhelper.php @@ -0,0 +1,339 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use NRFramework\File; +use NRFramework\Functions; +use Joomla\CMS\Factory; +use Joomla\Registry\Registry; +use Joomla\Filesystem\File as JoomlaFile; +use Joomla\Filesystem\Path; +use Joomla\CMS\Uri\Uri; + +/** + * Upload Helper class mainly used by the FileUpload field + */ +class ACFUploadHelper +{ + /** + * How long the files can stay in the temp folder. + * + * After each save a clean up is run and all files older + * than this value in days are removed. + * + * @var int + */ + private static $temp_files_cleanup_days = 1; + + /** + * Duplicates files. + * + * @param array $value + * + * @return void + */ + public static function duplicateFiles(&$value = []) + { + if (!is_array($value) || !count($value)) + { + return; + } + + // If it's a single file, make it an array + if (!is_array(reset($value))) + { + $value = [$value]; + } + + foreach ($value as &$val) + { + // Original file path + $path = implode(DIRECTORY_SEPARATOR, [JPATH_SITE, $val['value']]); + + // New file path + $newPath = File::copy($path, $path); + + $val['value'] = str_replace([JPATH_SITE, JPATH_ROOT], '', $newPath); + } + } + + /** + * Moves all given `tmp` items over to the destination folder. + * + * @param array $value + * @param object $field + * @param object $item + * + * @return void + */ + public static function moveTempItemsToDestination($value, $field, $item) + { + // Make field params use Registry + if (!$field->fieldparams instanceof Registry) + { + $field->fieldparams = new Registry($field->fieldparams); + } + + /** + * Prepare the items for backwards compatibility + */ + $items = is_string($value) ? json_decode($value, true) ?? [['value' => $value]] : $value; + + if (isset($items['value'])) + { + $items = [$items]; + } + + $limit_files = (int) $field->fieldparams->get('limit_files', 1); + + // Handle single file + if ($limit_files === 1 && is_array($items)) + { + $items = [reset($items)]; + } + + $ds = DIRECTORY_SEPARATOR; + + $destination_folder = trim(ltrim($field->fieldparams->get('upload_folder'), $ds), $ds); + + // Add field Smart Tags + $field_custom_tags = [ + 'id' => $field->id + ]; + + // Add item smart tags + $custom_tags = [ + 'id' => $item->id, + 'author_id' => isset($item->created_by) ? $item->created_by : '', + 'alias' => isset($item->alias) ? $item->alias : '', + 'cat_id' => isset($item->catid) ? $item->catid : '', + 'cat_alias' => isset($item->catid) ? self::getCategoryAlias($item->catid) : '' + ]; + + // Move all files from `tmp` folder over to the `upload folder` + foreach ($items as $key => &$item) + { + $item_path = implode($ds, [JPATH_ROOT, ltrim($item['value'], $ds)]); + if (!Functions::startsWith(ltrim($item['value'], $ds), self::getTempFolder()) || !file_exists($item_path)) + { + continue; + } + + // Smart Tags Instance + $st = new \NRFramework\SmartTags(); + + // Add field Smart Tags + $st->add($field_custom_tags, 'acf.field.'); + + // Add item Smart Tags + $st->add($custom_tags, 'acf.item.'); + + // Add file Smart Tags + $source_file_info = File::pathinfo($item['value']); + $source_basename = $source_file_info['basename']; + $source_file_info['filename'] = $source_file_info['filename']; + $source_file_info['basename'] = $source_basename; + $source_file_info['extension'] = $source_file_info['extension']; + $source_file_info['index'] = $key + 1; + $source_file_info['total'] = count($items); + + $st->add($source_file_info, 'acf.file.'); + + $_destination_folder = $st->replace($destination_folder); + + $destination_file = implode($ds, [JPATH_ROOT, $_destination_folder]); + + // Validate destination file by adding the extension at the end + $destination_file_info = File::pathinfo($destination_file); + if (!isset($destination_file_info['extension'])) + { + $destination_file = implode($ds, [$destination_file_info['dirname'], $destination_file_info['basename'], $source_basename]); + } + + // Move item + $destionation_path = File::move($item_path, $destination_file); + + // Get relative path + $relative_path = ltrim(rtrim(str_replace(JPATH_ROOT, '', $destionation_path), $ds), $ds); + + // Update destination path + $item['value'] = $relative_path; + } + + return $items; + } + + /** + * Returns a category alias by its ID. + * + * @param int $cat_id + * + * @return string + */ + private static function getCategoryAlias($cat_id = null) + { + if (!$cat_id) + { + return; + } + + $db = Factory::getDbo(); + + $query = $db->getQuery(true) + ->select($db->quoteName('alias')) + ->from($db->quoteName('#__categories')) + ->where($db->quoteName('id') . ' = ' . (int) $cat_id); + $db->setQuery($query); + + return $db->loadResult(); + } + + /** + * Delete an uploaded file + * + * @param string $filename The filename + * @param string $upload_folder The uploaded folder + * + * @return bool + */ + public static function deleteFile($filename) + { + if (empty($filename)) + { + return; + } + + $file = Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $filename])); + + if (!is_file($file)) + { + return; + } + + return JoomlaFile::delete($file); + } + + /** + * Return absolute full URL of a path + * + * @param string $path + * + * @return string + */ + public static function absURL($path) + { + $path = str_replace([JPATH_SITE, JPATH_ROOT, Uri::root()], '', $path); + $path = Path::clean($path); + + // Convert Windows Path to Unix + $path = str_replace('\\','/',$path); + + $path = ltrim($path, '/'); + $path = Uri::root() . $path; + + return $path; + } + + /** + * Get a human readable file size in PHP: + * Credits to: http://jeffreysambells.com/2012/10/25/human-readable-filesize-php + * + * @param integer $filename The file size in bytes + * @param int $decimals + * + * @return string The human readable string + */ + public static function humanFilesize($bytes, $decimals = 2) + { + $size = ['B','KB','MB','GB','TB','PB','EB','ZB','YB']; + $factor = floor((strlen($bytes) - 1) / 3); + + return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor]; + } + + /** + * Cleans the temp folder. + * + * Removes any image that is 1 day or older. + * + * @return void + */ + public static function clean() + { + $temp_folder = self::getFullTempFolder(); + + if (!is_dir($temp_folder)) + { + return; + } + + // Get images + $files = array_diff(scandir($temp_folder), ['.', '..', '.DS_Store', 'index.html']); + + $found = []; + + foreach ($files as $key => $filename) + { + $file_path = implode(DIRECTORY_SEPARATOR, [$temp_folder, $filename]); + + // Skip directories + if (is_dir($file_path)) + { + continue; + } + + $diff_in_miliseconds = time() - filemtime($file_path); + + // Skip the file if it's not old enough + if ($diff_in_miliseconds < (60 * 60 * 24 * self::$temp_files_cleanup_days)) + { + continue; + } + + $found[] = $file_path; + } + + if (!$found) + { + return; + } + + // Delete found old files + foreach ($found as $file) + { + unlink($file); + } + } + + /** + * Full temp directory where images are uploaded + * prior to them being saved in the final directory. + * + * @return string + */ + private static function getFullTempFolder() + { + return implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, self::getTempFolder()]); + } + + /** + * Temp folder where files are uploaded + * prior to them being saved in the final directory. + * + * @var string + */ + public static function getTempFolder() + { + return implode(DIRECTORY_SEPARATOR, ['media', 'acfupload', 'tmp']); + } +} \ No newline at end of file diff --git a/plugins/fields/acfupload/language/ca-ES/ca-ES.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/ca-ES/ca-ES.plg_fields_acfupload.ini new file mode 100644 index 00000000..6a9f7365 --- /dev/null +++ b/plugins/fields/acfupload/language/ca-ES/ca-ES.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Pujar arxiu" +ACF_UPLOAD="camps - ACF Pujar arxiu" +ACF_UPLOAD_DESC="Carrega qualsevol arxiu utilitzant un sistema drag&drop a l'administració que mostrarà l'arxiu pujat com un enllaç o imatge a la part pública." +; ACF_UPLOAD_VALUE_DESC="Select file(s) to upload" +ACF_UPLOAD_FOLDER="Carpeta de pujada" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Limit d'arxius" +ACF_UPLOAD_LIMIT_FILES_DESC="Quants arxius es poden pujar? Escriu 0 per no limitar." +ACF_UPLOAD_MAX_FILE_SIZE="Pes màxim d'arxiu" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Configura el pes d'arxiu màxim permès en megabytes. Escriu 0 per no limitar.

El màxim del teu servidor és: %s." +ACF_UPLOAD_TYPES="Tipus d'arxiu permesos" +; ACF_UPLOAD_TYPES_DESC="Enter comma separated list of allowed file types like:
.jpg, .gif, .png, .pdf

You may enter media types like: application/pdf, image/*, video/*

Or you can even mix both: application/*, .png, .jpg

Note: This is not fool-proof and can be tricked, please remember that there is always a danger in allowing users to upload files." +ACF_UPLOAD_ERROR="Formulari o clau de camp invàlida" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="No es pot pujar l'arxiu" +ACF_UPLOAD_ERROR_INVALID_FIELD="Camp de pujada invàlid" +; ACF_UPLOAD_ERROR_INVALID_FILE="This file seems unsafe or invalid and can't be uploaded." +ACF_UPLOAD_MAX_FILES_LIMIT="Pots pujar %d arxius" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Arrossega els arxius aquí" +ACF_UPLOAD_BROWSE="Navega" +ACF_UPLOAD_INVALID_FILE_TYPE="Arxiu invpalid. Tipus d'arxiu permesos: %s" +ACF_UPLOAD_FILE_IS_MISSING="Falta l'arxiu. Intenta tornar a pujar-lo." +ACF_UPLOAD_FOLDER_INVALID="El directori de pujada %s no existeix, o no s'hi pot escriure." +ACF_UPLOAD_FILETOOBIG="L'arxiu pesa massa ({{filesize}}MB). Pes màxim: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="No pots pujar arxius d'aquest tipus." +ACF_UPLOAD_FALLBACK_MESSAGE="El teu navegador no suporta les pujades arrossegant." +ACF_UPLOAD_RESPONSE_ERROR = "El servidor ha contestat amb el codi {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Cancel·lar pujada" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Estàs segur que vols cancel·lar aquesta pujada?" +ACF_UPLOAD_REMOVE_FILE="Eliminar arxiu" +; ACF_UPLOAD_REMOVE_FILE_CONFIRM="Are you sure?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="No pots carregar més arxius." +ACF_UPLOAD_RANDOMIZE="Aleatoritzar noms d'arxius" +ACF_UPLOAD_RANDOMIZE_DESC="Si està activat, s'afegirà un prefix aleatori al principi del nom d'arxiu pujat. Això ajuda a assegurar que els arxius existents amb el mateix nom mai no se sobreescriguin." +ACF_UPLOAD_FILE_MISSING="Falta l'arxiu" +ACF_UPLOAD_FORCE_DOWNLOAD="Forçar descàrrega" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Escull si descarregar l'arxiu en lloc de navegar a l'enllaç" +ACF_UPLOAD_LINK_TEXT="Enllaçar text" +; ACF_UPLOAD_LINK_TEXT_DESC="Enter a custom link text. If none provided, the file name will be used instead. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Disseny" +ACF_UPLOAD_LAYOUT_DESC="Estableix el disseny que s'utilitzarà per mostrar cada arxiu pujat a la part pública." +ACF_UPLOAD_CUSTOM_LAYOUT="Disseny personalitzat" +; ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Set a custom HTML layout that will be used to display each uploaded file in the front-end. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Enllaç" +ACF_UPLOAD_IMAGE="Imatge" +ACF_UPLOAD_CUSTOM="Personalitzat" +ACF_UPLOAD_FRONTEND_DISPLAY="Pantalla pública" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Configuració de pujada d'arxius" +; ACF_UPLOAD_ALLOW_UNSAFE="Allow Unsafe Files" +; ACF_UPLOAD_ALLOW_UNSAFE_DESC="Allow the upload of unsafe files.

A file is considered unsafe when:
- A null byte is found in the file name.
- File extension is forbidden: .php, py etc.
- There's a php tag in file content.
- There's a short tag in file content.
- There's a forbidden extension anywhere in the content.

This option protects you also from unsafe files included in compressed files such as zip, rar, tar e.t.c." +; ACF_UPLOAD_VIEW_FILE="View file in a new tab" +; ACF_UPLOAD_DOWNLOAD_FILE="Download file" +; ACF_UPLOAD_DELETE_FILE="Delete file" +; ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Show Downloads Links" +; ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Enable to display download links for each uploaded file in the file uploader." +; ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="The PHP extension fileinfo is required to guess the mime type of uploaded files but it's not installed or not loaded. Please contact your host to install it." +; ACF_UPLOAD_TITLE="Title" +; ACF_UPLOAD_TITLE_DESC="Define a title for this uploaded file." +; ACF_UPLOAD_TITLE_HINT="Enter a title..." +; ACF_UPLOAD_DESCRIPTION="Description" +; ACF_UPLOAD_DESCRIPTION_DESC="Define a description for this uploaded file." +; ACF_UPLOAD_DESCRIPTION_HINT="Enter a description..." +; ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Currently editing item: " +; ACF_UPLOAD_EDIT_ITEM="Edit Item" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/da-DK/da-DK.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/da-DK/da-DK.plg_fields_acfupload.ini new file mode 100644 index 00000000..1c88bbef --- /dev/null +++ b/plugins/fields/acfupload/language/da-DK/da-DK.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Fil upload" +ACF_UPLOAD="Felter - ACF Fil upload" +ACF_UPLOAD_DESC="Upload enhver fil ved at anvende træk og slip fil uploaderen i backend og vis de uploadede filer som et link eller som et billede i frontend." +ACF_UPLOAD_VALUE_DESC="Vælg fil(er) til upload" +ACF_UPLOAD_FOLDER="Upload mappe" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Filer grænse" +ACF_UPLOAD_LIMIT_FILES_DESC="Hvor mange filer kan uploades? Angiv 0 for ingen grænse." +ACF_UPLOAD_MAX_FILE_SIZE="Filstørrelse grænse" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Konfigurer den maksimalt tilladte filstørrelse for hver uploadet fil i megabyte. Angiv 0 for ingen grænse.

Din servers maksimale upload størrelse er: %s." +ACF_UPLOAD_TYPES="Tilladte filtyper" +ACF_UPLOAD_TYPES_DESC="Angiv en kommasepareret liste med tilladte filtyper såsom:
.jpg, .gif, .png, .pdf

Du kan angive medietyper såsom: application/pdf, image/*, video/*

Eller du kan endda blande begge dele: application/*, .png, .jpg

Bemærk: Dette er ikke idiotsikret and kan snydes. Husk venligst at der altid er en fare i at lade brugere uploade filer." +ACF_UPLOAD_ERROR="Ugyldig formular eller feltnøgle" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Kan ikke uploade fil" +ACF_UPLOAD_ERROR_INVALID_FIELD="Ugyldigt upload felt" +ACF_UPLOAD_ERROR_INVALID_FILE="Denne fil virker usikker eller ugyldig og kan ikke uploades." +ACF_UPLOAD_MAX_FILES_LIMIT="Du kan uploade op til %d filer" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Træk og slip filer her eller" +ACF_UPLOAD_BROWSE="Gennemse" +ACF_UPLOAD_INVALID_FILE_TYPE="Ugyldig fil. Tilladte filtyper: %s" +ACF_UPLOAD_FILE_IS_MISSING="Filen mangler. Prøv venligst at gen-upload den." +ACF_UPLOAD_FOLDER_INVALID="Upload mappen %s eksisterer ikke eller er ikke skrivbar" +ACF_UPLOAD_FILETOOBIG="Filen er for stor ({{filesize}}MB). Maks filstørrelse: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="Du kan ikke uploade filer af denne type." +ACF_UPLOAD_FALLBACK_MESSAGE="Din browser understøtter ikke træk og slip fil uploads." +ACF_UPLOAD_RESPONSE_ERROR = "Server svarede med koden {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Annuller upload" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Er du sikker på at du ønsker at annullere denne upload?" +ACF_UPLOAD_REMOVE_FILE="Fjern fil" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="Er du sikker ?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Du kan ikke uploade flere filer." +ACF_UPLOAD_RANDOMIZE="Randomiser filnavne" +ACF_UPLOAD_RANDOMIZE_DESC="Hvis aktiveret, så vil et tilfældigt præfiks blive tilføjet i starten af den uploadede fils navn. Dette hjælper med at sikre at eksisterende filer med det samme navn aldrig bliver overskrevet." +ACF_UPLOAD_FILE_MISSING="Filen mangler is missing" +ACF_UPLOAD_FORCE_DOWNLOAD="Gennemtving download" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Vælg om filen skal downloades i stedet for at navigere til linket" +ACF_UPLOAD_LINK_TEXT="Linktekst" +ACF_UPLOAD_LINK_TEXT_DESC="Angiv en brugerdefineret link tekst. Hvis ingen angives, så vil filnavnet blive anvendt i stedet. Understøtter smart tags.

Fil smart tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Layout" +ACF_UPLOAD_LAYOUT_DESC="Definer layoutet som vil blive anvendt til at vise hver uploadede fil i frontend." +ACF_UPLOAD_CUSTOM_LAYOUT="Brugerdefineret layout" +ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Angiv et brugerdefineret HTML layout som vil blive anvendt til visning af hver uploadede fil i frontend. Understøtter smart tags.

Fil smart tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Almindelige smart tags
{site.url}" +ACF_UPLOAD_LINK="Link" +ACF_UPLOAD_IMAGE="Billede" +ACF_UPLOAD_CUSTOM="Brugerdefineret" +ACF_UPLOAD_FRONTEND_DISPLAY="Frontend visningDisplay" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Fil upload indstillinger" +ACF_UPLOAD_ALLOW_UNSAFE="Tillad usikre filer" +ACF_UPLOAD_ALLOW_UNSAFE_DESC="Tillad upload af usikre filer.

En fil bliver betragtet som usikker når:
- Et null byte bliver fundet i filnavnet.
- Filendelsen er forbudt: .php, py etc.
- Der er et php tag i filindholdet.
- Det er et kort tag i filindholdet.
- Der er en forbudt udvidelse et sted i indholdet.

Denne indstilling beskytter dig også mod usikre filer inkluderet i komprimerede filer såsom zip, rar, tar e.t.c." +ACF_UPLOAD_VIEW_FILE="Vis filen i en ny fane" +ACF_UPLOAD_DOWNLOAD_FILE="Download til" +ACF_UPLOAD_DELETE_FILE="Slet fil" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Vis download links" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Aktiver at vise download links for hver uploadede fil i fil uploaderen." +ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="PHP udvidelsen fileinfo er krævet for at gætte mime typen på de uploadede filer, men den er ikke installeret eller ikke indlæst. Kontakt venligst din host for at få den installeret." +ACF_UPLOAD_TITLE="Titel" +ACF_UPLOAD_TITLE_DESC="Definer en titel for den uploadede fil." +ACF_UPLOAD_TITLE_HINT="Angiv en titel..." +ACF_UPLOAD_DESCRIPTION="Beskrivelse" +ACF_UPLOAD_DESCRIPTION_DESC="Definer en beskrivelse for den uploadede fil." +ACF_UPLOAD_DESCRIPTION_HINT="Angiv en beskrivelse..." +ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Aktuelt redigere element: " +ACF_UPLOAD_EDIT_ITEM="Rediger element" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/de-DE/de-DE.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/de-DE/de-DE.plg_fields_acfupload.ini new file mode 100644 index 00000000..86401fae --- /dev/null +++ b/plugins/fields/acfupload/language/de-DE/de-DE.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Datei hochladen" +ACF_UPLOAD="Felder - ACF-Datei hochladen" +ACF_UPLOAD_DESC="Laden Sie eine beliebige Datei mit einem Drag & Drop-Datei-Uploader im Backend hoch und zeigen Sie die hochgeladene Datei als Link oder Bild im Frontend an." +; ACF_UPLOAD_VALUE_DESC="Select file(s) to upload" +ACF_UPLOAD_FOLDER="Ordner hochladen" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Dateilimit" +ACF_UPLOAD_LIMIT_FILES_DESC="Wie viele Dateien können hochgeladen werden? Geben Sie 0 für unbegrenzt ein." +ACF_UPLOAD_MAX_FILE_SIZE="Dateigrößenbeschränkung" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Konfigurieren Sie die maximal zulässige Größe für jede hochgeladene Datei in Megabyte. Geben Sie unbegrenzt 0 ein.

Die maximale Upload-Größe Ihres Servers beträgt: % s ." +ACF_UPLOAD_TYPES="Zulässige Dateitypen" +; ACF_UPLOAD_TYPES_DESC="Enter comma separated list of allowed file types like: .jpg, .gif, .png, .pdf

You may enter media types like: application/pdf, image/*, video/*

Or you can even mix both: application/*, .png, .jpg

Note: This is not fool-proof and can be tricked, please remember that there is always a danger in allowing users to upload files." +ACF_UPLOAD_ERROR="Ungültiger Formular- oder Feldschlüssel" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Datei kann nicht hochgeladen werden" +ACF_UPLOAD_ERROR_INVALID_FIELD="Ungültiges Upload-Feld" +; ACF_UPLOAD_ERROR_INVALID_FILE="This file seems unsafe or invalid and can't be uploaded." +ACF_UPLOAD_MAX_FILES_LIMIT="Sie können bis zu %d Dateien hochladen" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Dateien hierher ziehen und ablegen oder" +ACF_UPLOAD_BROWSE="Durchsuchen" +ACF_UPLOAD_INVALID_FILE_TYPE="Ungültige Datei. Zulässige Dateitypen: %s" +ACF_UPLOAD_FILE_IS_MISSING="Die Datei fehlt. Bitte versuchen Sie erneut, sie hochzuladen." +ACF_UPLOAD_FOLDER_INVALID="Der Upload-Ordner% s existiert nicht oder ist nicht beschreibbar" +ACF_UPLOAD_FILETOOBIG="Die Datei ist zu groß ({{filesize}} MB). Maximale Dateigröße: {{maxFilesize}} MB." +ACF_UPLOAD_INVALID_FILE="Sie können keine Dateien dieses Typs hochladen." +ACF_UPLOAD_FALLBACK_MESSAGE="Ihr Browser unterstützt das Hochladen von Drag'n'Drop-Dateien nicht." +ACF_UPLOAD_RESPONSE_ERROR = "Server hat mit {{statusCode}} Code geantwortet." +ACF_UPLOAD_CANCEL_UPLOAD="Upload abbrechen" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Möchten Sie diesen Upload wirklich abbrechen?" +ACF_UPLOAD_REMOVE_FILE="Datei entfernen" +; ACF_UPLOAD_REMOVE_FILE_CONFIRM="Are you sure?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Sie können keine weiteren Dateien hochladen." +ACF_UPLOAD_RANDOMIZE="Dateinamen zufällig sortieren" +ACF_UPLOAD_RANDOMIZE_DESC="Wenn diese Option aktiviert ist, wird ein zufälliges Präfix an den Anfang des hochgeladenen Dateinamens angehängt. Dadurch wird sichergestellt, dass vorhandene Dateien mit demselben Namen niemals ersetzt werden." +ACF_UPLOAD_FILE_MISSING="Datei fehlt" +ACF_UPLOAD_FORCE_DOWNLOAD="Herunterladen erzwingen" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Wählen Sie aus, ob die Datei heruntergeladen werden soll, anstatt zum Link zu navigieren." +ACF_UPLOAD_LINK_TEXT="Linktext" +; ACF_UPLOAD_LINK_TEXT_DESC="Enter a custom link text. If none provided, the file name will be used instead. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Layout" +ACF_UPLOAD_LAYOUT_DESC="Definieren Sie das Layout, mit dem jede hochgeladene Datei im Front-End angezeigt wird." +ACF_UPLOAD_CUSTOM_LAYOUT="Benutzerdefiniertes Layout" +; ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Set a custom HTML layout that will be used to display each uploaded file in the front-end. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Link" +ACF_UPLOAD_IMAGE="Bild" +ACF_UPLOAD_CUSTOM="Benutzerdefiniert" +ACF_UPLOAD_FRONTEND_DISPLAY="Frontend-Anzeige" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Einstellungen zum Hochladen von Dateien" +; ACF_UPLOAD_ALLOW_UNSAFE="Allow Unsafe Files" +; ACF_UPLOAD_ALLOW_UNSAFE_DESC="Allow the upload of unsafe files.

A file is considered unsafe when:
- A null byte is found in the file name.
- File extension is forbidden: .php, py etc.
- There's a php tag in file content.
- There's a short tag in file content.
- There's a forbidden extension anywhere in the content.

This option protects you also from unsafe files included in compressed files such as zip, rar, tar e.t.c." +; ACF_UPLOAD_VIEW_FILE="View file in a new tab" +; ACF_UPLOAD_DOWNLOAD_FILE="Download file" +; ACF_UPLOAD_DELETE_FILE="Delete file" +; ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Show Downloads Links" +; ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Enable to display download links for each uploaded file in the file uploader." +; ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="The PHP extension fileinfo is required to guess the mime type of uploaded files but it's not installed or not loaded. Please contact your host to install it." +; ACF_UPLOAD_TITLE="Title" +; ACF_UPLOAD_TITLE_DESC="Define a title for this uploaded file." +; ACF_UPLOAD_TITLE_HINT="Enter a title..." +; ACF_UPLOAD_DESCRIPTION="Description" +; ACF_UPLOAD_DESCRIPTION_DESC="Define a description for this uploaded file." +; ACF_UPLOAD_DESCRIPTION_HINT="Enter a description..." +; ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Currently editing item: " +; ACF_UPLOAD_EDIT_ITEM="Edit Item" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/en-GB/en-GB.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/en-GB/en-GB.plg_fields_acfupload.ini new file mode 100644 index 00000000..d2dfc8c9 --- /dev/null +++ b/plugins/fields/acfupload/language/en-GB/en-GB.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - File Upload" +ACF_UPLOAD="Fields - ACF File Upload" +ACF_UPLOAD_DESC="Upload any file using a drag & drop file uploader in the back-end and display the uploaded file as a link or image in the front-end." +ACF_UPLOAD_VALUE_DESC="Select file(s) to upload" +ACF_UPLOAD_FOLDER="Upload Folder" +ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Files Limit" +ACF_UPLOAD_LIMIT_FILES_DESC="How many files can be uploaded? Enter 0 for no limit." +ACF_UPLOAD_MAX_FILE_SIZE="File Size Limit" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Configure the maximum allowable size for each uploaded file in megabytes. Enter 0 for no limit.

Your server's maximum upload size is: %s." +ACF_UPLOAD_TYPES="Allowed File Types" +ACF_UPLOAD_TYPES_DESC="Enter comma separated list of allowed file types like: .jpg, .gif, .png, .pdf

You may enter media types like: application/pdf, image/*, video/*

Or you can even mix both: application/*, .png, .jpg

Note: This is not fool-proof and can be tricked, please remember that there is always a danger in allowing users to upload files." +ACF_UPLOAD_ERROR="Invalid form or field key" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Cannot upload file" +ACF_UPLOAD_ERROR_INVALID_FIELD="Invalid upload field" +ACF_UPLOAD_ERROR_INVALID_FILE="This file seems unsafe or invalid and can't be uploaded." +ACF_UPLOAD_MAX_FILES_LIMIT="You can upload up to %d files" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Drag and drop files here or" +ACF_UPLOAD_BROWSE="Browse" +ACF_UPLOAD_INVALID_FILE_TYPE="Invalid file. Allowed file types: %s" +ACF_UPLOAD_FILE_IS_MISSING="File is missing. Please try to re-upload." +ACF_UPLOAD_FOLDER_INVALID="The upload folder %s doesn't exist or is not writable" +ACF_UPLOAD_FILETOOBIG="File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="You can't upload files of this type." +ACF_UPLOAD_FALLBACK_MESSAGE="Your browser does not support drag'n'drop file uploads." +ACF_UPLOAD_RESPONSE_ERROR = "Server responded with {{statusCode}} code." +ACF_UPLOAD_CANCEL_UPLOAD="Cancel upload" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Are you sure you want to cancel this upload?" +ACF_UPLOAD_REMOVE_FILE="Remove file" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="Are you sure?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="You can not upload any more files." +ACF_UPLOAD_RANDOMIZE="Randomize File names" +ACF_UPLOAD_RANDOMIZE_DESC="If enabled, a random prefix will be added to the beginning of the uploaded file name. This helps to ensure existing files with the same name never get replaced." +ACF_UPLOAD_FILE_MISSING="File is missing" +ACF_UPLOAD_FORCE_DOWNLOAD="Force Download" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Select whether to download the file instead of navigating to the link" +ACF_UPLOAD_LINK_TEXT="Link Text" +ACF_UPLOAD_LINK_TEXT_DESC="Enter a custom link text. If none provided, the file name will be used instead. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Layout" +ACF_UPLOAD_LAYOUT_DESC="Define the layout that will be used to display each uploaded file in the front-end." +ACF_UPLOAD_CUSTOM_LAYOUT="Custom Layout" +ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Set a custom HTML layout that will be used to display each uploaded file in the front-end. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Link" +ACF_UPLOAD_IMAGE="Image" +ACF_UPLOAD_CUSTOM="Custom" +ACF_UPLOAD_FRONTEND_DISPLAY="Front-end Display" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="File Upload Settings" +ACF_UPLOAD_ALLOW_UNSAFE="Allow Unsafe Files" +ACF_UPLOAD_ALLOW_UNSAFE_DESC="Allow the upload of unsafe files.

A file is considered unsafe when:
- A null byte is found in the file name.
- File extension is forbidden: .php, py etc.
- There's a php tag in file content.
- There's a short tag in file content.
- There's a forbidden extension anywhere in the content.

This option protects you also from unsafe files included in compressed files such as zip, rar, tar e.t.c." +ACF_UPLOAD_VIEW_FILE="View file in a new tab" +ACF_UPLOAD_DOWNLOAD_FILE="Download file" +ACF_UPLOAD_DELETE_FILE="Delete file" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Show Downloads Links" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Enable to display download links for each uploaded file in the file uploader." +ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="The PHP extension fileinfo is required to guess the mime type of uploaded files but it's not installed or not loaded. Please contact your host to install it." +ACF_UPLOAD_TITLE="Title" +ACF_UPLOAD_TITLE_DESC="Define a title for this uploaded file." +ACF_UPLOAD_TITLE_HINT="Enter a title..." +ACF_UPLOAD_DESCRIPTION="Description" +ACF_UPLOAD_DESCRIPTION_DESC="Define a description for this uploaded file." +ACF_UPLOAD_DESCRIPTION_HINT="Enter a description..." +ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Currently editing item: " +ACF_UPLOAD_EDIT_ITEM="Edit Item" +ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +ACF_UPLOAD_WIDTH="Width" +ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +ACF_UPLOAD_HEIGHT="Height" +ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." \ No newline at end of file diff --git a/plugins/fields/acfupload/language/en-GB/en-GB.plg_fields_acfupload.sys.ini b/plugins/fields/acfupload/language/en-GB/en-GB.plg_fields_acfupload.sys.ini new file mode 100644 index 00000000..653ee410 --- /dev/null +++ b/plugins/fields/acfupload/language/en-GB/en-GB.plg_fields_acfupload.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_UPLOAD="Fields - ACF File Upload" +ACF_UPLOAD_DESC="Upload any file using a drag & drop file uploader in the back-end and display the uploaded file as a link or image in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfupload/language/es-ES/es-ES.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/es-ES/es-ES.plg_fields_acfupload.ini new file mode 100644 index 00000000..c8af215e --- /dev/null +++ b/plugins/fields/acfupload/language/es-ES/es-ES.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Carga de Archivos" +ACF_UPLOAD="Campos - ACF Subida de Archivos" +ACF_UPLOAD_DESC="Cargue cualquier archivo usando un cargador de archivos de arrastrar y soltar en el back-end y muestre el archivo cargado como un enlace o imagen en el front-end." +ACF_UPLOAD_VALUE_DESC="Seleccionar archivo(s) para cargar" +ACF_UPLOAD_FOLDER="Cargar carpeta" +ACF_UPLOAD_FOLDER_DESC="Ingrese la ruta relativa a la raíz de su espacio web donde se almacenarán los archivos cargados. Para cambiar el nombre de los archivos cargados, utilice las siguientes etiquetas inteligentes.

Cargar etiquetas inteligentes
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Etiquetas inteligentes de campo
{acf.field.id}

Etiquetas inteligentes de elementos
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Etiquetas inteligentes comunes
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Límite de Archivos" +ACF_UPLOAD_LIMIT_FILES_DESC="¿Cuántos archivos se pueden subir? Ingrese 0 para no tener límite." +ACF_UPLOAD_MAX_FILE_SIZE="Límite de Tamaño de Archivo" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Configure el tamaño máximo permitido para cada archivo subido en megabytes. Ingrese 0 para no tener límite.

El tamaño máximo de carga de su servidor es: %s." +ACF_UPLOAD_TYPES="Tipos de Archivo Permitidos" +ACF_UPLOAD_TYPES_DESC="Ingrese una lista separada por comas de tipos de archivos permitidos como: .jpg, .gif, .png, .pdf

Puede ingresar tipos de medios como: application/pdf, image/*, video/*

O incluso puedes mezclar ambos: application/*, .png, .jpg

Nota: Esto no es infalible y se puede engañar, recuerde que siempre existe el peligro de permitir que los usuarios suban archivos." +ACF_UPLOAD_ERROR="Formulario o clave de campo no válido" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="No se puede cargar el archivo" +ACF_UPLOAD_ERROR_INVALID_FIELD="Campo de subida no válido" +ACF_UPLOAD_ERROR_INVALID_FILE="Este archivo parece inseguro o no válido y no se puede cargar." +ACF_UPLOAD_MAX_FILES_LIMIT="Puedes subir hasta %d archivos" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Arrastra y suelta archivos aquí o" +ACF_UPLOAD_BROWSE="Navegar" +ACF_UPLOAD_INVALID_FILE_TYPE="Archivo inválido. Tipos de archivo permitidos:%s" +ACF_UPLOAD_FILE_IS_MISSING="Falta el archivo. Intente volver a cargarlo." +ACF_UPLOAD_FOLDER_INVALID="La carpeta de carga %s no existe o no se puede escribir" +ACF_UPLOAD_FILETOOBIG="El archivo es demasiado grande ({{filesize}}MB). Tamaño máximo de archivo: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="No puedes subir archivos de este tipo." +ACF_UPLOAD_FALLBACK_MESSAGE="Su navegador no admite cargas de archivos de arrastrar y soltar." +ACF_UPLOAD_RESPONSE_ERROR = "El servidor respondió con el código {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Cancelar subida" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="¿Estás seguro de que deseas cancelar esta subida?" +ACF_UPLOAD_REMOVE_FILE="Borrar archivo" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="¿Está usted seguro?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Usted no puede subir más archivos." +ACF_UPLOAD_RANDOMIZE="Nombres de Archivos Aleatorios" +ACF_UPLOAD_RANDOMIZE_DESC="Si está habilitado, se agregará un prefijo aleatorio al comienzo del nombre del archivo cargado. Esto ayuda a garantizar que los archivos existentes con el mismo nombre nunca se reemplacen." +ACF_UPLOAD_FILE_MISSING="Falta el archivo" +ACF_UPLOAD_FORCE_DOWNLOAD="Forzar Descarga" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Seleccione si desea descargar el archivo en lugar de navegar hasta el enlace" +ACF_UPLOAD_LINK_TEXT="Texto del Enlace" +ACF_UPLOAD_LINK_TEXT_DESC="Introduzca un texto de enlace personalizado. Si no se proporciona ninguno, se usará el nombre del archivo en su lugar. Admite etiquetas inteligentes.

Etiquetas inteligentes de archivo:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Disposición" +ACF_UPLOAD_LAYOUT_DESC="Defina el diseño que se usará para mostrar cada archivo cargado en el front-end." +ACF_UPLOAD_CUSTOM_LAYOUT="Diseño personalizado" +ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Establezca un diseño HTML personalizado que se usará para mostrar cada archivo cargado en el front-end. Admite etiquetas inteligentes.

Etiquetas inteligentes de archivos:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Enlace" +ACF_UPLOAD_IMAGE="Imagen" +ACF_UPLOAD_CUSTOM="Personalizar" +ACF_UPLOAD_FRONTEND_DISPLAY="Pantalla Frontal" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Configuración de Carga de Archivos" +ACF_UPLOAD_ALLOW_UNSAFE="Permitir archivos no seguros" +ACF_UPLOAD_ALLOW_UNSAFE_DESC="Permitir la carga de archivos no seguros.

Un archivo se considera inseguro cuando:
- Se encuentra un byte nulo en el nombre del archivo.
- La extensión de archivo está prohibida: .php, py, etc.
- Hay una etiqueta php en el contenido del archivo.
- Hay una etiqueta corta en el contenido del archivo.
- Hay una extensión prohibida en cualquier parte del contenido.

Esta opción también lo protege de archivos no seguros incluidos en archivos comprimidos como zip, rar, tar, etc." +ACF_UPLOAD_VIEW_FILE="Ver archivo en una nueva pestaña" +ACF_UPLOAD_DOWNLOAD_FILE="Descargar archivo" +ACF_UPLOAD_DELETE_FILE="Borrar archivo" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Mostrar Enlaces de Descargas" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Habilite para mostrar enlaces de descarga para cada archivo cargado en el cargador de archivos." +ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="La extensión PHP fileinfose requiere para adivinar el tipo mime de los archivos cargados, pero si no está instalado o no está cargado. Póngase en contacto con su host para instalarlo." +ACF_UPLOAD_TITLE="Título" +ACF_UPLOAD_TITLE_DESC="Defina un título para este archivo cargado." +ACF_UPLOAD_TITLE_HINT="Introduzca un título..." +ACF_UPLOAD_DESCRIPTION="Descripción" +ACF_UPLOAD_DESCRIPTION_DESC="Defina una descripción para este archivo cargado." +ACF_UPLOAD_DESCRIPTION_HINT="Introduzca una descripción..." +ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Elemento de edición actual: " +ACF_UPLOAD_EDIT_ITEM="Editar elemento" +ACF_UPLOAD_RESIZE_IMAGES="Redimensionar Imágenes" +ACF_UPLOAD_RESIZE_IMAGES_DESC="Habilite el cambio de tamaño de las imágenes a las dimensiones especificadas.
Las imágenes admitidas son jpg, jpeg, png, gif y webp.

Solo se cambiará el tamaño de las imágenes nuevas, las imágenes existentes se dejarán como están.
Cuando se cambia el tamaño de la imagen, no se crean imágenes adicionales.

Escenarios útiles de cambio de tamaño:
Cambiar tamaño por ancho: para cambiar el tamaño de las imágenes cargadas por ancho y mantener la relación de aspecto, establezca solo un ancho y deje la altura en blanco.
Cambiar tamaño por altura: para cambiar el tamaño de las imágenes cargadas por altura y mantener la relación de aspecto, establezca solo una altura y deje ancho en blanco.
Cambiar el tamaño por ancho y alto: para cambiar el tamaño de las imágenes cargadas a un ancho y alto específicos, así como recortar la imagen a estas dimensiones, establezca un ancho y un alto." +ACF_UPLOAD_WIDTH="Width" +ACF_UPLOAD_WIDTH_DESC="Ingrese el ancho en píxeles para redimensionar la imagen." +ACF_UPLOAD_HEIGHT="Altura" +ACF_UPLOAD_HEIGHT_DESC="Ingrese la altura en píxeles para redimensionar la imagen." diff --git a/plugins/fields/acfupload/language/es-ES/es-ES.plg_fields_acfupload.sys.ini b/plugins/fields/acfupload/language/es-ES/es-ES.plg_fields_acfupload.sys.ini new file mode 100644 index 00000000..9cbddf2c --- /dev/null +++ b/plugins/fields/acfupload/language/es-ES/es-ES.plg_fields_acfupload.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_UPLOAD="Campos - ACF Subida de Archivo" +ACF_UPLOAD_DESC="Cargue cualquier archivo usando un cargador de archivos de arrastrar y soltar en el back-end y muestre el archivo cargado como un enlace o imagen en el front-end." diff --git a/plugins/fields/acfupload/language/fr-FR/fr-FR.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/fr-FR/fr-FR.plg_fields_acfupload.ini new file mode 100644 index 00000000..60adc3c1 --- /dev/null +++ b/plugins/fields/acfupload/language/fr-FR/fr-FR.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Téléversement de fichier" +ACF_UPLOAD="Champs - ACF Téléversement de fichier" +ACF_UPLOAD_DESC="Envoyer n'importe quel fichier par glisser-déposer dans l'administration et afficher le ficher en tant que lien ou image sur le site." +ACF_UPLOAD_VALUE_DESC="Sélectionner le(s) fichier(s) à envoyer" +ACF_UPLOAD_FOLDER="Dossier de destination" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Nb maximal de fichiers" +ACF_UPLOAD_LIMIT_FILES_DESC="Combien de fichiers peuvent être transférés ? Saisir 0 pour ne pas limiter limite." +ACF_UPLOAD_MAX_FILE_SIZE="Limite de poids des fichiers" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Configurer la taille maximale en mégaoctets autorisée pour chaque fichier. Saisir 0 pour ne pas limiter.

Le limite du serveur est : %s." +ACF_UPLOAD_TYPES="Types de fichiers autorisés" +; ACF_UPLOAD_TYPES_DESC="Enter comma separated list of allowed file types like: .jpg, .gif, .png, .pdf

You may enter media types like: application/pdf, image/*, video/*

Or you can even mix both: application/*, .png, .jpg

Note: This is not fool-proof and can be tricked, please remember that there is always a danger in allowing users to upload files." +; ACF_UPLOAD_ERROR="Invalid form or field key" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Impossible d'envoyer le fichier" +; ACF_UPLOAD_ERROR_INVALID_FIELD="Invalid upload field" +ACF_UPLOAD_ERROR_INVALID_FILE="Ce fichier paraît suspect ou incorrect, il ne peut être envoyé." +ACF_UPLOAD_MAX_FILES_LIMIT="Vous pouvez envoyer jusqu'à %d fichiers" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Glisser et déposer les fichier ici ou" +ACF_UPLOAD_BROWSE="Parcourir" +ACF_UPLOAD_INVALID_FILE_TYPE="Fichier non valide. Voici les types autorisés : %s" +ACF_UPLOAD_FILE_IS_MISSING="Fichier manquant. Veuillez réessayer l'envoi." +ACF_UPLOAD_FOLDER_INVALID="Le dossier d'envoi %s n'existe pas ou n'a pas de droit d'écriture" +ACF_UPLOAD_FILETOOBIG="Le fichier est trop volumineux ({{filesize}} Mo). La taille maximale est {{maxFilesize}} Mo." +ACF_UPLOAD_INVALID_FILE="Vous ne pouvez pas envoyer de fichiers de ce type." +ACF_UPLOAD_FALLBACK_MESSAGE="Votre navigateur ne supporte par l'envoi de fichiers par glisser-déposer." +ACF_UPLOAD_RESPONSE_ERROR = "Le serveur a répondu avec le code {{statusCode}}" +ACF_UPLOAD_CANCEL_UPLOAD="Annuler l'envoi" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Êtes-vous sûr de vouloir annuler cet envoi ?" +ACF_UPLOAD_REMOVE_FILE="Retirer le fichier" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="Êtes-vous sûr ?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Vous ne pouvez plus envoyer de fichiers." +; ACF_UPLOAD_RANDOMIZE="Randomize File names" +; ACF_UPLOAD_RANDOMIZE_DESC="If enabled, a random prefix will be added to the beginning of the uploaded file name. This helps to ensure existing files with the same name never get replaced." +ACF_UPLOAD_FILE_MISSING="Fichier manquant" +; ACF_UPLOAD_FORCE_DOWNLOAD="Force Download" +; ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Select whether to download the file instead of navigating to the link" +ACF_UPLOAD_LINK_TEXT="Texte du lien" +; ACF_UPLOAD_LINK_TEXT_DESC="Enter a custom link text. If none provided, the file name will be used instead. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +; ACF_UPLOAD_LAYOUT="Layout" +; ACF_UPLOAD_LAYOUT_DESC="Define the layout that will be used to display each uploaded file in the front-end." +; ACF_UPLOAD_CUSTOM_LAYOUT="Custom Layout" +; ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Set a custom HTML layout that will be used to display each uploaded file in the front-end. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Lien" +ACF_UPLOAD_IMAGE="Image" +; ACF_UPLOAD_CUSTOM="Custom" +ACF_UPLOAD_FRONTEND_DISPLAY="Affichage site" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Paramètres d'envoi du fichier" +; ACF_UPLOAD_ALLOW_UNSAFE="Allow Unsafe Files" +; ACF_UPLOAD_ALLOW_UNSAFE_DESC="Allow the upload of unsafe files.

A file is considered unsafe when:
- A null byte is found in the file name.
- File extension is forbidden: .php, py etc.
- There's a php tag in file content.
- There's a short tag in file content.
- There's a forbidden extension anywhere in the content.

This option protects you also from unsafe files included in compressed files such as zip, rar, tar e.t.c." +; ACF_UPLOAD_VIEW_FILE="View file in a new tab" +; ACF_UPLOAD_DOWNLOAD_FILE="Download file" +ACF_UPLOAD_DELETE_FILE="Supprimer le fichier" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Affiches les liens de téléchargement" +; ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Enable to display download links for each uploaded file in the file uploader." +; ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="The PHP extension fileinfo is required to guess the mime type of uploaded files but it's not installed or not loaded. Please contact your host to install it." +ACF_UPLOAD_TITLE="Titre" +ACF_UPLOAD_TITLE_DESC="Définir un titre pour ce fichier envoyé." +ACF_UPLOAD_TITLE_HINT="Inscrire un titre..." +ACF_UPLOAD_DESCRIPTION="Description" +ACF_UPLOAD_DESCRIPTION_DESC="Définir une description pour ce fichier envoyé." +ACF_UPLOAD_DESCRIPTION_HINT="Inscrire une description..." +ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Élément actuellement modifié : " +ACF_UPLOAD_EDIT_ITEM="Modifier l'élément" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/it-IT/it-IT.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/it-IT/it-IT.plg_fields_acfupload.ini new file mode 100644 index 00000000..530612e1 --- /dev/null +++ b/plugins/fields/acfupload/language/it-IT/it-IT.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - caricamento file" +ACF_UPLOAD="Campi - caricamento file ACF" +ACF_UPLOAD_DESC="Carica qualsiasi file utilizzando un caricatore di file trascina e rilascia in amministrazione e visualizza il file caricato come collegamento o immagine nel front-end." +ACF_UPLOAD_VALUE_DESC="Seleziona il(i) file da caricare" +ACF_UPLOAD_FOLDER="Cartella di caricamento" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Limite file" +ACF_UPLOAD_LIMIT_FILES_DESC="Quanti file possono essere caricati? Immettere 0 per nessun limite." +ACF_UPLOAD_MAX_FILE_SIZE="Limite dimensione file" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Configura la dimensione massima consentita in megabyte per ogni file caricato. Inserisci 0 per nessun limite.

La dimensione massima di caricamento del tuo server è: %s." +ACF_UPLOAD_TYPES="Tipi di file consentiti" +ACF_UPLOAD_TYPES_DESC="Inserisci un elenco separato da virgole di tipi di file consentiti come: .jpg, .gif, .png, .pdf

Puoi inserire tipi di file multimediali come: application/pdf, image/*, video/*

Oppure puoi anche combinare entrambi: application/*, .png, .jpg

Nota: questo non è infallibile e può essere ingannato, ricorda che c'è sempre un pericolo nel consentire agli utenti di caricare file." +ACF_UPLOAD_ERROR="Modulo o chiave di campo non validi" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Non posso caricare file" +ACF_UPLOAD_ERROR_INVALID_FIELD="Campo di caricamento non valido" +ACF_UPLOAD_ERROR_INVALID_FILE="Questo file sembra non sicuro o non valido e non può essere caricato." +ACF_UPLOAD_MAX_FILES_LIMIT="Puoi caricare fino a %d file" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Trascina e rilascia file qui o" +ACF_UPLOAD_BROWSE="Sfoglia" +ACF_UPLOAD_INVALID_FILE_TYPE="File non valido. Tipi di file consentiti: %s" +ACF_UPLOAD_FILE_IS_MISSING="Manca il file. Prova a ricaricarlo." +ACF_UPLOAD_FOLDER_INVALID="La cartella di caricamento %s non esiste o non è scrivibile" +ACF_UPLOAD_FILETOOBIG="Il file è troppo grande ({{filesize}}MB). Dimensione massima del file: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="Non puoi caricare file di questo tipo." +ACF_UPLOAD_FALLBACK_MESSAGE="Il tuo browser non supporta il caricamento di file trascina e rilascia." +ACF_UPLOAD_RESPONSE_ERROR = "Il server ha risposto con il codice {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Cancella il caricamento" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Sei sicuro di voler annullare questo caricamento?" +ACF_UPLOAD_REMOVE_FILE="Elimina il file" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="Sei sicuro?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Non puoi caricare altri file." +ACF_UPLOAD_RANDOMIZE="Rendi casuali i nomi dei file" +ACF_UPLOAD_RANDOMIZE_DESC="Se abilitato, verrà aggiunto un prefisso casuale all'inizio del nome del file caricato. Ciò aiuta a garantire che i file esistenti con lo stesso nome non vengano mai sostituiti." +ACF_UPLOAD_FILE_MISSING="Il file è mancante" +ACF_UPLOAD_FORCE_DOWNLOAD="Scaricamento forzato" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Scegli se scaricare il file invece di navigare fino al collegamento" +ACF_UPLOAD_LINK_TEXT="Testo collegamento" +; ACF_UPLOAD_LINK_TEXT_DESC="Enter a custom link text. If none provided, the file name will be used instead. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Impaginazione" +ACF_UPLOAD_LAYOUT_DESC="Definisci l'impaginazione che verrà utilizzata per visualizzare ogni file caricato nel front-end." +ACF_UPLOAD_CUSTOM_LAYOUT="Impaginazione personalizzata" +; ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Set a custom HTML layout that will be used to display each uploaded file in the front-end. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Collegamento" +ACF_UPLOAD_IMAGE="Immagine" +ACF_UPLOAD_CUSTOM="Personalizzato" +ACF_UPLOAD_FRONTEND_DISPLAY="Visualizzazione front-end" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Impostazioni di caricamento file" +; ACF_UPLOAD_ALLOW_UNSAFE="Allow Unsafe Files" +; ACF_UPLOAD_ALLOW_UNSAFE_DESC="Allow the upload of unsafe files.

A file is considered unsafe when:
- A null byte is found in the file name.
- File extension is forbidden: .php, py etc.
- There's a php tag in file content.
- There's a short tag in file content.
- There's a forbidden extension anywhere in the content.

This option protects you also from unsafe files included in compressed files such as zip, rar, tar e.t.c." +ACF_UPLOAD_VIEW_FILE="Visualizza il file in una nuova scheda" +ACF_UPLOAD_DOWNLOAD_FILE="Scarica file" +ACF_UPLOAD_DELETE_FILE="Cancella il file" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Mostra link di scaricamento" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Abilita per visualizzare i link di download per ogni file caricato nel caricatore di file." +; ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="The PHP extension fileinfo is required to guess the mime type of uploaded files but it's not installed or not loaded. Please contact your host to install it." +; ACF_UPLOAD_TITLE="Title" +; ACF_UPLOAD_TITLE_DESC="Define a title for this uploaded file." +; ACF_UPLOAD_TITLE_HINT="Enter a title..." +; ACF_UPLOAD_DESCRIPTION="Description" +; ACF_UPLOAD_DESCRIPTION_DESC="Define a description for this uploaded file." +; ACF_UPLOAD_DESCRIPTION_HINT="Enter a description..." +; ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Currently editing item: " +; ACF_UPLOAD_EDIT_ITEM="Edit Item" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/nl-NL/nl-NL.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/nl-NL/nl-NL.plg_fields_acfupload.ini new file mode 100644 index 00000000..d994b4f7 --- /dev/null +++ b/plugins/fields/acfupload/language/nl-NL/nl-NL.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - File Upload" +ACF_UPLOAD="Velden - ACF File Upload" +ACF_UPLOAD_DESC="Upload elk bestand met een uploader voor slepen en neerzetten aan de back-end en geef het geüploade bestand weer als een link of afbeelding op de front-end." +ACF_UPLOAD_VALUE_DESC="Selecteer te uploaden bestand(en)" +ACF_UPLOAD_FOLDER="Upload Folder" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Bestandslimiet" +ACF_UPLOAD_LIMIT_FILES_DESC="Hoeveel ĕstanden kunnen er worden geupload? Vul de 0 in voor ongelimiteerd" +ACF_UPLOAD_MAX_FILE_SIZE="Bestandsgrootte Limiet" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Configureer de maximaal toegestane grootte voor elk geüpload bestand in megabytes. Voer 0 in voor geen limiet.

De maximale uploadgrootte van jouw server is:%s." +ACF_UPLOAD_TYPES="Toegestane Bestandstypen" +ACF_UPLOAD_TYPES_DESC="Voer een door komma's gescheiden lijst met toegestane bestandstypen in zoals: .jpg, .gif, .png, .pdf

Je kunt mediatypen invoeren zoals: application/pdf, image/*, video/*

Of je kunt zelfs beide mixen: application/*, . png, .jpg

Opmerking: dit is niet onfeilbaar en kan worden misleid. Houd er rekening mee dat er altijd een gevaar bestaat als gebruikers worden toegestaan bestanden te uploaden." +ACF_UPLOAD_ERROR="Ongeldige formulier- of veldsleutel" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Kan bestand niet uploaden" +ACF_UPLOAD_ERROR_INVALID_FIELD="Ongeldig uploadveld" +ACF_UPLOAD_ERROR_INVALID_FILE="Dit bestand lijkt onveilig of ongeldig en kan niet worden geüpload." +ACF_UPLOAD_MAX_FILES_LIMIT="Je kunt maximaal %d bestanden uploaden" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Sleep bestanden hierheen of" +ACF_UPLOAD_BROWSE="Bladeren" +ACF_UPLOAD_INVALID_FILE_TYPE="Ongeldig bestand. Toegestane bestandstypen zijn: %s" +ACF_UPLOAD_FILE_IS_MISSING="Bestand ontbreekt. Probeer het opnieuw" +ACF_UPLOAD_FOLDER_INVALID="De uploadmap %s bestaat niet of is niet beschrijfbaar" +ACF_UPLOAD_FILETOOBIG="Het bestand van ({{filesize}}MB) is te groot. Max bestandsgrootte is: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="Je kunt geen bestanden van dit type uploaden." +ACF_UPLOAD_FALLBACK_MESSAGE="Je browser ondersteunt geen drag'n'drop voor het uploaden van bestanden." +ACF_UPLOAD_RESPONSE_ERROR = "De Server reageerde met {{statusCode}} code." +ACF_UPLOAD_CANCEL_UPLOAD="Annuleer upload" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Weet je het zeker dat je deze upload wilt annuleren?" +ACF_UPLOAD_REMOVE_FILE="Verwijder bestand" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="Weet je het zeker?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Je kunt geen bestanden meer uploaden" +ACF_UPLOAD_RANDOMIZE="Maak bestandsnamen willekeurig" +ACF_UPLOAD_RANDOMIZE_DESC="Indien ingeschakeld, wordt een willekeurig voorvoegsel toegevoegd aan het begin van de geüploade bestandsnaam. Dit helpt ervoor te zorgen dat bestaande bestanden met dezelfde naam nooit worden vervangen." +ACF_UPLOAD_FILE_MISSING="Bestand ontbreekt" +ACF_UPLOAD_FORCE_DOWNLOAD="Forceer Download" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Selecteer of je het bestand wilt downloaden in plaats de link volgen" +ACF_UPLOAD_LINK_TEXT="Link Tekst" +ACF_UPLOAD_LINK_TEXT_DESC="Voer een aangepaste linktekst in. Als er geen is opgegeven, wordt in plaats daarvan de bestandsnaam gebruikt. Ondersteunt Slimme Tags.

Bestand Slimme Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Indeling" +ACF_UPLOAD_LAYOUT_DESC="Definieer de indeling die zal worden gebruikt om elk geüpload bestand aan de front-end weer te geven." +ACF_UPLOAD_CUSTOM_LAYOUT="Aangepaste Indeling" +ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Stel een aangepaste HTML indeling in die wordt gebruikt om elk geüpload bestand in de front-end weer te geven. Ondersteunt slimme tags.

Bestand Slimme Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Normale Slimme Tags
{site.url}" +ACF_UPLOAD_LINK="Link" +ACF_UPLOAD_IMAGE="Afbeelding" +ACF_UPLOAD_CUSTOM="Aangepast" +ACF_UPLOAD_FRONTEND_DISPLAY="Front-end Vertoning" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Bestandsupload Instellingen" +ACF_UPLOAD_ALLOW_UNSAFE="Sta onveilige bestanden toe" +ACF_UPLOAD_ALLOW_UNSAFE_DESC="Sta het uploaden van onveilige bestanden toe.

Een bestand wordt als onveilig beschouwd wanneer:
- Een null-byte wordt gevonden in de bestandsnaam.
- Bestandsextensie verboden is: .php, py enz.
- Er staat een php-tag in de bestandsinhoud.
- Er is een korte of slimme tag in de bestandsinhoud.
- Er is ergens in de inhoud een verboden extensie.

Deze optie beschermt je ook tegen onveilige bestanden in gecomprimeerde bestanden zoals zip, rar, tar enz." +ACF_UPLOAD_VIEW_FILE="Bekijk het bestand in een nieuw tabblad" +ACF_UPLOAD_DOWNLOAD_FILE="Doanload bestand" +ACF_UPLOAD_DELETE_FILE="Verwijder bestand" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Toon downloadlinks" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Schakel in om downloadlinks weer te geven voor elk geüpload bestand in de bestandsuploader." +ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="De PHP-extensie fileinfo is vereist om het MIME-type van geüploade bestanden te raden, maar het is niet geïnstalleerd of niet geladen. Neem contact op met jouw host om het te installeren." +ACF_UPLOAD_TITLE="Titel" +ACF_UPLOAD_TITLE_DESC="Definieer een titel voor dit geüploade bestand." +ACF_UPLOAD_TITLE_HINT="Voer een titel in..." +ACF_UPLOAD_DESCRIPTION="Beschrijving" +ACF_UPLOAD_DESCRIPTION_DESC="Definieer een beschrijving voor dit geüploade bestand." +ACF_UPLOAD_DESCRIPTION_HINT="Voer een beschrijving in..." +ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Item dat momenteel wordt bewerkt: " +ACF_UPLOAD_EDIT_ITEM="Edit Item" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/ru-RU/ru-RU.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/ru-RU/ru-RU.plg_fields_acfupload.ini new file mode 100644 index 00000000..d156a089 --- /dev/null +++ b/plugins/fields/acfupload/language/ru-RU/ru-RU.plg_fields_acfupload.ini @@ -0,0 +1,52 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Загрузка файла" +ACF_UPLOAD="Поля - Загрузка файла ACF" +ACF_UPLOAD_DESC="Загрузите любой файл с помощью средства для перетаскивания файлов в серверной части и отобразите загруженный файл в виде ссылки или изображения в пользовательском интерфейсе." +ACF_UPLOAD_FOLDER="Загрузить папку" +ACF_UPLOAD_FOLDER_DESC="Введите путь относительно корня вашего веб-пространства, в котором будут храниться загруженные файлы. Убедитесь, что введенный путь доступен для записи, в противном случае загрузка файла завершится неудачей." +ACF_UPLOAD_LIMIT_FILES="Ограничение файлов" +ACF_UPLOAD_LIMIT_FILES_DESC="Сколько файлов может быть загружено? Введите 0 без ограничений." +ACF_UPLOAD_MAX_FILE_SIZE="Ограничение размера файла" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Настройте максимально допустимый размер каждого загружаемого файла в мегабайтах. Введите 0 без ограничений.

Максимальный размер загрузки вашего сервера: % s ." +ACF_UPLOAD_TYPES="Разрешенные типы файлов" +ACF_UPLOAD_TYPES_DESC="Список разрешенных типов файлов через запятую. Оставьте пустым, чтобы принять все типы файлов.

Пример: .jpg, .gif, .png, .pdf

Это не защищает от ошибок и может быть обманным путем, пожалуйста, помните, что всегда есть опасность позволить пользователям загружать файлы »" +ACF_UPLOAD_ERROR="Неверный ключ формы или поля" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Невозможно загрузить файл" +ACF_UPLOAD_ERROR_INVALID_FIELD="Неверное поле загрузки" +ACF_UPLOAD_ERROR_INVALID_FILE="Неверный или неподдерживаемый файл" +ACF_UPLOAD_MAX_FILES_LIMIT="Вы можете загрузить до %d файлов" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Перетащите файлы сюда или" +ACF_UPLOAD_BROWSE="Просмотр" +ACF_UPLOAD_INVALID_FILE_TYPE="Неверный файл. Разрешенные типы файлов: %s" +ACF_UPLOAD_FILE_IS_MISSING="Файл отсутствует. Пожалуйста, попробуйте заново загрузить." +ACF_UPLOAD_FOLDER_INVALID="Папка для загрузки% s не существует или недоступна для записи" +ACF_UPLOAD_FILETOOBIG="Файл слишком большой ({{filesize}} МБ). Максимальный размер файла: {{maxFilesize}} МБ." +ACF_UPLOAD_INVALID_FILE="Вы не можете загружать файлы этого типа." +ACF_UPLOAD_FALLBACK_MESSAGE="Ваш браузер не поддерживает перетаскивание файлов загрузки." +ACF_UPLOAD_RESPONSE_ERROR = "Сервер ответил кодом {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Отменить загрузку" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Вы уверены, что хотите отменить загрузку?" +ACF_UPLOAD_REMOVE_FILE="Удалить файл" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Вы не можете больше загружать файлы." +ACF_UPLOAD_RANDOMIZE="Рандомизировать имена файлов" +ACF_UPLOAD_RANDOMIZE_DESC="Если включено, случайный префикс будет добавлен в начало имени загружаемого файла. Это помогает гарантировать, что существующие файлы с одинаковыми именами никогда не будут заменены." +ACF_UPLOAD_FILE_MISSING="Файл отсутствует" +ACF_UPLOAD_FORCE_DOWNLOAD="Принудительная загрузка" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Выберите, загружать ли файл вместо перехода по ссылке" +ACF_UPLOAD_LINK_TEXT="Текст ссылки" +ACF_UPLOAD_LINK_TEXT_DESC="Введите текст пользовательской ссылки. Если ничего не указано, вместо этого будет использоваться имя файла." +ACF_UPLOAD_LAYOUT="Макет" +ACF_UPLOAD_LAYOUT_DESC="Определить макет, который будет использоваться для отображения каждого загруженного файла во внешнем интерфейсе." +ACF_UPLOAD_CUSTOM_LAYOUT="Пользовательский макет" +ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Установить пользовательский макет HTML, который будет использоваться для отображения каждого загруженного файла в интерфейсе.

Доступные переменные
{site.url}
{ file.name}
{file.path}
{file.url} {
file.size}
{file.ext}" +ACF_UPLOAD_LINK="Ссылка" +ACF_UPLOAD_IMAGE="Изображение" +ACF_UPLOAD_CUSTOM="Пользовательский" +ACF_UPLOAD_FRONTEND_DISPLAY="Внешний интерфейс" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Настройки загрузки файлов" diff --git a/plugins/fields/acfupload/language/sv-SE/sv-SE.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/sv-SE/sv-SE.plg_fields_acfupload.ini new file mode 100644 index 00000000..16e41251 --- /dev/null +++ b/plugins/fields/acfupload/language/sv-SE/sv-SE.plg_fields_acfupload.ini @@ -0,0 +1,76 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - File Upload" +ACF_UPLOAD="Fält - ACF File Upload" +ACF_UPLOAD_DESC="Ladda upp vilken fil som helst med hjälp av en dra-och-släpp filuppladdare i backend och visa den uppladdade filen som en länk eller bild i frontend." +ACF_UPLOAD_VALUE_DESC="Välj fil(er) att ladda upp" +ACF_UPLOAD_FOLDER="Uppladdningsmapp" +; ACF_UPLOAD_FOLDER_DESC="Enter the path relative to the root of your webspace where the uploaded files will be stored. To rename uploaded files use the following Smart Tags.

Upload Smart Tags
{acf.file.basename}
{acf.file.extension}
{acf.file.filename}
{acf.file.index}
{acf.file.total}

Field Smart Tags
{acf.field.id}

Item Smart Tags
{acf.item.id}
{acf.item.author_id}
{acf.item.alias}
{acf.item.cat_id}
{acf.item.cat_alias}

Common Smart Tags
{date}
{time}
{day}
{month}
{year}" +ACF_UPLOAD_LIMIT_FILES="Filbegränsning" +ACF_UPLOAD_LIMIT_FILES_DESC="Hur många filer kan laddas upp? Ange 0 för ingen begränsning." +ACF_UPLOAD_MAX_FILE_SIZE="Filstorlek gräns" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Ange den maximalt tillåtna storleken för varje uppladdad fil i megabytes. Ange 0 för ingen bwegränsning.

Din servers maximala uppladdningsstorlek är: %s." +ACF_UPLOAD_TYPES="Tillåtna filtyper" +; ACF_UPLOAD_TYPES_DESC="Enter comma separated list of allowed file types like:
.jpg, .gif, .png, .pdf

You may enter media types like: application/pdf, image/*, video/*

Or you can even mix both: application/*, .png, .jpg

Note: This is not fool-proof and can be tricked, please remember that there is always a danger in allowing users to upload files." +ACF_UPLOAD_ERROR="Felaktigt formulär eller fältnyckel" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Kan inte ladda upp filen" +ACF_UPLOAD_ERROR_INVALID_FIELD="Felaktigt uppladdningsfält" +ACF_UPLOAD_ERROR_INVALID_FILE="Den här filen verkar vara osäker eller felaktig och kan inte laddas upp" +ACF_UPLOAD_MAX_FILES_LIMIT="Du kan ladda upp max %d filer." +ACF_UPLOAD_DRAG_AND_DROP_FILES="Dra och släpp filer här eller" +ACF_UPLOAD_BROWSE="Bläddra" +ACF_UPLOAD_INVALID_FILE_TYPE="Felaktig fil. Tillåtna filtyper: %s" +ACF_UPLOAD_FILE_IS_MISSING="Filen saknas. Försök att ladda upp den igen." +ACF_UPLOAD_FOLDER_INVALID="Uppladdningsmappen %s finns inte eller så är den inte skrivbar." +ACF_UPLOAD_FILETOOBIG="Filen är för stor ({{filesize}}MB). Max filstorlek: {{maxFilesize}}MB." +ACF_UPLOAD_INVALID_FILE="Du kan inte ladda upp filer av den här typen." +ACF_UPLOAD_FALLBACK_MESSAGE="Din webbläsare stöder inte dra-och-släpp pladdning av -filer." +ACF_UPLOAD_RESPONSE_ERROR = "Servern svarade med koden {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Avbryt uppladdning" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Är du säker på att du vill avbryta den här uppladdningen?" +ACF_UPLOAD_REMOVE_FILE="Ta bort fil" +ACF_UPLOAD_REMOVE_FILE_CONFIRM="Är du säker?" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Du kan inte ladda upp fler filer." +ACF_UPLOAD_RANDOMIZE="Slumpmässigt filnamn" +ACF_UPLOAD_RANDOMIZE_DESC="Om aktiverat, läggs ett slumpmässigt prefix till det uppladdade filnamnet. Detta hjälper till att säkerställa att befintliga filer med samma namn aldrig ersätts." +ACF_UPLOAD_FILE_MISSING="Filen saknas" +ACF_UPLOAD_FORCE_DOWNLOAD="Tvinga nedladdning" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Välj om du vill att filen ska laddas ner istället för att navigera till länken" +ACF_UPLOAD_LINK_TEXT="Länktext" +; ACF_UPLOAD_LINK_TEXT_DESC="Enter a custom link text. If none provided, the file name will be used instead. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.extension}
{acf.file.title}
{acf.file.description}" +ACF_UPLOAD_LAYOUT="Layout" +ACF_UPLOAD_LAYOUT_DESC="Definiera layouten som ska användas för att visa varje uppladdad fil i frontend." +ACF_UPLOAD_CUSTOM_LAYOUT="Anpassad layout" +; ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Set a custom HTML layout that will be used to display each uploaded file in the front-end. Supports Smart Tags.

File Smart Tags:
{acf.file.basename}
{acf.file.filename}
{acf.file.path}
{acf.file.url}
{acf.file.size}
{acf.file.extension}
{acf.file.title}
{acf.file.description}
{acf.file.index}
{acf.file.total}

Common Smart Tags
{site.url}" +ACF_UPLOAD_LINK="Länk" +ACF_UPLOAD_IMAGE="Bild" +ACF_UPLOAD_CUSTOM="Anpassad" +ACF_UPLOAD_FRONTEND_DISPLAY="Frontend visning" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Filuppladdning inställningar" +ACF_UPLOAD_ALLOW_UNSAFE="Tillåt osäkra filer" +; ACF_UPLOAD_ALLOW_UNSAFE_DESC="Allow the upload of unsafe files.

A file is considered unsafe when:
- A null byte is found in the file name.
- File extension is forbidden: .php, py etc.
- There's a php tag in file content.
- There's a short tag in file content.
- There's a forbidden extension anywhere in the content.

This option protects you also from unsafe files included in compressed files such as zip, rar, tar e.t.c." +ACF_UPLOAD_VIEW_FILE="Visa filen i en ny flik" +ACF_UPLOAD_DOWNLOAD_FILE="Ladda ner filen" +ACF_UPLOAD_DELETE_FILE="Radera filen" +ACF_UPLOAD_SHOW_DOWNLOAD_LINKS="Visa nedladdningslänkar" +; ACF_UPLOAD_SHOW_DOWNLOAD_LINKS_DESC="Enable to display download links for each uploaded file in the file uploader." +; ACF_UPLOAD_MIME_CONTENT_TYPE_MISSING="The PHP extension fileinfo is required to guess the mime type of uploaded files but it's not installed or not loaded. Please contact your host to install it." +ACF_UPLOAD_TITLE="Titel" +ACF_UPLOAD_TITLE_DESC="Definiera en titel för den här uppladdade filen" +ACF_UPLOAD_TITLE_HINT="Skriv in en titel...." +ACF_UPLOAD_DESCRIPTION="Beskrivning" +ACF_UPLOAD_DESCRIPTION_DESC="Definiera en beskrivning för den här uppladdade filen" +ACF_UPLOAD_DESCRIPTION_HINT="Lägg till en beskrivning..." +; ACF_UPLOAD_CURRENTLY_EDITING_ITEM="Currently editing item: " +ACF_UPLOAD_EDIT_ITEM="Redigera detta" +; ACF_UPLOAD_RESIZE_IMAGES="Resize Images" +; ACF_UPLOAD_RESIZE_IMAGES_DESC="Enable to resize images to the specified dimensions.
Supported images are jpg, jpeg, png, gif, and webp.

Only new images will be resized, existing images are left as-is.
When the image is resized, no additional images are created.

Helpful Resizing Scenarios:
Resize by width: To resize uploaded images by width and keep aspect ratio, set only a width and leave height blank.
Resize by height: To resize uploaded images by height and keep the aspect ratio, set only a height and leave width blank.
Resize by width & height: To resize uploaded images to a specific width and height, as well as crop the image to these dimensions, set both a width and height." +; ACF_UPLOAD_WIDTH="Width" +; ACF_UPLOAD_WIDTH_DESC="Enter the width in pixels to resize the image to." +; ACF_UPLOAD_HEIGHT="Height" +; ACF_UPLOAD_HEIGHT_DESC="Enter the height in pixels to resize the image to." diff --git a/plugins/fields/acfupload/language/uk-UA/uk-UA.plg_fields_acfupload.ini b/plugins/fields/acfupload/language/uk-UA/uk-UA.plg_fields_acfupload.ini new file mode 100644 index 00000000..3c1c9947 --- /dev/null +++ b/plugins/fields/acfupload/language/uk-UA/uk-UA.plg_fields_acfupload.ini @@ -0,0 +1,52 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFUPLOAD_LABEL="ACF - Завантаження файлів" +ACF_UPLOAD="Поля - Завантаження файлів ACF" +ACF_UPLOAD_DESC="Завантажте будь-який файл за допомогою завантажувача файлів перетягування на задній панелі та покажіть завантажений файл у вигляді посилання або зображення у передній частині." +ACF_UPLOAD_FOLDER="Завантажити папку" +ACF_UPLOAD_FOLDER_DESC="Введіть шлях відносно кореня вашого веб-простору, де зберігатимуться завантажені файли. Переконайтесь, що введений шлях написаний для запису, інакше завантаження файлу не вдасться." +ACF_UPLOAD_LIMIT_FILES="Обмеження файлів" +ACF_UPLOAD_LIMIT_FILES_DESC="Скільки файлів можна завантажити? Введіть 0 без обмежень." +ACF_UPLOAD_MAX_FILE_SIZE="Обмеження розміру файлу" +ACF_UPLOAD_MAX_FILE_SIZE_DESC="Налаштуйте максимально допустимий розмір для кожного завантаженого файлу в мегабайтах. Введіть 0 без обмежень.

Максимальний розмір завантаження вашого сервера: % s ." +ACF_UPLOAD_TYPES="Дозволені типи файлів" +ACF_UPLOAD_TYPES_DESC="Список розділених комами дозволених типів файлів. Залиште порожнім, щоб прийняти всі типи файлів.

Приклад: .jpg, .gif, .png, .pdf

Це не дурно, і їх можна обдурити, пам’ятайте, що завжди існує небезпека дозволити користувачам завантажувати файли. " +ACF_UPLOAD_ERROR="Недійсна форма або ключ поля" +ACF_UPLOAD_ERROR_CANNOT_UPLOAD_FILE="Неможливо завантажити файл" +ACF_UPLOAD_ERROR_INVALID_FIELD="Неправильне поле для завантаження" +ACF_UPLOAD_ERROR_INVALID_FILE="Недійсний або непідтримуваний файл" +ACF_UPLOAD_MAX_FILES_LIMIT="Ви можете завантажити до %d файлів" +ACF_UPLOAD_DRAG_AND_DROP_FILES="Перетягніть сюди файли або" +ACF_UPLOAD_BROWSE="Переглянути" +ACF_UPLOAD_INVALID_FILE_TYPE="Недійсний файл. Дозволені типи файлів: %s" +ACF_UPLOAD_FILE_IS_MISSING="Файл відсутній. Будь ласка, спробуйте повторно завантажити." +ACF_UPLOAD_FOLDER_INVALID="Папка для завантаження% s не існує або не піддається запису" +ACF_UPLOAD_FILETOOBIG="Файл занадто великий ({{розмір файлів}} МБ). Максимальний розмір файлів: {{maxFilesize}} МБ." +ACF_UPLOAD_INVALID_FILE="Ви не можете завантажувати файли такого типу." +ACF_UPLOAD_FALLBACK_MESSAGE="Ваш браузер не підтримує перетягування файлів перетягування." +ACF_UPLOAD_RESPONSE_ERROR = "Сервер відповів кодом {{statusCode}}." +ACF_UPLOAD_CANCEL_UPLOAD="Скасувати завантаження" +ACF_UPLOAD_CANCEL_UPLOAD_CONFIRMATION="Ви впевнені, що хочете скасувати це завантаження?" +ACF_UPLOAD_REMOVE_FILE="Видалити файл" +ACF_UPLOAD_MAX_FILES_EXCEEDED="Ви не можете завантажувати більше файлів." +ACF_UPLOAD_RANDOMIZE="Випадкові імена файлів" +ACF_UPLOAD_RANDOMIZE_DESC="Якщо увімкнено, до початку завантаженого імені файлу буде доданий випадковий префікс. Це допомагає гарантувати, що існуючі файли з тим самим іменем ніколи не замінюються." +ACF_UPLOAD_FILE_MISSING="Файл відсутній" +ACF_UPLOAD_FORCE_DOWNLOAD="Завантажити" +ACF_UPLOAD_FORCE_DOWNLOAD_DESC="Виберіть, чи потрібно завантажити файл замість переходу до посилання" +ACF_UPLOAD_LINK_TEXT="Текст посилання" +ACF_UPLOAD_LINK_TEXT_DESC="Введіть власний текст посилання. Якщо такого не вказано, замість цього буде використано ім'я файлу." +ACF_UPLOAD_LAYOUT="Макет" +ACF_UPLOAD_LAYOUT_DESC="Визначте макет, який використовуватиметься для відображення кожного завантаженого файлу в передній частині." +ACF_UPLOAD_CUSTOM_LAYOUT="Спеціальний макет" +ACF_UPLOAD_CUSTOM_LAYOUT_DESC="Встановити користувальницький макет HTML, який буде використовуватися для відображення кожного завантаженого файлу в передньому відділі.

Доступні змінні | file.name}
{file.path}
{file.url} | {file.size}
{file.ext} " +ACF_UPLOAD_LINK="Посилання" +ACF_UPLOAD_IMAGE="Зображення" +ACF_UPLOAD_CUSTOM="Спеціальні" +ACF_UPLOAD_FRONTEND_DISPLAY="Передній дисплей" +ACF_UPLOAD_FILE_UPLOAD_SETTINGS="Налаштування завантаження файлу" diff --git a/plugins/fields/acfupload/params/acfupload.xml b/plugins/fields/acfupload/params/acfupload.xml new file mode 100644 index 00000000..40cdc1a8 --- /dev/null +++ b/plugins/fields/acfupload/params/acfupload.xml @@ -0,0 +1,101 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfupload/script.install.helper.php b/plugins/fields/acfupload/script.install.helper.php new file mode 100644 index 00000000..bc1c58d0 --- /dev/null +++ b/plugins/fields/acfupload/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfuploadInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfupload/script.install.php b/plugins/fields/acfupload/script.install.php new file mode 100644 index 00000000..bf9f097b --- /dev/null +++ b/plugins/fields/acfupload/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFUploadInstallerScript extends PlgFieldsACFUploadInstallerScriptHelper +{ + public $alias = 'acfupload'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfupload/tmpl/acfupload.php b/plugins/fields/acfupload/tmpl/acfupload.php new file mode 100644 index 00000000..a11ad5b6 --- /dev/null +++ b/plugins/fields/acfupload/tmpl/acfupload.php @@ -0,0 +1,141 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$value = $field->value) +{ + return; +} + +require_once JPATH_SITE . '/plugins/fields/acfupload/fields/uploadhelper.php'; + +$limit_files = (int) $fieldParams->get('limit_files'); + +/** + * This handles backwards compatibility + */ +$files = is_string($value) ? json_decode($value, true) ?? [['value' => $value]] : $value; + +// Handle single file +if ($limit_files === 1 && is_array($files)) +{ + if (isset($files['value'])) + { + $files = [$files]; + } + + $files = [reset($files)]; +} + +$layout = $fieldParams->get('layout', 'link'); +$buffer = []; +$total_files = count($files); + +$index = 1; +foreach ($files as $value) +{ + // Make sure we have a value + if (!$value) + { + continue; + } + + $file = isset($value['value']) ? $value['value'] : $value; + $title = isset($value['title']) && !empty($value['title']) ? $value['title'] : ''; + $description = isset($value['description']) && !empty($value['description']) ? html_entity_decode(urldecode($value['description'])) : ''; + + $file_url = ACFUploadHelper::absURL($file); + + switch ($layout) + { + // Image + case 'img': + $item = ''; + break; + + // Custom Layout + case 'custom': + if (!$subject = $fieldParams->get('custom_layout')) + { + return; + } + + // Add the prefix "acf." to any "{file.*}" Smart Tags found + $new_format = 'acf.file.'; + $subject = preg_replace('/{file\.([^}]*)}/', '{'.$new_format.'$1}', $subject); + + $file_full_path = JPATH_SITE . '/' . $file; + $exists = is_file($file_full_path); + + // Always use framework's pathinfo to fight issues with non latin characters. + $filePathInfo = NRFramework\File::pathinfo($file); + + $st = new \NRFramework\SmartTags(); + + $file_tags = [ + 'index' => $index, + 'total' => $total_files, + 'name' => $filePathInfo['basename'], + 'basename' => $filePathInfo['basename'], + 'filename' => $filePathInfo['filename'], + 'ext' => $filePathInfo['extension'], + 'extension' => $filePathInfo['extension'], + 'path' => $file, + 'url' => $file_url, + 'size' => $exists ? ACFUploadHelper::humanFilesize(filesize($file_full_path)) : 0, + 'title' => $title, + 'description' => nl2br($description) + ]; + $st->add($file_tags, 'acf.file.'); + + $item = $st->replace($subject); + + break; + + // Link + default: + $item = 'get('force_download', true)) + { + $item .= ' download'; + } + + $link_text = $fieldParams->get('link_text', $file); + + $st = new \NRFramework\SmartTags(); + + // Always use framework's pathinfo to fight issues with non latin characters. + $filePathInfo = NRFramework\File::pathinfo($file); + + $file_tags = [ + 'index' => $index, + 'total' => $total_files, + 'basename' => $filePathInfo['basename'], + 'filename' => $filePathInfo['filename'], + 'extension' => $filePathInfo['extension'], + 'title' => $title, + 'description' => nl2br($description) + ]; + $st->add($file_tags, 'acf.file.'); + + $item .= '>' . $st->replace($link_text) . ''; + break; + } + + $buffer[] = $layout === 'custom' ? $item : '' . $item . ''; + + $index++; +} + +echo implode('', $buffer); \ No newline at end of file diff --git a/plugins/fields/acfupload/version.php b/plugins/fields/acfupload/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfupload/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfurl/acfurl.php b/plugins/fields/acfurl/acfurl.php new file mode 100644 index 00000000..c97c75c9 --- /dev/null +++ b/plugins/fields/acfurl/acfurl.php @@ -0,0 +1,115 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Language\Text; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFUrl extends ACF_Field +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType = 'UrlAdvanced'; + + /** + * Update the label of the field in filters. + * + * @param \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options + * + * @return \Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection + */ + public function onJFiltersOptionsAfterCreation(\Bluecoder\Component\Jfilters\Administrator\Model\Filter\Option\Collection $options) + { + // Make sure it is a field of that type + if ($options->getFilterItem()->getAttributes()->get('type') !== $this->_name) + { + return $options; + } + + foreach ($options as $option) + { + $data = $option->getData(); + $value = isset($data->value) ? $data->value : ''; + + if ($value) + { + $value = json_decode($value, true); + $value = isset($value['text']) ? Text::_($value['text']) : ''; + } + + $option->setLabel($value); + } + + return $options; + } + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + * + * @since 3.7.0 + */ + public function onContentPrepareForm(Form $form, $data) + { + // Make sure we are manipulating the right field. + if (isset($data->type) && ($data->type == $this->_name)) + { + $form->removeField('default_value'); + } + + return parent::onContentPrepareForm($form, $data); + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param JForm $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return; + } + + $fieldNode->setAttribute('default', json_encode([ + 'text' => $field->fieldparams->get('default_text'), + 'target' => $field->fieldparams->get('default_target', 'same_tab') + ])); + + return $fieldNode; + } +} \ No newline at end of file diff --git a/plugins/fields/acfurl/acfurl.xml b/plugins/fields/acfurl/acfurl.xml new file mode 100644 index 00000000..4d6ce622 --- /dev/null +++ b/plugins/fields/acfurl/acfurl.xml @@ -0,0 +1,22 @@ + + + ACF_URL + ACF_URL_DESC + Tassos Marinos + October 2018 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfurl.php + script.install.helper.php + version.php + fields + language + params + tmpl + + diff --git a/plugins/fields/acfurl/fields/urladvanced.php b/plugins/fields/acfurl/fields/urladvanced.php new file mode 100644 index 00000000..72fcb1c8 --- /dev/null +++ b/plugins/fields/acfurl/fields/urladvanced.php @@ -0,0 +1,85 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Form\Form; + +class JFormFieldUrlAdvanced extends FormField +{ + /** + * Method to attach a JForm object to the field. + * + * @param SimpleXMLElement $element The SimpleXMLElement object representing the tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. + * + * @return boolean True on success. + * + * @since 3.6 + */ + public function setup(SimpleXMLElement $element, $value, $group = null) + { + if (!parent::setup($element, $value, $group)) + { + return false; + } + + if ($this->value && is_string($this->value)) + { + // Guess here is the JSON string from 'default' attribute + $this->value = json_decode($this->value, true); + } + + return true; + } + + protected function getInput() + { + $form_source = new SimpleXMLElement(' +
+
+ + + + + + + +
+
+ '); + + $control = $this->name; + $formname = 'urladvanced.' . str_replace(['jform[', '[', ']'], ['', '.', ''], $control); + + $form = Form::getInstance($formname, $form_source->asXML(), ['control' => $control]); + $form->bind($this->value); + + return $form->renderFieldset('url'); + } +} \ No newline at end of file diff --git a/plugins/fields/acfurl/language/ca-ES/ca-ES.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/ca-ES/ca-ES.plg_fields_acfurl.ini new file mode 100644 index 00000000..f6b0f446 --- /dev/null +++ b/plugins/fields/acfurl/language/ca-ES/ca-ES.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Camps - ACF URL" +ACF_URL_DESC="Escriu un URL i un text a l'administració que es mostrarà com un enllaç a la part pública." +ACF_URL_VALUE_DESC="L'URL de l'enllaç" +ACF_URL_DEFAULT_TEXT="Text per defecte de l'enllaç" +ACF_URL_TEXT="Text de l'enllaç" +ACF_URL_TEXT_DESC="El text de l'enllaç" +ACF_URL_CLASS="Classe URL" +ACF_URL_CLASS_DESC="Personalitza l'URL amb les teves pròpies classes" +ACF_URL_DEFAULT_TARGET="Obrir a per defecte" +ACF_URL_TARGET="Obrir a" +ACF_URL_TARGET_DESC="Escull si obrir l'enllaç per defecte a la mateixa pestanya, a una de nova o a una finestra emergent." +ACF_URL_DL_LINK="Tractar com un enllaç de descàrrega" +ACF_URL_DL_LINK_DESC="Escull si vols descarregar l'arxiu enlloc de navegar-hi" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Estableix si afegir l'atribut NoFollow perquè els cercadors no vagin a aquest enllaç" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Escull si vols afegir l'atribut NoReferrer per tal de no enviar la capçalera HTTP del referent i que no en facin seguiment les eines d'analítica" +ACF_URL_ONCLICK="on Click" +ACF_URL_ONCLICK_DESC="Escriu codi Javascript a executar en clicar." +ACF_URL_TARGET_SAME_TAB="Mateixa pestanya" +ACF_URL_TARGET_NEW_TAB="Pestanya nova" +ACF_URL_TARGET_POPUP="Finestra emergent" diff --git a/plugins/fields/acfurl/language/da-DK/da-DK.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/da-DK/da-DK.plg_fields_acfurl.ini new file mode 100644 index 00000000..c6a91c61 --- /dev/null +++ b/plugins/fields/acfurl/language/da-DK/da-DK.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Felter - ACF URL" +ACF_URL_DESC="Angiv en URL og en tekst i backend og vis det som et link i frontend." +ACF_URL_VALUE_DESC="URL'en for linket" +ACF_URL_DEFAULT_TEXT="Standard linktekst" +ACF_URL_TEXT="Linktekst" +ACF_URL_TEXT_DESC="Teksten på linket of the link" +ACF_URL_CLASS="URL klasse" +ACF_URL_CLASS_DESC="Brugerdefiner URLen med dine egne CSS klasser" +ACF_URL_DEFAULT_TARGET="Standard åben iOpen in" +ACF_URL_TARGET="Åben i" +ACF_URL_TARGET_DESC="Vælg at åbne linket i samme fane, i en ny fane eller i et popup vindue." +ACF_URL_DL_LINK="Behandl som et download link" +ACF_URL_DL_LINK_DESC="Vælg om filen skal downloades i stedet for at der navigeres til linket" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Vælg om attributten NoFollow skal tilføjes, så søgemaskiner ikke følger dette link" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Vælg om attributten NoReferrer skal tilføjes, som så ikke vil sende HTTP henviser hovedet, så det ikke kan spores af analyseværktøjer" +ACF_URL_ONCLICK="ved klik" +ACF_URL_ONCLICK_DESC="Angiv Javascript kode der skal køres ved klik." +ACF_URL_TARGET_SAME_TAB="Samme fane" +ACF_URL_TARGET_NEW_TAB="Ny fane" +ACF_URL_TARGET_POPUP="Popup vindue" diff --git a/plugins/fields/acfurl/language/de-DE/de-DE.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/de-DE/de-DE.plg_fields_acfurl.ini new file mode 100644 index 00000000..c6aadcea --- /dev/null +++ b/plugins/fields/acfurl/language/de-DE/de-DE.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Felder - ACF URL" +ACF_URL_DESC="Geben Sie eine URL und einen Text in das Backend ein und zeigen Sie diese als Link im Frontend an." +ACF_URL_VALUE_DESC="Die URL des Links" +ACF_URL_DEFAULT_TEXT="Standard-Linktext" +ACF_URL_TEXT="Linktext" +ACF_URL_TEXT_DESC="Der Text des Links" +ACF_URL_CLASS="URL-Klasse" +ACF_URL_CLASS_DESC="Passen Sie die URL mit Ihren eigenen CSS-Klassen an" +ACF_URL_DEFAULT_TARGET="Standardmäßig geöffnet in" +ACF_URL_TARGET="Öffnen in" +ACF_URL_TARGET_DESC="Wählen Sie diese Option, um den Link auf derselben Registerkarte, in einer neuen Registerkarte oder in einem Popup-Fenster zu öffnen." +ACF_URL_DL_LINK="Als Download-Link behandeln" +ACF_URL_DL_LINK_DESC="Wählen Sie aus, ob die Datei heruntergeladen werden soll, anstatt zum Link zu navigieren." +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Wählen Sie aus, ob das NoFollow-Attribut hinzugefügt werden soll, damit Suchmaschinen diesem Link nicht folgen." +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Wählen Sie aus, ob das NoReferrer-Attribut hinzugefügt werden soll, das den HTTP-Referrer-Kopf nicht sendet, damit er nicht von den Analysetools verfolgt wird." +ACF_URL_ONCLICK="bei Klick" +ACF_URL_ONCLICK_DESC="Geben Sie den Javascript-Code ein, der bei Klick ausgeführt werden soll." +ACF_URL_TARGET_SAME_TAB="Gleiche Registerkarte" +ACF_URL_TARGET_NEW_TAB="Neuer Tab" +ACF_URL_TARGET_POPUP="Popup-Fenster" diff --git a/plugins/fields/acfurl/language/en-GB/en-GB.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/en-GB/en-GB.plg_fields_acfurl.ini new file mode 100644 index 00000000..d693e75d --- /dev/null +++ b/plugins/fields/acfurl/language/en-GB/en-GB.plg_fields_acfurl.ini @@ -0,0 +1,32 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Fields - ACF URL" +ACF_URL_DESC="Enter a URL and a Text in the back-end and display it as a link in the front-end." +ACF_URL_VALUE_DESC="The URL of the link" +ACF_URL_DEFAULT_TEXT="Default Link Text" +ACF_URL_TEXT="Link Text" +ACF_URL_TEXT_DESC="The text of the link" +ACF_URL_CLASS="URL Class" +ACF_URL_CLASS_DESC="Customize the URL with your own CSS classes" +ACF_URL_DEFAULT_TARGET="Default Open in" +ACF_URL_TARGET="Open in" +ACF_URL_TARGET_DESC="Select to open the link in the same tab, in a new tab or in a popup window." +ACF_URL_DL_LINK="Treat as Download Link" +ACF_URL_DL_LINK_DESC="Select whether to download the file instead of navigating to the link" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Select whether to add NoFollow attribute so search engines wont follow this link" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Select whether to add NoReferrer attribute which won't send the HTTP referrer head so it won't be tracked by analytics tools" +ACF_URL_ONCLICK="on Click" +ACF_URL_ONCLICK_DESC="Enter Javascript code to run on click." +ACF_URL_TARGET_SAME_TAB="Same tab" +ACF_URL_TARGET_NEW_TAB="New tab" +ACF_URL_TARGET_POPUP="Popup window" +ACF_URL_NOOPENER="NoOpener" +ACF_URL_NOOPENER_DESC="Select whether to add NoOpener attribute to prevent the new tab from giving context access to the original page.

What this does is essentially prevent any macilious code that may be present on the new tab to steal information from the original page.

Note that this attribute is already added by default by all modern browsers to external links.

Note: On links that open in new tab, this will always be applied." \ No newline at end of file diff --git a/plugins/fields/acfurl/language/en-GB/en-GB.plg_fields_acfurl.sys.ini b/plugins/fields/acfurl/language/en-GB/en-GB.plg_fields_acfurl.sys.ini new file mode 100644 index 00000000..95ec0699 --- /dev/null +++ b/plugins/fields/acfurl/language/en-GB/en-GB.plg_fields_acfurl.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_URL="Fields - ACF URL" +ACF_URL_DESC="Enter a URL and a Text in the back-end and display it as a link in the front-end." \ No newline at end of file diff --git a/plugins/fields/acfurl/language/es-ES/es-ES.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/es-ES/es-ES.plg_fields_acfurl.ini new file mode 100644 index 00000000..e98b802b --- /dev/null +++ b/plugins/fields/acfurl/language/es-ES/es-ES.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Campos - ACF URL" +ACF_URL_DESC="Ingrese una URL y un Texto en el back-end y muéstrelo como un enlace en el front-end." +ACF_URL_VALUE_DESC="La URL del enlace" +ACF_URL_DEFAULT_TEXT="Texto de vínculo predeterminado" +ACF_URL_TEXT="Texto del Enlace" +ACF_URL_TEXT_DESC="El texto del enlace" +ACF_URL_CLASS="Clase de URL" +ACF_URL_CLASS_DESC="Personalizar la URL con sus propias clases de CSS" +ACF_URL_DEFAULT_TARGET="Predeterminado Abrir en" +ACF_URL_TARGET="Abrir en" +ACF_URL_TARGET_DESC="Seleccione para abrir el enlace en la misma pestaña, en una nueva pestaña o en una ventana emergente." +ACF_URL_DL_LINK="Tratar como enlace de descarga" +ACF_URL_DL_LINK_DESC="Seleccione si desea descargar el archivo en lugar de navegar hasta el enlace" +ACF_URL_NOFOLLOW="No Seguir" +ACF_URL_NOFOLLOW_DESC="Seleccione si desea agregar el atributo NoFollow para que los motores de búsqueda no sigan este enlace" +ACF_URL_NOREFERRER="Sin Referencia" +ACF_URL_NOREFERRER_DESC="Seleccione si desea agregar el atributo NoReferrer que no enviará el encabezado de referencia HTTP para que las herramientas de análisis no lo rastreen." +ACF_URL_ONCLICK="al hacer clic" +ACF_URL_ONCLICK_DESC="Ingrese el código Javascript para ejecutar al hacer clic." +ACF_URL_TARGET_SAME_TAB="Misma pestaña" +ACF_URL_TARGET_NEW_TAB="Nueva pestaña" +ACF_URL_TARGET_POPUP="Ventana emergente" diff --git a/plugins/fields/acfurl/language/es-ES/es-ES.plg_fields_acfurl.sys.ini b/plugins/fields/acfurl/language/es-ES/es-ES.plg_fields_acfurl.sys.ini new file mode 100644 index 00000000..ebc878d0 --- /dev/null +++ b/plugins/fields/acfurl/language/es-ES/es-ES.plg_fields_acfurl.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_URL="Campos - ACF URL" +ACF_URL_DESC="Ingrese una URL y un Texto en el back-end y muéstrelo como un enlace en el front-end." diff --git a/plugins/fields/acfurl/language/fr-FR/fr-FR.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/fr-FR/fr-FR.plg_fields_acfurl.ini new file mode 100644 index 00000000..c2305b43 --- /dev/null +++ b/plugins/fields/acfurl/language/fr-FR/fr-FR.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Champs - ACF URL" +ACF_URL_DESC="Saisir une URL et un texte en arrière-plan et affichez-le en tant que lien sur le site public." +ACF_URL_VALUE_DESC="URL du lien. Ne pas oublier http:// ou https://" +ACF_URL_DEFAULT_TEXT="Texte par défaut du lien" +ACF_URL_TEXT="Texte du lien" +ACF_URL_TEXT_DESC="Le texte du lien" +ACF_URL_CLASS="Classe CSS de l'URL" +ACF_URL_CLASS_DESC="Personnaliser l'URL avec vos propres classes CSS" +ACF_URL_DEFAULT_TARGET="Par défaut, ouvrir dans" +ACF_URL_TARGET="Ouvrir dans" +ACF_URL_TARGET_DESC="Choisir d'ouvrir le lien dans le même onglet, dans un nouvel onglet ou dans une fenêtre popup." +ACF_URL_DL_LINK="Traiter comme un lien de téléchargement" +ACF_URL_DL_LINK_DESC="Choisir de télécharger le fichier au lieu de naviguer vers le lien" +; ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Sélectionner pour ajouter l'attribut NoFollow afin que les moteurs de recherche ne suivent pas ce lien" +; ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Choisir d’ajouter ou non l’attribut NoReferrer qui n’enverra pas l'en-tête HTTP de référence et qui ne sera donc pas suivie par les outils d’analyse" +; ACF_URL_ONCLICK="on Click" +ACF_URL_ONCLICK_DESC="Entrer le code Javascript à exécuter au moment du clic." +ACF_URL_TARGET_SAME_TAB="Même onglet" +ACF_URL_TARGET_NEW_TAB="Nouvel onglet" +ACF_URL_TARGET_POPUP="Fenêtre popup" diff --git a/plugins/fields/acfurl/language/it-IT/it-IT.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/it-IT/it-IT.plg_fields_acfurl.ini new file mode 100644 index 00000000..f6a7a0e8 --- /dev/null +++ b/plugins/fields/acfurl/language/it-IT/it-IT.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Campi - URL ACF" +ACF_URL_DESC="Immettere un URL e un testo in amministrazione e visualizzarli come collegamento nel front-end." +ACF_URL_VALUE_DESC="L'URL del collegamento" +ACF_URL_DEFAULT_TEXT="Testo predefinito del collegamento" +ACF_URL_TEXT="Testo collegamento" +ACF_URL_TEXT_DESC="Il testo del collegamento" +ACF_URL_CLASS="Classe URL" +ACF_URL_CLASS_DESC="Personalizza l'URL con le tue classi CSS" +ACF_URL_DEFAULT_TARGET="Apri in predefinito" +ACF_URL_TARGET="Apri in" +ACF_URL_TARGET_DESC="Selezionare per aprire il collegamento nella stessa scheda, in una nuova scheda o in una finestra popup." +ACF_URL_DL_LINK="Utilizza come collegamento di scaricamento" +ACF_URL_DL_LINK_DESC="Scegli se scaricare il file invece di navigare fino al collegamento" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Seleziona se aggiungere l'attributo NoFollow in modo che i motori di ricerca non seguano questo collegamento" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Seleziona se aggiungere l'attributo NoReferrer che non invierà l'intestazione del referrer HTTP in modo che non venga tracciato dagli strumenti di analisi dei dati" +ACF_URL_ONCLICK="al clic" +ACF_URL_ONCLICK_DESC="Inserisci il codice Javascript da eseguire al clic." +ACF_URL_TARGET_SAME_TAB="Stessa scheda" +ACF_URL_TARGET_NEW_TAB="Nuova scheda" +ACF_URL_TARGET_POPUP="Finestra popup" diff --git a/plugins/fields/acfurl/language/nl-NL/nl-NL.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/nl-NL/nl-NL.plg_fields_acfurl.ini new file mode 100644 index 00000000..f260ab12 --- /dev/null +++ b/plugins/fields/acfurl/language/nl-NL/nl-NL.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Velsden - ACF URL" +ACF_URL_DESC="Voer een URL en een tekst in op de back-end en toon deze als een link aan de front-end." +ACF_URL_VALUE_DESC="De URL van de link" +ACF_URL_DEFAULT_TEXT="Standaard Link Tekst" +ACF_URL_TEXT="Link Tekst" +ACF_URL_TEXT_DESC="De tekst van de link" +ACF_URL_CLASS="URL Klasse" +ACF_URL_CLASS_DESC="Pas de URL aan met klasses van jezelf" +ACF_URL_DEFAULT_TARGET="Standaard Openen in" +ACF_URL_TARGET="Open in" +ACF_URL_TARGET_DESC="Selecteer om de link te openen op het zelfde tabblad, op een nieuw tabblad of in een pop-upvenster ." +ACF_URL_DL_LINK="Behandel als downloadlink" +ACF_URL_DL_LINK_DESC="Selecteer of u het bestand wilt downloaden in plaats van de link te volgen" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Selecteer of je het kenmerk NoFollow wilt toevoegen zodat zoekmachines deze link niet zullen volgen" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Selecteer of je het NoReferrer-kenmerk wilt toevoegen dat de HTTP-verwijzingskop niet verzend en niet zal worden bijgehouden door analysetools" +ACF_URL_ONCLICK="on Click" +ACF_URL_ONCLICK_DESC="Voer Javascript-code in om te draaien zodra er geklikt is" +ACF_URL_TARGET_SAME_TAB="Zelfde Tabblad" +ACF_URL_TARGET_NEW_TAB="Nieuw Tabblad" +ACF_URL_TARGET_POPUP="Popup Scherm" diff --git a/plugins/fields/acfurl/language/ru-RU/ru-RU.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/ru-RU/ru-RU.plg_fields_acfurl.ini new file mode 100644 index 00000000..e148bae0 --- /dev/null +++ b/plugins/fields/acfurl/language/ru-RU/ru-RU.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Поля - ACF URL" +ACF_URL_DESC="Введите URL-адрес и текст на серверной части и отобразите их в виде ссылки на серверной части." +ACF_URL_VALUE_DESC="URL ссылки" +ACF_URL_DEFAULT_TEXT="Текст ссылки по умолчанию" +ACF_URL_TEXT="Текст ссылки" +ACF_URL_TEXT_DESC="Текст ссылки" +ACF_URL_CLASS="Класс URL" +ACF_URL_CLASS_DESC="Настройте URL с вашими собственными классами CSS" +ACF_URL_DEFAULT_TARGET="Открытие по умолчанию в" +ACF_URL_TARGET="Открыть в" +ACF_URL_TARGET_DESC="Выберите, чтобы открыть ссылку на той же вкладке, на новой вкладке или во всплывающем окне." +ACF_URL_DL_LINK="Рассматривать как ссылку для скачивания" +ACF_URL_DL_LINK_DESC="Выберите, загружать ли файл вместо перехода по ссылке" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Выберите, следует ли добавить атрибут NoFollow, чтобы поисковые системы не переходили по этой ссылке" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Выберите, следует ли добавить атрибут NoReferrer, который не будет отправлять голову реферера HTTP, чтобы он не отслеживался инструментами аналитики" +ACF_URL_ONCLICK="при клике" +ACF_URL_ONCLICK_DESC="Введите код Javascript для запуска при клике." +ACF_URL_TARGET_SAME_TAB="Та же вкладка" +ACF_URL_TARGET_NEW_TAB="Новая вкладка" +ACF_URL_TARGET_POPUP="Всплывающее окно" diff --git a/plugins/fields/acfurl/language/sv-SE/sv-SE.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/sv-SE/sv-SE.plg_fields_acfurl.ini new file mode 100644 index 00000000..a03242b3 --- /dev/null +++ b/plugins/fields/acfurl/language/sv-SE/sv-SE.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Fält - ACF URL" +ACF_URL_DESC="Ange en URL och en text i backend och visa den som en länk i frontend." +ACF_URL_VALUE_DESC="Länkens URL" +ACF_URL_DEFAULT_TEXT="Standard länktext" +ACF_URL_TEXT="Länktext" +ACF_URL_TEXT_DESC="Länkens text." +ACF_URL_CLASS="URL klass" +ACF_URL_CLASS_DESC="Anpassa URLen med egna CSS-klasser." +ACF_URL_DEFAULT_TARGET="Standard öppna i" +ACF_URL_TARGET="Öppna i" +ACF_URL_TARGET_DESC="Välj om länken ska öppnas i samma flik, i en ny flik eller i ett popup-fönster." +ACF_URL_DL_LINK="Behandla som nedladdningslänk" +ACF_URL_DL_LINK_DESC="Välj om du vill att filen ska laddas ner istället för att navigera till länken" +ACF_URL_NOFOLLOW="NoFollow" +ACF_URL_NOFOLLOW_DESC="Välj om du vill lägga till NoFollow-attribut så att sökmotorer inte följer den här länken" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Välj om du vill lägga till NoReferrer-attribut som stoppar HTTP-referrerhuvudet att skickas, så att det inte spåras av analysverktyg" +ACF_URL_ONCLICK="on Click" +ACF_URL_ONCLICK_DESC="Ange Javascript-kod för att köra vid klick." +ACF_URL_TARGET_SAME_TAB="Samma flik" +ACF_URL_TARGET_NEW_TAB="My flik" +ACF_URL_TARGET_POPUP="Popup-fönster" diff --git a/plugins/fields/acfurl/language/uk-UA/uk-UA.plg_fields_acfurl.ini b/plugins/fields/acfurl/language/uk-UA/uk-UA.plg_fields_acfurl.ini new file mode 100644 index 00000000..ac300caa --- /dev/null +++ b/plugins/fields/acfurl/language/uk-UA/uk-UA.plg_fields_acfurl.ini @@ -0,0 +1,30 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFURL_LABEL="ACF - URL" +ACF_URL="Поля - URL-адреса ACF" +ACF_URL_DESC="Введіть URL-адресу та текст у зворотній частині та відображіть їх як посилання на передньому." +ACF_URL_VALUE_DESC="URL-адреса посилання" +ACF_URL_DEFAULT_TEXT="Текст посилання за замовчуванням" +ACF_URL_TEXT="Текст посилання" +ACF_URL_TEXT_DESC="Текст посилання" +ACF_URL_CLASS="Клас URL" +ACF_URL_CLASS_DESC="Налаштувати URL-адресу за допомогою власних класів CSS" +ACF_URL_DEFAULT_TARGET="Відкрити за замовчуванням" +ACF_URL_TARGET="Відкрити в" +ACF_URL_TARGET_DESC="Виберіть, щоб відкрити посилання на тій же вкладці, у новій вкладці або у спливаючому вікні." +ACF_URL_DL_LINK="Розглянути як посилання для завантаження" +ACF_URL_DL_LINK_DESC="Виберіть, чи потрібно завантажити файл замість переходу до посилання" +ACF_URL_NOFOLLOW="Не виконувати" +ACF_URL_NOFOLLOW_DESC="Виберіть, чи потрібно додати атрибут NoFollow, щоб пошукові системи не переходили за цим посиланням" +ACF_URL_NOREFERRER="NoReferrer" +ACF_URL_NOREFERRER_DESC="Виберіть, чи потрібно додати атрибут NoReferrer, який не буде надсилати голову реферала HTTP, щоб його не відстежували інструменти аналітики" +ACF_URL_ONCLICK="при натисканні" +ACF_URL_ONCLICK_DESC="Введіть код Javascript для запуску при натисканні." +ACF_URL_TARGET_SAME_TAB="Та сама вкладка" +ACF_URL_TARGET_NEW_TAB="Нова вкладка" +ACF_URL_TARGET_POPUP="Вікно спливаючого вікна" diff --git a/plugins/fields/acfurl/params/acfurl.xml b/plugins/fields/acfurl/params/acfurl.xml new file mode 100644 index 00000000..b33bdcc9 --- /dev/null +++ b/plugins/fields/acfurl/params/acfurl.xml @@ -0,0 +1,54 @@ + +
+ +
+ + + + + + + + + + + + + + + + + +
+
+
+ diff --git a/plugins/fields/acfurl/script.install.helper.php b/plugins/fields/acfurl/script.install.helper.php new file mode 100644 index 00000000..5db7abcd --- /dev/null +++ b/plugins/fields/acfurl/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfurlInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfurl/script.install.php b/plugins/fields/acfurl/script.install.php new file mode 100644 index 00000000..9050ff7a --- /dev/null +++ b/plugins/fields/acfurl/script.install.php @@ -0,0 +1,23 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFUrlInstallerScript extends PlgFieldsACFUrlInstallerScriptHelper +{ + public $alias = 'acfurl'; + public $extension_type = 'plugin'; + public $plugin_folder = "fields"; + public $show_message = false; +} diff --git a/plugins/fields/acfurl/tmpl/acfurl.php b/plugins/fields/acfurl/tmpl/acfurl.php new file mode 100644 index 00000000..43830c1e --- /dev/null +++ b/plugins/fields/acfurl/tmpl/acfurl.php @@ -0,0 +1,88 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\Registry\Registry; +use Joomla\CMS\Language\Text; + +if (!$url = $field->value) +{ + return; +} + +$url = new Registry($url); + +if (empty($url['url'])) +{ + return; +} + +$id = 'acf_url_' . $item->id . '_' . $field->id; + +// Output +$rel = []; +$CSSClass = trim('acf_url ' . $fieldParams->get('url_class')); +$buffer = 'get('noopener', '1') === '1'; + +// Add noopener rel attribute +$rel = array_merge($rel, [ + 'noopener' => $noopener +]); + +// Set target attribute +if ($url->get('target') == 'new_tab') +{ + $buffer .= ' target="_blank"'; + + // Force it on new_tab links + $rel = array_merge($rel, [ + 'noopener' => 1 + ]); +} + +if ($url->get('target') == 'popup') +{ + $onclick = $fieldParams->get('onclick'); + $new_window_code = 'window.open(\'' . $url->get('url') . '\', \'_blank\', \'width=800,height=600\'); return false;'; + $fieldParams->set('onclick', $onclick . $new_window_code); +} + +// Set the onClick handler - Do not remove this block from Free version as it's required by the target property. +$onclick = $fieldParams->get('onclick'); +if (!empty($onclick)) { + $buffer .= ' onclick="' . $onclick . '"'; +} + + +// Set rel attributes +$rel = array_filter(array_merge($rel, [ + 'noreferrer' => $fieldParams->get('noreferrer', false), + 'nofollow' => $fieldParams->get('nofollow', false) +])); + +// Treat it as a download link +if ($fieldParams->get('treat_as_download_link', false)) +{ + $buffer .= ' download'; +} + + +if ($rel) +{ + $buffer .= ' rel="' . implode(' ', array_keys($rel)) . '"'; +} + +$buffer .= '>' . Text::_($url->get('text', $fieldParams->get('default_text'))) . ''; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfurl/version.php b/plugins/fields/acfurl/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfurl/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/fields/acfvideo/acfvideo.php b/plugins/fields/acfvideo/acfvideo.php new file mode 100644 index 00000000..af7a4bba --- /dev/null +++ b/plugins/fields/acfvideo/acfvideo.php @@ -0,0 +1,59 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFVideo extends ACF_Field +{ + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return; + } + + // Self Hosted Videos accept relative paths which won't pass the "url" validation + if ($field->fieldparams->get('provider') != 'SelfHostedVideo') + { + $this->validate = 'url'; + } + else + { + $this->validate = null; + $fieldNode->setAttribute('validate', ''); + } + + return parent::onCustomFieldsPrepareDom($field, $parent, $form); + } +} \ No newline at end of file diff --git a/plugins/fields/acfvideo/acfvideo.xml b/plugins/fields/acfvideo/acfvideo.xml new file mode 100644 index 00000000..1deda5aa --- /dev/null +++ b/plugins/fields/acfvideo/acfvideo.xml @@ -0,0 +1,36 @@ + + + ACF_VIDEO + ACF_VIDEO_DESC + Tassos Marinos + August 2023 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfvideo.php + script.install.helper.php + version.php + fields + params + tmpl + language + + + +
+ +
+
+
+ + img + +
diff --git a/plugins/fields/acfvideo/fields/acfvideo.php b/plugins/fields/acfvideo/fields/acfvideo.php new file mode 100644 index 00000000..74323962 --- /dev/null +++ b/plugins/fields/acfvideo/fields/acfvideo.php @@ -0,0 +1,56 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\Field\TextField; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; + +class JFormFieldACFVideo extends TextField +{ + /** + * Renders the input field with the video previewer. + * + * @return string The field input markup. + */ + protected function getInput() + { + $provider = (string) $this->element['provider']; + + $xml = ' + + '; + + $this->form->setField(new SimpleXMLElement($xml)); + $field = $this->form->getField($this->fieldname, null, $this->value); + $field->name = $this->name; + $field->id = $this->id; + + return $field->getInput(); + } + + private function isPreviewerEnabled() + { + $plugin = PluginHelper::getPlugin('fields', 'acfvideo'); + $params = new Registry($plugin->params); + + return $params->get('enable_previewer', '1') === '1'; + } +} diff --git a/plugins/fields/acfvideo/language/en-GB/en-GB.plg_fields_acfvideo.ini b/plugins/fields/acfvideo/language/en-GB/en-GB.plg_fields_acfvideo.ini new file mode 100644 index 00000000..5ee947c0 --- /dev/null +++ b/plugins/fields/acfvideo/language/en-GB/en-GB.plg_fields_acfvideo.ini @@ -0,0 +1,78 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFVIDEO_LABEL="ACF - Video" +ACF_VIDEO="Fields - ACF Video" +ACF_VIDEO_DESC="Enter a video from YouTube, Vimeo, Dailymotion, Facebook, or even self-hosted videos." +ACF_VIDEO_VALUE_DESC="Enter a video URL." +ACF_VIDEO_PROVIDER="Provider" +ACF_VIDEO_PROVIDER_DESC="Select which provider to use to display the video." +ACF_VIDEO_WIDTH="Video Width" +ACF_VIDEO_WIDTH_DESC="Set the video's width." +ACF_VIDEO_HEIGHT="Video Height" +ACF_VIDEO_HEIGHT_DESC="Set the video's height." +ACF_VIDEO_AUTOPLAY="Autoplay" +ACF_VIDEO_AUTOPLAY_DESC="Set whether the video will automatically start to play when the player loads." +ACF_VIDEO_AUTOPAUSE="Autopause" +ACF_VIDEO_AUTOPAUSE_DESC="Set whether the video will be auto paused when it goes outside the viewport." +ACF_VIDEO_CLOSED_CAPTIONS="Closed Captions" +ACF_VIDEO_CLOSED_CAPTIONS_DESC="Enable to make closed captions to be shown by default, even if the user has turned captions off." +ACF_VIDEO_COLOR="Color" +ACF_VIDEO_COLOR_DESC="Set the color that will be used in the player's video progress bar to highlight the amount of the video that the viewer has already seen." +ACF_VIDEO_COLOR_RED="Red" +ACF_VIDEO_COLOR_WHITE="White" +ACF_VIDEO_CONTROLS="Controls" +ACF_VIDEO_CONTROLS_DESC="Set whether the video player controls are displayed." +ACF_VIDEO_DISABLE_KEYBOARD="Disable Keyboard Shortcuts" +ACF_VIDEO_DISABLE_KEYBOARD_DESC="Enable to make the player to not respond to keyboard controls." +ACF_VIDEO_START="Start Time" +ACF_VIDEO_START_DESC="Specify the time, measured in seconds from the start of the video, when the player will start playing the video." +ACF_VIDEO_END="End Time" +ACF_VIDEO_END_DESC="Specify the time, measured in seconds from the start of the video, when the player will stop playing the video." +ACF_VIDEO_FULLSCREEN="Fullscreen" +ACF_VIDEO_FULLSCREEN_DESC="Set whether to allow the video to be played in fullscreen mode." +ACF_VIDEO_LOOP="Loop" +ACF_VIDEO_LOOP_DESC="Set whether the player will repeatedly play the video." +ACF_VIDEO_MODESTBRANDING="Modest Branding" +ACF_VIDEO_MODESTBRANDING_DESC="Enable to prevent the YouTube logo from displaying in the control bar. Note that a small YouTube text label will still display in the upper-right corner of a paused video when the user's mouse pointer hovers over the player." +ACF_VIDEO_REL="Related Videos" +ACF_VIDEO_REL_DESC="Enable to show related videos when playback of the initial video ends." +ACF_VIDEO_PRIVACY_MODE="Privacy-Enhanced Mode" +ACF_VIDEO_PRIVACY_MODE_DESC="When you turn on privacy-enhanced mode, YouTube won't store information about visitors on your website unless they play the video." +ACF_VIDEO_YOUTUBE="YouTube" +ACF_VIDEO_DAILYMOTION="DailyMotion" +ACF_VIDEO_VIMEO="Vimeo" +ACF_VIDEO_FACEBOOK="Facebook" +ACF_VIDEO_SELF_HOSTED_VIDEO="Self-Hosted Video" +ACF_VIDEO_MUTE="Mute" +ACF_VIDEO_MUTE_DESC="Whether to mute the video or not" +ACF_VIDEO_COVER_IMAGE_TYPE="Cover Image Type" +ACF_VIDEO_COVER_IMAGE_TYPE_DESC="Select the cover image type." +ACF_VIDEO_COVER_IMAGE="Cover Image" +ACF_VIDEO_SELECT_COVER_IMAGE="Select Cover Image" +ACF_VIDEO_SELECT_COVER_IMAGE_DESC="Select a custom cover image." +ACF_VIDEO_NO_COVER_IMAGE="No Cover Image" +ACF_VIDEO_AUTO_COVER="Auto (From video provider)" +ACF_VIDEO_CUSTOM="Custom" +ACF_VIDEO_TITLE="Display Title" +ACF_VIDEO_TITLE_DESC="Choose whether to display the title of the video, only if the owner allows it." +ACF_VIDEO_BYLINE="Display Byline" +ACF_VIDEO_BYLINE_DESC="Choose to display the video's byline, only if the owner allows it.

The byline is everything that is displayed right under the title. In most occassions it's the creator's username." +ACF_VIDEO_PORTRAIT="Display Portrait" +ACF_VIDEO_PORTRAIT_DESC="Choose to display the creator's portrait, only if the owner allows it." +ACF_VIDEO_VIMEO_COLOR="UI Color" +ACF_VIDEO_VIMEO_COLOR_DESC="Pick a color for the video's UI

This color will be used for the title, the byline and the player controls." +ACF_VIDEO_PIP="Picture In Picture" +ACF_VIDEO_PIP_DESC="Enable to show the picture-in-picture button in the control bar." +ACF_VIDEO_FACEBOOKVIDEO_INCLUDEPOST="Include Post" +ACF_VIDEO_FACEBOOKVIDEO_INCLUDEPOST_DESC="Enable to include the text from the Facebook post associated with the video, if any. Only available for desktop sites." +ACF_VIDEO_FACEBOOKVIDEO_SHOWCAPTIONS="Show Captions" +ACF_VIDEO_FACEBOOKVIDEO_SHOWCAPTIONS_DESC="Enable to show captions (if available) by default. Captions are only available on desktop." +ACF_VIDEO_PREVIEW_VIDEO="Preview video" +ACF_VIDEO_ENABLE_PREVIEWER="Enable Previewer" +ACF_VIDEO_ENABLE_PREVIEWER_DESC="Set whether to enable the previewer upon finishing typing a URL when editing an item (article, contact, user, etc...)." +ACF_VIDEO_TYPE_A_PROVIDER_VIDEO_URL="Type a%s video URL..." \ No newline at end of file diff --git a/plugins/fields/acfvideo/language/en-GB/en-GB.plg_fields_acfvideo.sys.ini b/plugins/fields/acfvideo/language/en-GB/en-GB.plg_fields_acfvideo.sys.ini new file mode 100644 index 00000000..125ea828 --- /dev/null +++ b/plugins/fields/acfvideo/language/en-GB/en-GB.plg_fields_acfvideo.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_VIDEO="Fields - ACF Video" +ACF_VIDEO_DESC="Enter a video from YouTube, Vimeo, Dailymotion, Facebook, or even self-hosted videos." \ No newline at end of file diff --git a/plugins/fields/acfvideo/language/es-ES/es-ES.plg_fields_acfvideo.ini b/plugins/fields/acfvideo/language/es-ES/es-ES.plg_fields_acfvideo.ini new file mode 100644 index 00000000..aa7f46c8 --- /dev/null +++ b/plugins/fields/acfvideo/language/es-ES/es-ES.plg_fields_acfvideo.ini @@ -0,0 +1,78 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFVIDEO_LABEL="ACF - Vídeo" +ACF_VIDEO="Campos - ACF Vídeo" +ACF_VIDEO_DESC="Ingrese un vídeo de YouTube, Vimeo, Dailymotion, Facebook o incluso vídeos auto hospedados." +ACF_VIDEO_VALUE_DESC="Introduzca una URL de vídeo." +ACF_VIDEO_PROVIDER="Proveedor" +ACF_VIDEO_PROVIDER_DESC="Seleccione qué proveedor usar para mostrar el vídeo." +ACF_VIDEO_WIDTH="Ancho del vídeo" +ACF_VIDEO_WIDTH_DESC="Establezca el ancho del vídeo." +ACF_VIDEO_HEIGHT="Altura del vídeo" +ACF_VIDEO_HEIGHT_DESC="Establezca la altura del vídeo." +ACF_VIDEO_AUTOPLAY="Auto-reproducción" +ACF_VIDEO_AUTOPLAY_DESC="Establezca si el vídeo comenzará a reproducirse automáticamente cuando se cargue el reproductor." +ACF_VIDEO_AUTOPAUSE="Pausa Automática" +ACF_VIDEO_AUTOPAUSE_DESC="Establezca si el video se pausará automáticamente cuando salga de la ventana gráfica." +ACF_VIDEO_CLOSED_CAPTIONS="Subtítulos" +ACF_VIDEO_CLOSED_CAPTIONS_DESC="Habilite esta opción para que los subtítulos ocultos se muestren de forma predeterminada, incluso si el usuario los ha desactivado." +ACF_VIDEO_COLOR="Color" +ACF_VIDEO_COLOR_DESC="Establezca el color que se usará en la barra de progreso del video del reproductor para resaltar la cantidad de video que el espectador ya ha visto." +ACF_VIDEO_COLOR_RED="Rojo" +ACF_VIDEO_COLOR_WHITE="Blanco" +ACF_VIDEO_CONTROLS="Controles" +ACF_VIDEO_CONTROLS_DESC="Establezca si se muestran los controles del reproductor de video." +ACF_VIDEO_DISABLE_KEYBOARD="Deshabilitar atajos de teclado" +ACF_VIDEO_DISABLE_KEYBOARD_DESC="Habilite para hacer que el reproductor no responda a los controles del teclado." +ACF_VIDEO_START="Tiempo de Inicio" +ACF_VIDEO_START_DESC="Especifique el tiempo, medido en segundos desde el inicio del video, cuando el reproductor comenzará a reproducir el video." +ACF_VIDEO_END="Tiempo Final" +ACF_VIDEO_END_DESC="Especifique el tiempo, medido en segundos desde el inicio del video, cuando el reproductor dejará de reproducir el video." +ACF_VIDEO_FULLSCREEN="Pantalla Completa" +ACF_VIDEO_FULLSCREEN_DESC="Establezca si desea permitir que el video se reproduzca en modo de pantalla completa." +ACF_VIDEO_LOOP="Bucle" +ACF_VIDEO_LOOP_DESC="Establezca si el reproductor reproducirá repetidamente el vídeo." +ACF_VIDEO_MODESTBRANDING="Logotipo" +ACF_VIDEO_MODESTBRANDING_DESC="Habilite para evitar que el logotipo de YouTube se muestre en la barra de control. Tenga en cuenta que aún se mostrará una pequeña etiqueta de texto de YouTube en la esquina superior derecha de un video en pausa cuando el puntero del mouse del usuario se desplace sobre el reproductor." +ACF_VIDEO_REL="Vídeos Relacionados" +ACF_VIDEO_REL_DESC="Habilite para mostrar videos relacionados cuando finalice la reproducción del video inicial." +ACF_VIDEO_PRIVACY_MODE="Modo de privacidad mejorada" +ACF_VIDEO_PRIVACY_MODE_DESC="Cuando activa el modo de privacidad mejorada, YouTube no almacenará información sobre los visitantes en su sitio web a menos que reproduzcan el video." +ACF_VIDEO_YOUTUBE="YouTube" +ACF_VIDEO_DAILYMOTION="DailyMotion" +ACF_VIDEO_VIMEO="Vimeo" +ACF_VIDEO_FACEBOOK="Facebook" +ACF_VIDEO_SELF_HOSTED_VIDEO="Vídeo Auto Hospedado" +ACF_VIDEO_MUTE="Silencio " +ACF_VIDEO_MUTE_DESC="Si silenciar el video o no" +ACF_VIDEO_COVER_IMAGE_TYPE="Tipo de Imagen de Portada" +ACF_VIDEO_COVER_IMAGE_TYPE_DESC="Seleccione el tipo de imagen de portada." +ACF_VIDEO_COVER_IMAGE="Imagen de Portada" +ACF_VIDEO_SELECT_COVER_IMAGE="Seleccione la Imagen de Portada" +ACF_VIDEO_SELECT_COVER_IMAGE_DESC="Seleccione una imagen de portada personalizada." +ACF_VIDEO_NO_COVER_IMAGE="Sín Imagen de Portada" +ACF_VIDEO_AUTO_COVER="Automático (del proveedor de vídeo)" +ACF_VIDEO_CUSTOM="Personalizar" +ACF_VIDEO_TITLE="Mostrar Título" +ACF_VIDEO_TITLE_DESC="Elija si desea mostrar el título del vídeo, solo si el propietario lo permite." +ACF_VIDEO_BYLINE="Mostrar línea de autor" +ACF_VIDEO_BYLINE_DESC="Elija mostrar la línea de autor del vídeo, solo si el propietario lo permite.

La línea de autor es todo lo que se muestra justo debajo del título. En la mayoría de ocasiones es el nombre de usuario del creador." +ACF_VIDEO_PORTRAIT="Mostrar Retrato" +ACF_VIDEO_PORTRAIT_DESC="Elija mostrar el retrato del creador, sólo si el propietario lo permite." +ACF_VIDEO_VIMEO_COLOR="Color de IU" +ACF_VIDEO_VIMEO_COLOR_DESC="Elija un color para la interfaz de usuario del video

Este color se usará para el título, la firma y los controles del reproductor." +ACF_VIDEO_PIP="Imagen en imagen" +ACF_VIDEO_PIP_DESC="Habilite para mostrar el botón de imagen en imagen en la barra de control." +ACF_VIDEO_FACEBOOKVIDEO_INCLUDEPOST="Incluir Publicación" +ACF_VIDEO_FACEBOOKVIDEO_INCLUDEPOST_DESC="Habilite para incluir el texto de la publicación de Facebook asociada con el video, si corresponde. Solo disponible para sitios de escritorio." +ACF_VIDEO_FACEBOOKVIDEO_SHOWCAPTIONS="Mostrar Subtítulos" +ACF_VIDEO_FACEBOOKVIDEO_SHOWCAPTIONS_DESC="Habilite para mostrar los subtítulos (si están disponibles) de forma predeterminada. Los subtítulos solo están disponibles en el escritorio." +ACF_VIDEO_PREVIEW_VIDEO="Vista previa del vídeo" +ACF_VIDEO_ENABLE_PREVIEWER="Habilitar Vista Previa" +ACF_VIDEO_ENABLE_PREVIEWER_DESC="Establezca si desea habilitar la vista previa al terminar de escribir una URL al editar un elemento (artículo, contacto, usuario, etc.)." +ACF_VIDEO_TYPE_A_PROVIDER_VIDEO_URL="Introduzca una URL de vídeo de %s ..." diff --git a/plugins/fields/acfvideo/language/es-ES/es-ES.plg_fields_acfvideo.sys.ini b/plugins/fields/acfvideo/language/es-ES/es-ES.plg_fields_acfvideo.sys.ini new file mode 100644 index 00000000..fd67b60e --- /dev/null +++ b/plugins/fields/acfvideo/language/es-ES/es-ES.plg_fields_acfvideo.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_VIDEO="Campos - ACF Vídeo" +ACF_VIDEO_DESC="Ingrese un vídeo de YouTube, Vimeo, Dailymotion, Facebook o incluso vídeos auto hospedados." diff --git a/plugins/fields/acfvideo/params/acfvideo.xml b/plugins/fields/acfvideo/params/acfvideo.xml new file mode 100644 index 00000000..8c27d7af --- /dev/null +++ b/plugins/fields/acfvideo/params/acfvideo.xml @@ -0,0 +1,279 @@ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/plugins/fields/acfvideo/script.install.helper.php b/plugins/fields/acfvideo/script.install.helper.php new file mode 100644 index 00000000..e72382de --- /dev/null +++ b/plugins/fields/acfvideo/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfvideoInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfvideo/script.install.php b/plugins/fields/acfvideo/script.install.php new file mode 100644 index 00000000..178f513f --- /dev/null +++ b/plugins/fields/acfvideo/script.install.php @@ -0,0 +1,43 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\Filesystem\File; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFVideoInstallerScript extends PlgFieldsACFVideoInstallerScriptHelper +{ + public $alias = 'acfvideo'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + // If version.php doesn't exist, copy it from the system plugin + if ($this->isInstalled() && !file_exists($this->getMainFolder() . '/version.php')) + { + $systemVersionPath = JPATH_SITE . '/plugins/system/acf/version.php'; + + $result = File::copy($systemVersionPath, $this->getMainFolder() . '/version.php'); + } + + return parent::onBeforeInstall(); + } +} diff --git a/plugins/fields/acfvideo/tmpl/acfvideo.php b/plugins/fields/acfvideo/tmpl/acfvideo.php new file mode 100644 index 00000000..ab945ae4 --- /dev/null +++ b/plugins/fields/acfvideo/tmpl/acfvideo.php @@ -0,0 +1,27 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +if (!$provider = $fieldParams->get('provider', 'YouTube')) +{ + return; +} + +$file = __DIR__ . '/providers/' . strtolower($provider) . '.php'; +if(!file_exists($file)) +{ + return; +} + +// Display selected widget +require $file; \ No newline at end of file diff --git a/plugins/fields/acfvideo/tmpl/providers/dailymotion.php b/plugins/fields/acfvideo/tmpl/providers/dailymotion.php new file mode 100644 index 00000000..2a22d426 --- /dev/null +++ b/plugins/fields/acfvideo/tmpl/providers/dailymotion.php @@ -0,0 +1,41 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$videoURL = $field->value) +{ + return; +} + +$payload = [ + 'value' => $videoURL, + 'width' => $fieldParams->get('width', '480px'), + 'height' => $fieldParams->get('height', '270px'), + + 'mute' => $fieldParams->get('mute', '0') === '1', + 'loop' => $fieldParams->get('loop', '0') === '1', + 'autopause' => $fieldParams->get('autopause', '0') === '1', + 'start' => $fieldParams->get('start', ''), + 'end' => $fieldParams->get('end', ''), + 'coverImageType' => $fieldParams->get('coverImageType', false), + 'coverImage' => $fieldParams->get('coverImage', ''), + +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('Dailymotion', $payload); \ No newline at end of file diff --git a/plugins/fields/acfvideo/tmpl/providers/facebookvideo.php b/plugins/fields/acfvideo/tmpl/providers/facebookvideo.php new file mode 100644 index 00000000..55b6aca5 --- /dev/null +++ b/plugins/fields/acfvideo/tmpl/providers/facebookvideo.php @@ -0,0 +1,39 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$videoURL = $field->value) +{ + return; +} + +$payload = [ + 'value' => $videoURL, + 'width' => $fieldParams->get('width', '480px'), + 'height' => null, + + 'fs' => $fieldParams->get('fs', '1') === '1', + 'autoplay' => $fieldParams->get('autoplay', '0') === '1', + 'autopause' => $fieldParams->get('autopause', '0') === '1', + 'show_text' => $fieldParams->get('show_text', '0') === '1', + 'show_captions' => $fieldParams->get('show_captions', '0') === '1', + +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('FacebookVideo', $payload); \ No newline at end of file diff --git a/plugins/fields/acfvideo/tmpl/providers/index.php b/plugins/fields/acfvideo/tmpl/providers/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/fields/acfvideo/tmpl/providers/selfhostedvideo.php b/plugins/fields/acfvideo/tmpl/providers/selfhostedvideo.php new file mode 100644 index 00000000..73e41002 --- /dev/null +++ b/plugins/fields/acfvideo/tmpl/providers/selfhostedvideo.php @@ -0,0 +1,40 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$videoURL = $field->value) +{ + return; +} + +$payload = [ + 'value' => $videoURL, + 'width' => $fieldParams->get('width', '480px'), + 'height' => $fieldParams->get('height', '270px'), + 'preload' => $fieldParams->get('preload', 'auto'), + + 'autopause' => $fieldParams->get('autopause', '0') === '1', + + 'autoplay' => $fieldParams->get('selfhostedvideo_autoplay', '0') === '1', + 'controls' => $fieldParams->get('selfhostedvideo_controls', '0') === '1', + 'loop' => $fieldParams->get('selfhostedvideo_loop', '0') === '1', + 'mute' => $fieldParams->get('selfhostedvideo_mute', '0') === '1' +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('SelfHostedVideo', $payload); \ No newline at end of file diff --git a/plugins/fields/acfvideo/tmpl/providers/vimeo.php b/plugins/fields/acfvideo/tmpl/providers/vimeo.php new file mode 100644 index 00000000..9756c61e --- /dev/null +++ b/plugins/fields/acfvideo/tmpl/providers/vimeo.php @@ -0,0 +1,51 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$videoURL = $field->value) +{ + return; +} + +$payload = [ + 'value' => $videoURL, + 'width' => $fieldParams->get('width', '480px'), + 'height' => $fieldParams->get('height', '270px'), + 'privacy' => $fieldParams->get('privacyMode', '0') === '1', + + 'controls' => $fieldParams->get('controls', '1') === '1', + 'loop' => $fieldParams->get('loop', '0') === '1', + 'mute' => $fieldParams->get('mute', '0') === '1', + 'autoplay' => $fieldParams->get('autoplay', '0') === '1', + 'autopause' => $fieldParams->get('autopause', '0') === '1', + 'title' => $fieldParams->get('title', '0') === '1', + 'byline' => $fieldParams->get('byline', '0') === '1', + 'portrait' => $fieldParams->get('portrait', '0') === '1', + 'pip' => $fieldParams->get('pip', '0') === '1', + 'speed' => $fieldParams->get('speed', '0') === '1', + 'color' => $fieldParams->get('vimeo_color'), + 'keyboard' => $fieldParams->get('disablekb', '0') === '0', + 'start' => $fieldParams->get('start', ''), + 'end' => $fieldParams->get('end', ''), + 'coverImageType' => $fieldParams->get('coverImageType', false), + 'coverImage' => $fieldParams->get('coverImage', ''), + +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('Vimeo', $payload); \ No newline at end of file diff --git a/plugins/fields/acfvideo/tmpl/providers/youtube.php b/plugins/fields/acfvideo/tmpl/providers/youtube.php new file mode 100644 index 00000000..1c041e53 --- /dev/null +++ b/plugins/fields/acfvideo/tmpl/providers/youtube.php @@ -0,0 +1,50 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!$videoURL = $field->value) +{ + return; +} + +$payload = [ + 'value' => $videoURL, + 'width' => $fieldParams->get('width', '480px'), + 'height' => $fieldParams->get('height', '270px'), + 'privacy' => $fieldParams->get('privacyMode', '0') === '1', + + 'autoplay' => $fieldParams->get('autoplay', '0') === '1', + 'autopause' => $fieldParams->get('autopause', '0') === '1', + 'fs' => $fieldParams->get('fs', '1') === '1', + 'controls' => $fieldParams->get('controls', '1') === '1', + 'loop' => $fieldParams->get('loop', '0') === '1', + 'mute' => $fieldParams->get('mute', '0') === '1', + 'cc_load_policy' => $fieldParams->get('cc_load_policy', '0') === '1', + 'disablekb' => $fieldParams->get('disablekb', '0') === '1', + 'start' => $fieldParams->get('start', ''), + 'end' => $fieldParams->get('end', ''), + 'modestbranding' => $fieldParams->get('modestbranding', '0') === '1', + 'rel' => $fieldParams->get('rel', '') === '1' ? '1' : '0', + 'color' => $fieldParams->get('youtube_color', 'red'), + 'coverImageType' => $fieldParams->get('coverImageType', false), + 'coverImage' => $fieldParams->get('coverImage', ''), + +]; + +// Set custom layout +if ($field->params->get('acf_layout_override')) +{ + $payload['layout'] = $field->params->get('acf_layout_override'); +} + +echo \NRFramework\Widgets\Helper::render('YouTube', $payload); \ No newline at end of file diff --git a/plugins/fields/acfvideo/version.php b/plugins/fields/acfvideo/version.php new file mode 100644 index 00000000..452fc947 --- /dev/null +++ b/plugins/fields/acfvideo/version.php @@ -0,0 +1,14 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; \ No newline at end of file diff --git a/plugins/fields/acfwhatsappctc/acfwhatsappctc.php b/plugins/fields/acfwhatsappctc/acfwhatsappctc.php new file mode 100644 index 00000000..7974877d --- /dev/null +++ b/plugins/fields/acfwhatsappctc/acfwhatsappctc.php @@ -0,0 +1,40 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2020 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +JLoader::register('ACF_Field', JPATH_PLUGINS . '/system/acf/helper/plugin.php'); + +if (!class_exists('ACF_Field')) +{ + Factory::getApplication()->enqueueMessage('Advanced Custom Fields System Plugin is missing', 'error'); + return; +} + +class PlgFieldsACFWhatsAppCTC extends ACF_Field +{ + /** + * Field's Hint Description + * + * @var string + */ + protected $hint = 'ACF_WHATSAPPCTC_HINT'; + + /** + * Field's Class + * + * @var string + */ + protected $class = 'input-xlarge w-100'; +} diff --git a/plugins/fields/acfwhatsappctc/acfwhatsappctc.xml b/plugins/fields/acfwhatsappctc/acfwhatsappctc.xml new file mode 100644 index 00000000..9f6fc36c --- /dev/null +++ b/plugins/fields/acfwhatsappctc/acfwhatsappctc.xml @@ -0,0 +1,21 @@ + + + ACF_WHATSAPPCTC + ACF_WHATSAPPCTC_DESC + Tassos Marinos + July 2019 + Copyright (C) 2019 Tassos Marinos. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + info@tassos.gr + www.tassos.gr + 1.0 + script.install.php + + acfwhatsappctc.php + script.install.helper.php + version.php + params + tmpl + language + + diff --git a/plugins/fields/acfwhatsappctc/language/en-GB/en-GB.plg_fields_acfwhatsappctc.ini b/plugins/fields/acfwhatsappctc/language/en-GB/en-GB.plg_fields_acfwhatsappctc.ini new file mode 100644 index 00000000..0a30601d --- /dev/null +++ b/plugins/fields/acfwhatsappctc/language/en-GB/en-GB.plg_fields_acfwhatsappctc.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFWHATSAPPCTC_LABEL="ACF - WhatsApp Click to Chat" +ACF_WHATSAPPCTC="Fields - ACF WhatsApp Click to Chat" +ACF_WHATSAPPCTC_DESC="Enter a telephone number to display a link and start chatting on WhatsApp!" +ACF_WHATSAPPCTC_VALUE_DESC="Enter a full phone number in international format, omitting any zeroes, brackets or dashes!

Correct: 15551234567
Wrong: +001-(555)1234567" +ACF_WHATSAPPCTC_PREFILLED_MSG="Pre-filled Message" +ACF_WHATSAPPCTC_PREFILLED_MSG_DESC="Enter a pre-filled message that will automatically appear in the text field of the chat." +ACF_WHATSAPPCTC_LABEL="Link Label" +ACF_WHATSAPPCTC_LABEL_DESC="Set the label of the link." +ACF_WHATSAPPCTC_REQUIRE_PHONE_NUMBER="Require Phone Number" +ACF_WHATSAPPCTC_REQUIRE_PHONE_NUMBER_DESC="Set whether to require a phone number or show a list of contacts you can send your message to." +ACF_WHATSAPPCTC_HINT="Enter a telephone number" \ No newline at end of file diff --git a/plugins/fields/acfwhatsappctc/language/en-GB/en-GB.plg_fields_acfwhatsappctc.sys.ini b/plugins/fields/acfwhatsappctc/language/en-GB/en-GB.plg_fields_acfwhatsappctc.sys.ini new file mode 100644 index 00000000..7c3e47b6 --- /dev/null +++ b/plugins/fields/acfwhatsappctc/language/en-GB/en-GB.plg_fields_acfwhatsappctc.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_WHATSAPPCTC="Fields - ACF WhatsApp Click to Chat" +ACF_WHATSAPPCTC_DESC="Enter a telephone number to display a link and start chatting on WhatsApp!" \ No newline at end of file diff --git a/plugins/fields/acfwhatsappctc/language/es-ES/es-ES.plg_fields_acfwhatsappctc.ini b/plugins/fields/acfwhatsappctc/language/es-ES/es-ES.plg_fields_acfwhatsappctc.ini new file mode 100644 index 00000000..23e85e76 --- /dev/null +++ b/plugins/fields/acfwhatsappctc/language/es-ES/es-ES.plg_fields_acfwhatsappctc.ini @@ -0,0 +1,18 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_FIELDS_ACFWHATSAPPCTC_LABEL="ACF - WhatsApp Haga clic para chatear" +ACF_WHATSAPPCTC="Campos - ACF WhatsApp Haga clic para chatear" +ACF_WHATSAPPCTC_DESC="¡Ingrese un número de teléfono para mostrar un enlace y comience a chatear en WhatsApp!" +ACF_WHATSAPPCTC_VALUE_DESC="Ingrese un número de teléfono completo en formato internacional, omitiendo ceros, corchetes o guiones

Correcto: 15551234567
Incorrecto: +001-(555)1234567" +ACF_WHATSAPPCTC_PREFILLED_MSG="Mensaje Precargado" +ACF_WHATSAPPCTC_PREFILLED_MSG_DESC="Ingrese un mensaje precargado que aparecerá automáticamente en el campo de texto del chat." +ACF_WHATSAPPCTC_LABEL="Etiqueta de Enlace" +ACF_WHATSAPPCTC_LABEL_DESC="Establece la etiqueta del enlace." +ACF_WHATSAPPCTC_REQUIRE_PHONE_NUMBER="Requerir Número de Teléfono" +ACF_WHATSAPPCTC_REQUIRE_PHONE_NUMBER_DESC="Establezca si desea solicitar un número de teléfono o mostrar una lista de contactos a los que puede enviar su mensaje." +ACF_WHATSAPPCTC_HINT="Introduzca un número de teléfono" diff --git a/plugins/fields/acfwhatsappctc/language/es-ES/es-ES.plg_fields_acfwhatsappctc.sys.ini b/plugins/fields/acfwhatsappctc/language/es-ES/es-ES.plg_fields_acfwhatsappctc.sys.ini new file mode 100644 index 00000000..569ff024 --- /dev/null +++ b/plugins/fields/acfwhatsappctc/language/es-ES/es-ES.plg_fields_acfwhatsappctc.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF_WHATSAPPCTC="Campos - ACF WhatsApp Haga clic para chatear" +ACF_WHATSAPPCTC_DESC="¡Ingrese un número de teléfono para mostrar un enlace y comience a chatear en WhatsApp!" diff --git a/plugins/fields/acfwhatsappctc/params/acfwhatsappctc.xml b/plugins/fields/acfwhatsappctc/params/acfwhatsappctc.xml new file mode 100644 index 00000000..c3ce1c80 --- /dev/null +++ b/plugins/fields/acfwhatsappctc/params/acfwhatsappctc.xml @@ -0,0 +1,15 @@ + +
+ +
+ + +
+
+
diff --git a/plugins/fields/acfwhatsappctc/script.install.helper.php b/plugins/fields/acfwhatsappctc/script.install.helper.php new file mode 100644 index 00000000..e94751e1 --- /dev/null +++ b/plugins/fields/acfwhatsappctc/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgFieldsAcfwhatsappctcInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/fields/acfwhatsappctc/script.install.php b/plugins/fields/acfwhatsappctc/script.install.php new file mode 100644 index 00000000..d68109cb --- /dev/null +++ b/plugins/fields/acfwhatsappctc/script.install.php @@ -0,0 +1,24 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgFieldsACFWhatsAppCTCInstallerScript extends PlgFieldsACFWhatsAppCTCInstallerScriptHelper +{ + public $alias = 'acfwhatsappctc'; + public $extension_type = 'plugin'; + public $plugin_folder = 'fields'; + public $show_message = false; + +} diff --git a/plugins/fields/acfwhatsappctc/tmpl/acfwhatsappctc.php b/plugins/fields/acfwhatsappctc/tmpl/acfwhatsappctc.php new file mode 100644 index 00000000..a4fb9fb9 --- /dev/null +++ b/plugins/fields/acfwhatsappctc/tmpl/acfwhatsappctc.php @@ -0,0 +1,41 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +// Setup Variables +$id = 'acf_whatsappctc_' . $item->id . '_' . $field->id; +$tel_number = htmlspecialchars($field->value, ENT_COMPAT, 'UTF-8'); +$prefilled_msg = $fieldParams->get('prefilled_msg', ''); +$label = $fieldParams->get('label', ''); +$buffer = ''; + +// If the phone number we entered +// on the article editing page is empty, we do show nothing. +if (empty($tel_number)) +{ + return; +} + +// base url addons +$add_prefilled_msg = (!empty($prefilled_msg)) ? '?text='.urlencode($prefilled_msg) : ''; + +// base url +$base_url = 'https://wa.me/' . $tel_number . $add_prefilled_msg; + +// link text +$link_text = (!empty($label)) ? $label : $base_url; + +// final element +$buffer = '' . $link_text . ''; + +echo $buffer; \ No newline at end of file diff --git a/plugins/fields/acfwhatsappctc/version.php b/plugins/fields/acfwhatsappctc/version.php new file mode 100644 index 00000000..cfa0e6ec --- /dev/null +++ b/plugins/fields/acfwhatsappctc/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; + +?> \ No newline at end of file diff --git a/plugins/finder/highlightshighlights/highlightshighlights.php b/plugins/finder/highlightshighlights/highlightshighlights.php new file mode 100644 index 00000000..13d112d0 --- /dev/null +++ b/plugins/finder/highlightshighlights/highlightshighlights.php @@ -0,0 +1,208 @@ +reindex($row->id); + } + } + + /** + * Method to update the link information for items that have been changed + * from outside the edit screen. This is fired when the item is published, + * unpublished, archived, or unarchived from the list view. + * + * @param string $context The context for the highlight passed to the plugin. + * @param array $pks An array of primary key ids of the highlight that has changed state. + * @param integer $value The value of the state that the highlight has been changed to. + * + * @return void + * + * @since 1.0.0 + */ + public function onFinderChangeState($context, $pks, $value) + { + // We only want to handle highlight here. + if ($context === 'com_highlights.highlight') + { + $this->itemStateChange($pks, $value); + } + } + + /** + * Method to get a SQL query to load the published and access states for + * an highlight and category. + * + * @return QueryInterface A database object. + * + * @since 1.0.0 + */ + protected function getStateQuery() + { + $query = $this->db->getQuery(true); + + // Item ID + $query->select('a.id'); + $query->select('1 AS access'); + $query->from($this->table . ' AS a'); + + return $query; + } + + /** + * Method to index an item. The item must be a Result object. + * + * @param Result $item The item to index as a Result object. + * + * @return void + * + * @since 1.0.0 + * @throws Exception on database error. + */ + protected function index(Result $item) + { + $item->setLanguage(); + + // Check if the extension is enabled. + if (ComponentHelper::isEnabled($this->extension) === false) + { + return; + } + + $item->url = $this->getUrl($item->id, $this->extension, $this->layout); + $item->route = $item->url; + + + $item->context = 'com_highlights.highlight'; + + $this->indexer->index($item); + } + + /** + * Method to get the SQL query used to retrieve the list of highlight items. + * + * @param mixed $query A DatabaseQuery object or null. + * + * @return DatabaseQuery A database object. + * + * @since 1.0.0 + */ + protected function getListQuery($query = null) + { + $db = $this->db; + + // Check if we can use the supplied SQL query. + $query = $query instanceof DatabaseQuery ? $query : $db->getQuery(true) + ->select('a.id, a.titolo AS title, a.titolo AS summary, a.state AS state, 1 AS access'); + + + $query->from($this->table . ' as a'); + return $query; + } +} diff --git a/plugins/finder/highlightshighlights/highlightshighlights.xml b/plugins/finder/highlightshighlights/highlightshighlights.xml new file mode 100644 index 00000000..668f7df9 --- /dev/null +++ b/plugins/finder/highlightshighlights/highlightshighlights.xml @@ -0,0 +1,28 @@ + + + plg_finder_highlightshighlights + Eddy Prosperi + 2024-12-30 + 2024 Eddy Prosperi + GNU General Public License versione 2 o successiva; vedi LICENSE.txt + eddy.prosperi@protocollicreativi.it + http:// + CVS: 1.0.0 + PLG_FINDER_COM_HIGHLIGHTS_XML_DESCRIPTION + + highlightshighlights.php + index.html + + + + en-GB/plg_finder_highlightshighlights.ini + en-GB/plg_finder_highlightshighlights.sys.ini + it-IT/plg_finder_highlightshighlights.ini + it-IT/plg_finder_highlightshighlights.sys.ini + + + + + https://www.component-creator.com/index.php?task=builder.preupdatecheckhook&option=com_combuilder&component=NzY0NzgtMjEzOTAw + + diff --git a/plugins/finder/highlightshighlights/index.html b/plugins/finder/highlightshighlights/index.html new file mode 100644 index 00000000..2efb97f3 --- /dev/null +++ b/plugins/finder/highlightshighlights/index.html @@ -0,0 +1 @@ + diff --git a/plugins/system/acf/ACF/Helpers/Field.php b/plugins/system/acf/ACF/Helpers/Field.php new file mode 100644 index 00000000..f5f0fe52 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Field.php @@ -0,0 +1,63 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers; + +defined('_JEXEC') or die; + +class Field +{ + /** + * Returns the widget for which the ACF custom field is based upon. + * + * @param string $type + * @param object $field_options + * + * @return string + */ + public static function isWidgetBased($type = '', $field_options = []) + { + if (!$type) + { + return false; + } + + $widget_name = self::getWidgetName($type); + + $widget = \NRFramework\Widgets\Helper::find($widget_name); + + if ($widget === 'Map' && !is_null($field_options->fieldparams)) + { + return !empty($field_options->fieldparams['provider']) ? $field_options->fieldparams['provider'] : $widget; + } + + return $widget; + } + + /** + * Returns the widget name of an ACF custom field. + * + * @param string @type + * + * @return string + */ + public static function getWidgetName($type = '') + { + // Remove the "acf" prefix + $type = str_replace('acf', '', $type); + + // Map for any ACF fields that do not automatically translate to a Widget + $map = [ + 'address' => 'MapAddress' + ]; + + // Transform a field type to its corresponding widget from the map + return isset($map[$type]) ? $map[$type] : $type; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Address.php b/plugins/system/acf/ACF/Helpers/Fields/Address.php new file mode 100644 index 00000000..edb071af --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Address.php @@ -0,0 +1,189 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Address +{ + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'latitude', + 'metadata' => [ + 'label' => 'Latitude' + ], + ], + [ + 'type' => 'String', + 'name' => 'longitude', + 'metadata' => [ + 'label' => 'Longitude' + ], + ], + [ + 'type' => 'String', + 'name' => 'value', + 'metadata' => [ + 'label' => 'Coordinates' + ], + ], + [ + 'type' => 'String', + 'name' => 'address', + 'metadata' => [ + 'label' => 'Address' + ], + ], + [ + 'type' => 'String', + 'name' => 'postal_code', + 'metadata' => [ + 'label' => 'Postal Code' + ], + ], + [ + 'type' => 'String', + 'name' => 'country_code', + 'metadata' => [ + 'label' => 'Country Code' + ], + ], + [ + 'type' => 'String', + 'name' => 'country', + 'metadata' => [ + 'label' => 'Country' + ], + ], + [ + 'type' => 'String', + 'name' => 'state', + 'metadata' => [ + 'label' => 'State' + ], + ], + [ + 'type' => 'String', + 'name' => 'municipality', + 'metadata' => [ + 'label' => 'Municipality' + ], + ], + [ + 'type' => 'String', + 'name' => 'town', + 'metadata' => [ + 'label' => 'Town' + ], + ], + [ + 'type' => 'String', + 'name' => 'city', + 'metadata' => [ + 'label' => 'City' + ], + ], + [ + 'type' => 'String', + 'name' => 'road', + 'metadata' => [ + 'label' => 'Road' + ], + ] + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return $name; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? json_encode($item["field{$args['field_id']}"]) : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (is_string($value)) + { + if (!$value = json_decode($value, true)) + { + return; + } + } + + if (!is_array($value)) + { + return; + } + + $lat = isset($value['address']['latitude']) ? $value['address']['latitude'] : null; + $lng = isset($value['address']['longitude']) ? $value['address']['longitude'] : null; + + return [ + 'latitude' => $lat, + 'longitude' => $lng, + 'value' => $lat && $lng ? implode(',', [$lat, $lng]) : null, + 'address' => isset($value['address']['address']) ? $value['address']['address'] : null, + 'postal_code' => isset($value['address']['postal_code']) ? $value['address']['postal_code'] : null, + 'country_code' => isset($value['address']['country_code']) ? $value['address']['country_code'] : null, + 'country' => isset($value['address']['country']) ? $value['address']['country'] : null, + 'state' => isset($value['address']['state']) ? $value['address']['state'] : null, + 'municipality' => isset($value['address']['municipality']) ? $value['address']['municipality'] : null, + 'town' => isset($value['address']['town']) ? $value['address']['town'] : null, + 'city' => isset($value['address']['city']) ? $value['address']['city'] : null, + 'road' => isset($value['address']['road']) ? $value['address']['road'] : null + ]; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Articles.php b/plugins/system/acf/ACF/Helpers/Fields/Articles.php new file mode 100644 index 00000000..1debfebe --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Articles.php @@ -0,0 +1,172 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Builder\Joomla\Source\ArticleHelper; +use Joomla\CMS\Factory; + +class Articles +{ + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + if ($field->only_use_in_subform === 1) + { + return; + } + + $max_articles = (int) $field->fieldparams->get('max_articles', 0); + $multiple = $max_articles === 0 || $max_articles > 1; + return $multiple ? ['listOf' => 'Article'] : 'Article'; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $ids = $field->rawvalue; + + $max_articles = 0; + $multiple = true; + + // Handle Linked Articles + $field_type = $field->fieldparams->get('articles_type', 'default'); + if ($field_type === 'linked') + { + + if (in_array($ids, ['1', 'true'])) + { + $ids = self::getLinkedArticlesIDs($item->id, $field); + } + + } + else + { + $max_articles = (int) $field->fieldparams->get('max_articles', 0); + $multiple = $max_articles === 0 || $max_articles > 1; + } + + if (is_scalar($ids)) + { + $ids = [(int) $ids]; + } + + if (!is_array($ids)) + { + return; + } + + $value = ArticleHelper::get($ids); + + if ($field->only_use_in_subform === 1) + { + $data = array_values(array_map(function($article) { + return $article->title; + }, $value)); + + return implode(', ', $data); + } + + if ($multiple) + { + return array_values(array_map(function ($value) { + return is_scalar($value) ? compact('value') : $value; + }, $value)); + } + else + { + return array_shift($value); + } + + } + + + public static function getLinkedArticlesIDs($item_id = null, $field = []) + { + if (!$acf_articles = array_filter($field->fieldparams->get('articles_fields', []))) + { + return; + } + + // Grab all linked articles + $db = Factory::getDbo(); + $query = $db->getQuery(true); + + $query->select('a.id') + ->from($db->quoteName('#__fields_values', 'fv')) + ->join('LEFT', $db->quoteName('#__content', 'a') . ' ON a.id = fv.item_id') + ->where($db->quoteName('fv.field_id') . ' IN (' . implode(',', $acf_articles) . ')') + ->where($db->quoteName('fv.value') . ' = ' . $db->q($item_id)); + + // Filter results + require_once JPATH_PLUGINS . '/fields/acfarticles/fields/acfarticlesfilters.php'; + $payload = $field->fieldparams; + $filters = new \ACFArticlesFilters($query, $payload); + $query = $filters->apply(); + + // Set query + $db->setQuery($query); + + // Get articles + if (!$articles = $db->loadAssocList()) + { + return; + } + + // Return article IDs + $articles = array_map(function($article) { + return $article['id']; + }, $articles); + + return $articles; + } + +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Chainedfields.php b/plugins/system/acf/ACF/Helpers/Fields/Chainedfields.php new file mode 100644 index 00000000..28a9a0d8 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Chainedfields.php @@ -0,0 +1,93 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Chainedfields +{ + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'value', + 'metadata' => [ + 'label' => 'Choices' + ], + ] + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return $name; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : null; + + // Use the subform field + $field = $subfield; + } + + if (!$value = $field->rawvalue) + { + return; + } + + if (!is_array($value)) + { + return; + } + + return [ + 'value' => implode(', ', $value) + ]; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Country.php b/plugins/system/acf/ACF/Helpers/Fields/Country.php new file mode 100644 index 00000000..9d5a8652 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Country.php @@ -0,0 +1,139 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Country +{ + /** + * Returns the YooTheme type. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $multiple_selection = $field->fieldparams->get('multiple_selection', '0') === '1'; + if (!$multiple_selection) + { + return null; + } + + $fields = [ + [ + 'type' => 'String', + 'name' => 'code', + 'metadata' => [ + 'label' => 'Country Code' + ], + ], + [ + 'type' => 'String', + 'name' => 'name', + 'metadata' => [ + 'label' => 'Country Name' + ], + ] + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return $name; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + $multiple_selection = $field->fieldparams->get('multiple_selection', '0') === '1'; + + if ($multiple_selection) + { + if (!is_array($value)) + { + $value = [$value]; + } + + return [ + 'code' => implode(',', $value), + 'name' => implode(',', self::getNames($value)) + ]; + } + else + { + // If it's an array, try to grab the first item. + if (is_array($value)) + { + $value = array_values($value); + $value = isset($value[0]) ? $value[0] : $value; + } + + if (!is_string($value)) + { + return; + } + + $display_mode = $field->fieldparams->get('countrydisplay', 'name'); + + if ($display_mode === 'code') + { + return $value; + } + + if (!$country = \NRFramework\Countries::getCountry($value)) + { + return $value; + } + + return $country['name']; + } + } + + public static function getNames($items = []) + { + return array_map(function ($value) { + $country = \NRFramework\Countries::getCountry($value); + return isset($country['name']) ? $country['name'] : $value['code']; + }, $items); + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Downloadbutton.php b/plugins/system/acf/ACF/Helpers/Fields/Downloadbutton.php new file mode 100644 index 00000000..435089b0 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Downloadbutton.php @@ -0,0 +1,58 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; + +class Downloadbutton +{ + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $filename = $field->rawvalue; + + if (!is_string($filename)) + { + return; + } + + $directory = $field->fieldparams->get('directory'); + + return implode(DIRECTORY_SEPARATOR, [$directory, $filename]); + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Faq.php b/plugins/system/acf/ACF/Helpers/Fields/Faq.php new file mode 100644 index 00000000..4cdb4d32 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Faq.php @@ -0,0 +1,107 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Faq +{ + /** + * Returns the YooTheme type. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'question', + 'metadata' => [ + 'label' => 'Question' + ], + ], + [ + 'type' => 'String', + 'name' => 'answer', + 'metadata' => [ + 'label' => 'Answer' + ], + ], + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return ['listOf' => $name]; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (is_string($value)) + { + if (!$value = json_decode($value, true)) + { + return; + } + } + + if (!is_array($value)) + { + return; + } + + if (!isset($value['value']) || !is_array($value['value'])) + { + return; + } + + $value = array_values($value['value']); + $value = array_filter($value, function($item) { + return !empty($item['question']) && !empty($item['answer']); + }); + + return $value; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Gallery.php b/plugins/system/acf/ACF/Helpers/Fields/Gallery.php new file mode 100644 index 00000000..58a6ae65 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Gallery.php @@ -0,0 +1,388 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use Joomla\CMS\Helper\TagsHelper; +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; + +class Gallery +{ + public static function deleteFilesFromItem($item_id = '') + { + if (!$item_id) + { + return; + } + + self::deleteGalleryFiles($item_id); + + if (version_compare(JVERSION, '4', '>=')) + { + self::deleteGalleryFilesInSubform($item_id); + } + } + + protected static function deleteGalleryFiles($item_id = '') + { + if (!$item_id) + { + return; + } + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('fv.value') + ->from('#__fields_values AS fv') + ->join('LEFT', '#__fields AS f ON fv.field_id = f.id') + ->where('fv.item_id = ' . $db->quote($item_id)) + ->where('f.type = ' . $db->quote('acfgallery')); + + $db->setQuery($query); + + $fieldsValues = $db->loadAssocList(); + + if (!$fieldsValues) + { + return; + } + + foreach ($fieldsValues as $value) + { + $files = json_decode($value['value'], true); + + if (!$files) + { + return; + } + + if (!isset($files['items']) || !is_array($files['items']) || !count($files['items'])) + { + return; + } + + foreach ($files['items'] as $file) + { + self::deleteGalleryItem('source', $file); + self::deleteGalleryItem('image', $file); + self::deleteGalleryItem('thumbnail', $file); + self::deleteGalleryItem('slideshow', $file); + } + } + } + + protected static function deleteGalleryFilesInSubform($item_id = '') + { + if (!$item_id) + { + return; + } + + $db = Factory::getDbo(); + + // Get all ACF File Upload custom field IDs + $query = $db->getQuery(true) + ->select('distinct f.id') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('fv.item_id = ' . (int) $item_id) + ->where('f.type = ' . $db->quote('acfgallery')); + $db->setQuery($query); + $fileupload_ids = array_keys($db->loadAssocList('id')); + + if (!$fileupload_ids) + { + return; + } + + // Get all Subform custom fields + $query->clear() + ->select('f.id as field_id, fv.item_id as item_id, fv.value as value') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('fv.item_id = ' . (int) $item_id) + ->where('f.type = ' . $db->quote('subform')); + $db->setQuery($query); + $subform_fields = $db->loadAssocList(); + + foreach ($subform_fields as $subform_field) + { + if (!$subform_field_items = json_decode($subform_field['value'], true)) + { + continue; + } + + foreach ($subform_field_items as $row => &$row_items) + { + if (!is_array($row_items)) + { + continue; + } + + foreach ($row_items as $field_name => &$field_value) + { + // Get the field id + $field_id = str_replace('field', '', $field_name); + + // Check if its a gallery field + if (!in_array($field_id, $fileupload_ids)) + { + continue; + } + + if (!is_array($field_value)) + { + continue; + } + + if (!isset($field_value['items']) || !is_array($field_value['items']) || !count($field_value['items'])) + { + return; + } + + foreach ($field_value['items'] as $file) + { + self::deleteGalleryItem('source', $file); + self::deleteGalleryItem('image', $file); + self::deleteGalleryItem('thumbnail', $file); + self::deleteGalleryItem('slideshow', $file); + } + } + } + } + } + + private static function deleteGalleryItem($key, $item) + { + if (isset($item[$key]) && !empty($item[$key])) + { + // Original file path + $path = implode(DIRECTORY_SEPARATOR, [JPATH_SITE, $item[$key]]); + + if (!file_exists($path)) + { + return; + } + + unlink($path); + } + } + + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'thumb', + 'metadata' => [ + 'label' => 'Thumbnail Image URL' + ], + ], + [ + 'type' => 'String', + 'name' => 'value', + 'metadata' => [ + 'label' => 'Full Image URL' + ], + ], + [ + 'type' => 'String', + 'name' => 'source', + 'metadata' => [ + 'label' => 'Source Image URL' + ], + ], + [ + 'type' => 'String', + 'name' => 'caption', + 'metadata' => [ + 'label' => 'Caption' + ], + ], + [ + 'type' => 'String', + 'name' => 'alt', + 'metadata' => [ + 'label' => 'Alt' + ], + ], + [ + 'type' => 'String', + 'name' => 'tags', + 'metadata' => [ + 'label' => 'Tags' + ], + ] + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + $limit_files = (int) $field->fieldparams->get('limit_files', 0); + if ($limit_files === 1) + { + return $name; + } + + return ['listOf' => $name]; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? json_encode($item["field{$args['field_id']}"]) : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (is_string($value)) + { + if (!$value = json_decode($value, true)) + { + return; + } + } + + if (!is_array($value)) + { + return; + } + + if (!isset($value['items'])) + { + return; + } + + $tagsHelper = new TagsHelper(); + + // Apply ordering + $ordering = $field->fieldparams->get('ordering', 'default'); + self::randomize($value['items'], $ordering); + + $limit_files = (int) $field->fieldparams->get('limit_files', 0); + if ($limit_files === 1) + { + $uploaded_data = array_values($value['items']); + + $tags = isset($uploaded_data[0]['tags']) && is_array($uploaded_data[0]['tags']) ? $tagsHelper->getTagNames($uploaded_data[0]['tags']) : []; + + return [ + 'caption' => isset($uploaded_data[0]['caption']) ? $uploaded_data[0]['caption'] : '', + 'source' => isset($uploaded_data[0]['source']) ? $uploaded_data[0]['source'] : '', + 'thumb' => isset($uploaded_data[0]['thumbnail']) ? $uploaded_data[0]['thumbnail'] : '', + 'alt' => isset($uploaded_data[0]['alt']) ? $uploaded_data[0]['alt'] : '', + 'value' => isset($uploaded_data[0]['image']) ? $uploaded_data[0]['image'] : '', + 'tags' => implode(', ', $tags) + ]; + } + + $data = array_values(array_map(function($item) use ($tagsHelper) { + $tags = isset($item['tags']) && is_array($item['tags']) ? $tagsHelper->getTagNames($item['tags']) : []; + + return [ + 'value' => $item['image'], + 'thumb' => $item['thumbnail'], + 'source' => isset($item['source']) ? $item['source'] : '', + 'alt' => isset($item['alt']) ? $item['alt'] : '', + 'caption' => $item['caption'], + 'tags' => implode(', ', $tags) + ]; + }, $value['items'])); + + return $data; + } + + public static function randomize(&$data = [], $ordering = 'default') + { + if (!is_array($data)) + { + return; + } + + switch ($ordering) + { + case 'random': + shuffle($data); + break; + case 'alphabetical': + usort($data, [__CLASS__, 'compareByThumbnailASC']); + break; + case 'reverse_alphabetical': + usort($data, [__CLASS__, 'compareByThumbnailDESC']); + break; + } + } + + /** + * Compares thumbnail file names in ASC order + * + * @param array $a + * @param array $b + * + * @return bool + */ + public static function compareByThumbnailASC($a, $b) + { + return strcmp(basename($a['thumbnail']), basename($b['thumbnail'])); + } + + /** + * Compares thumbnail file names in DESC order + * + * @param array $a + * @param array $b + * + * @return bool + */ + public static function compareByThumbnailDESC($a, $b) + { + return strcmp(basename($b['thumbnail']), basename($a['thumbnail'])); + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Map.php b/plugins/system/acf/ACF/Helpers/Fields/Map.php new file mode 100644 index 00000000..309267e2 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Map.php @@ -0,0 +1,136 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Map +{ + /** + * Returns the YooTheme type. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'address', + 'metadata' => [ + 'label' => Text::_('NR_MARKER_ADDRESS') + ], + ], + [ + 'type' => 'String', + 'name' => 'coordinates', + 'metadata' => [ + 'label' => Text::_('NR_MARKER_COORDINATES') + ], + ], + [ + 'type' => 'String', + 'name' => 'latitude', + 'metadata' => [ + 'label' => Text::_('NR_MARKER_LATITUDE') + ], + ], + [ + 'type' => 'String', + 'name' => 'longitude', + 'metadata' => [ + 'label' => Text::_('NR_MARKER_LONGITUDE') + ], + ], + [ + 'type' => 'String', + 'name' => 'label', + 'metadata' => [ + 'label' => Text::_('NR_MARKER_LABEL') + ], + ], + [ + 'type' => 'String', + 'name' => 'description', + 'metadata' => [ + 'label' => Text::_('NR_MARKER_DESCRIPTION') + ], + ], + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return ['listOf' => $name]; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (is_string($value)) + { + if (!$value = json_decode($value, true)) + { + return; + } + } + + if (!is_array($value)) + { + return; + } + + foreach ($value as $key => &$v) + { + if (!isset($v['latitude']) || !isset($v['longitude'])) + { + continue; + } + + $v['coordinates'] = $v['latitude'] . ',' . $v['longitude']; + } + + return $value; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Osm.php b/plugins/system/acf/ACF/Helpers/Fields/Osm.php new file mode 100644 index 00000000..43c9f160 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Osm.php @@ -0,0 +1,66 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; + +class Osm +{ + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? json_encode($item["field{$args['field_id']}"]) : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (!is_string($value)) + { + return; + } + + if (!$value = json_decode($value, true)) + { + return; + } + + if (!isset($value['coordinates'])) + { + return; + } + + return $value['coordinates']; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Php.php b/plugins/system/acf/ACF/Helpers/Fields/Php.php new file mode 100644 index 00000000..a074d327 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Php.php @@ -0,0 +1,65 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; + +class Php +{ + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + if (!$value = $field->rawvalue) + { + return; + } + + // Get plugin params + $plugin = PluginHelper::getPlugin('fields', 'acfphp'); + $params = new Registry($plugin->params); + + // Enable buffer output + $executer = new \NRFramework\Executer($value); + + return $executer + ->setForbiddenPHPFunctions($params->get('forbidden_php_functions')) + ->run(); + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Soundcloud.php b/plugins/system/acf/ACF/Helpers/Fields/Soundcloud.php new file mode 100644 index 00000000..44d0b06e --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Soundcloud.php @@ -0,0 +1,58 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; + +class Soundcloud +{ + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + $value = is_string($value) ? json_decode($value, true) : $value; + + if (!is_array($value)) + { + return; + } + + return isset($value['id']) ? $value['id'] : null; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Telephone.php b/plugins/system/acf/ACF/Helpers/Fields/Telephone.php new file mode 100644 index 00000000..d7a17a67 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Telephone.php @@ -0,0 +1,141 @@ + +* @link http://www.tassos.gr +* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved +* @license GNU GPLv3 or later +*/ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Telephone +{ + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'phone_number', + 'metadata' => [ + 'label' => 'Phone Number' + ], + ], + [ + 'type' => 'String', + 'name' => 'calling_code', + 'metadata' => [ + 'label' => 'Calling Code' + ], + ], + [ + 'type' => 'String', + 'name' => 'country_code', + 'metadata' => [ + 'label' => 'Country Code' + ], + ], + [ + 'type' => 'String', + 'name' => 'country_name', + 'metadata' => [ + 'label' => 'Country Name' + ], + ] + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return $name; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + // var_dump($info->rootValue['parent']->children[0]); + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (is_string($value) && json_decode($value, true)) + { + $value = json_decode($value, true); + } + + $countryCode = ''; + $callingCode = ''; + + if (is_array($value)) + { + $countryCode = isset($value['code']) ? $value['code'] : ''; + $phoneNumber = isset($value['value']) ? $value['value'] : ''; + + if ($phoneNumber) + { + $callingCode = \NRFramework\Countries::getCallingCodeByCountryCode($countryCode); + $callingCode = $callingCode !== '' ? '+' . $callingCode : ''; + + $value = $callingCode . $phoneNumber; + } + else + { + $value = ''; + } + } + + if (!$value) + { + return; + } + + return [ + 'phone_number' => $value, + 'calling_code' => $callingCode, + 'country_code' => $countryCode, + 'country_name' => ($countryCode ? \NRFramework\Countries::toCountryName($countryCode) : '') + ]; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Upload.php b/plugins/system/acf/ACF/Helpers/Fields/Upload.php new file mode 100644 index 00000000..3e5af84e --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Upload.php @@ -0,0 +1,300 @@ + +* @link http://www.tassos.gr +* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved +* @license GNU GPLv3 or later +*/ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; +use Joomla\CMS\Factory; + +class Upload +{ + + public static function deleteFilesFromItem($item_id = '') + { + if (!$item_id) + { + return; + } + + self::deleteFileUploadFiles($item_id); + + if (version_compare(JVERSION, '4', '>=')) + { + self::deleteFileUploadFilesInSubform($item_id); + } + } + + protected static function deleteFileUploadFiles($item_id = '') + { + if (!$item_id) + { + return; + } + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('fv.value') + ->from('#__fields_values AS fv') + ->join('LEFT', '#__fields AS f ON fv.field_id = f.id') + ->where('fv.item_id = ' . $db->quote($item_id)) + ->where('f.type = ' . $db->quote('acfupload')); + + $db->setQuery($query); + + $fieldsValues = $db->loadAssocList(); + + if (!$fieldsValues) + { + return; + } + + foreach ($fieldsValues as $value) + { + $files = json_decode($value['value'], true); + + if (!$files) + { + return; + } + + foreach ($files as $file) + { + $filePath = implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $file['value']]); + + if (!file_exists($filePath)) + { + continue; + } + + unlink($filePath); + } + } + } + + protected static function deleteFileUploadFilesInSubform($item_id = '') + { + if (!$item_id) + { + return; + } + + $db = Factory::getDbo(); + + // Get all ACF File Upload custom field IDs + $query = $db->getQuery(true) + ->select('distinct f.id') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('fv.item_id = ' . (int) $item_id) + ->where('f.type = ' . $db->quote('acfupload')); + $db->setQuery($query); + $fileupload_ids = array_keys($db->loadAssocList('id')); + + if (!$fileupload_ids) + { + return; + } + + // Get all Subform custom fields + $query->clear() + ->select('f.id as field_id, fv.item_id as item_id, fv.value as value') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('fv.item_id = ' . (int) $item_id) + ->where('f.type = ' . $db->quote('subform')); + $db->setQuery($query); + $subform_fields = $db->loadAssocList(); + + foreach ($subform_fields as $subform_field) + { + if (!$subform_field_items = json_decode($subform_field['value'], true)) + { + continue; + } + + foreach ($subform_field_items as $row => &$row_items) + { + if (!is_array($row_items)) + { + continue; + } + + foreach ($row_items as $field_name => &$field_value) + { + // Get the field id + $field_id = str_replace('field', '', $field_name); + + // Check if its a gallery field + if (!in_array($field_id, $fileupload_ids)) + { + continue; + } + + if (!is_array($field_value)) + { + continue; + } + + foreach ($field_value as $file) + { + $filePath = implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $file['value']]); + + if (!file_exists($filePath)) + { + continue; + } + + unlink($filePath); + } + } + } + } + } + + + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $limit_files = (int) $field->fieldparams->get('limit_files', 0); + + $fields = [ + [ + 'type' => 'String', + 'name' => 'value', + 'metadata' => [ + 'label' => 'File URL' + ], + ], + [ + 'type' => 'String', + 'name' => 'title', + 'metadata' => [ + 'label' => 'Title' + ], + ], + [ + 'type' => 'String', + 'name' => 'description', + 'metadata' => [ + 'label' => 'Description' + ], + ], + [ + 'type' => 'String', + 'name' => 'filesize', + 'metadata' => [ + 'label' => 'File size' + ], + ], + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + if ($limit_files === 1) + { + return $name; + } + + return ['listOf' => $name]; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? json_encode($item["field{$args['field_id']}"]) : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + $value = is_string($value) ? json_decode($value, true) ?? $value : $value; + + if (!is_array($value)) + { + return; + } + + $value = array_values($value); + + if (is_array($value)) + { + // Fix new lines in description + foreach ($value as $key => &$val) + { + if (isset($val['description'])) + { + $val['description'] = nl2br($val['description']); + } + } + } + + foreach ($value as $key => &$val) + { + $val['filesize'] = ''; + + $path = implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $val['value']]); + if (file_exists($path)) + { + $val['filesize'] = filesize($path); + $val['filesize'] = sprintf("%4.2f MB", $val['filesize'] / 1024 / 1024); + } + + if (isset($val['value'])) + { + $val['value'] = $val['value']; + } + } + + $limit_files = (int) $field->fieldparams->get('limit_files', 0); + if ($limit_files === 1 && is_array($value) && count($value) === 1) + { + return $value[0]; + } + + return $value; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/Url.php b/plugins/system/acf/ACF/Helpers/Fields/Url.php new file mode 100644 index 00000000..6ef99e2e --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Fields/Url.php @@ -0,0 +1,106 @@ + +* @link http://www.tassos.gr +* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved +* @license GNU GPLv3 or later +*/ + +namespace ACF\Helpers\Fields; + +defined('_JEXEC') or die; + +use YOOtheme\Builder\Joomla\Fields\Type\FieldsType; +use YOOtheme\Str; + +class Url +{ + /** + * Returns the YooTheme type. + * + * If this accepts one image: + * - Tells YooTheme to use the default type for the dropdown mapping option. + * + * If this accepts multiple images: + * - Tells YooTheme to only return the value of this field in the dropdown mapping option. + * + * @param object $field + * @param object $source + * + * @return array + */ + public static function getYooType($field = [], $source = []) + { + $fields = [ + [ + 'type' => 'String', + 'name' => 'url', + 'metadata' => [ + 'label' => 'URL' + ], + ], + [ + 'type' => 'String', + 'name' => 'text', + 'metadata' => [ + 'label' => 'Label' + ], + ], + [ + 'type' => 'String', + 'name' => 'target', + 'metadata' => [ + 'label' => 'Target' + ], + ] + ]; + $name = Str::camelCase(['Field', $field->name], true); + $source->objectType($name, compact('fields')); + + return $name; + } + + /** + * Transforms the field value to an appropriate value that YooTheme can understand. + * + * @return array + */ + public static function yooResolve($item, $args, $ctx, $info) + { + // var_dump($info->rootValue['parent']->children[0]); + $name = str_replace('String', '', strtr($info->fieldName, '_', '-')); + + // Check if it's a subform field + $subfield = clone \ACF\Helpers\Yoo::getSubfield($args['field_id'], $args['context']); + + // When we have a subform field, the $item is an array of values + if (!$subfield || !is_array($item)) + { + if (!isset($item->id) || !($field = FieldsType::getField($name, $item, $args['context']))) + { + return; + } + } + else + { + // Set rawvalue + $subfield->rawvalue = isset($item["field{$args['field_id']}"]) ? $item["field{$args['field_id']}"] : ''; + + // Use the subform field + $field = $subfield; + } + + $value = $field->rawvalue; + + if (is_string($value)) + { + if (!$value = json_decode($value, true)) + { + return; + } + } + + return $value; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Fields/index.php b/plugins/system/acf/ACF/Helpers/Fields/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/system/acf/ACF/Helpers/Previewer.php b/plugins/system/acf/ACF/Helpers/Previewer.php new file mode 100644 index 00000000..4f533597 --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Previewer.php @@ -0,0 +1,48 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers; + +defined('_JEXEC') or die; + +class Previewer +{ + /** + * Returns the field previewer data. + * + * @return array + */ + public static function getFieldPreviewData($field = '') + { + if (empty($field)) + { + return; + } + + // Get path to file + $file = implode(DIRECTORY_SEPARATOR, [self::getJsonDirectory(), $field . '.json']); + + if (!file_exists($file)) + { + return; + } + + return file_get_contents($file); + } + + /** + * Returns the path to the fields previewer JSON directory. + * + * @return string + */ + public static function getJsonDirectory() + { + return implode(DIRECTORY_SEPARATOR, [JPATH_SITE, 'media', 'plg_system_acf', 'data', 'previewer']); + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/Yoo.php b/plugins/system/acf/ACF/Helpers/Yoo.php new file mode 100644 index 00000000..1ea78c1f --- /dev/null +++ b/plugins/system/acf/ACF/Helpers/Yoo.php @@ -0,0 +1,87 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Helpers; + +defined('_JEXEC') or die; + +use NRFramework\Functions; +use YOOtheme\Builder\Joomla\Fields\FieldsHelper; +use YOOtheme\Application as YooApplication; +use YOOtheme\Event; + +class Yoo +{ + public static function initFieldParser() + { + // Ensure YOOtheme Pro is ready + if (!class_exists(YooApplication::class, false)) + { + return; + } + + Event::on('source.com_fields.field', function($config, $field, $source, $context) { + // If it's not an ACF Field, return current config + if (substr($field->type, 0, 3) !== 'acf') + { + return $config; + } + + // Get the helper class of the field + $helperClass = '\ACF\Helpers\Fields\\' . ucfirst(substr($field->type, 3)); + + // If it does not exist, return current config + if (!class_exists($helperClass)) + { + return $config; + } + + // If the method does not have a resolve method, return current config + if (!method_exists($helperClass, 'yooResolve')) + { + return $config; + } + + $payload = [ + 'extensions' => [ + 'call' => [ + 'func' => $helperClass . '::yooResolve', + 'args' => ['context' => $context, 'field_id' => $field->id] + ] + ], + ] + $config; + + // Get and set the type + $type = method_exists($helperClass, 'getYooType') ? $helperClass::getYooType($field, $source) : ''; + if (!empty($type)) + { + $payload['type'] = $type; + } + + return $payload; + }); + } + + public static function getSubfield($id, $context) + { + static $fields = []; + + if (!isset($fields[$context])) + { + $fields[$context] = []; + + foreach (FieldsHelper::getFields($context, null, true) as $field) + { + $fields[$context][$field->id] = $field; + } + } + + return !empty($fields[$context][$id]) ? $fields[$context][$id] : null; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Helpers/index.php b/plugins/system/acf/ACF/Helpers/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/system/acf/ACF/Item.php b/plugins/system/acf/ACF/Item.php new file mode 100644 index 00000000..486fbaf7 --- /dev/null +++ b/plugins/system/acf/ACF/Item.php @@ -0,0 +1,58 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +class Item +{ + /** + * Determines whether we're copying an item. + * + * @return bool + */ + public static function isCopying() + { + return self::isBatchCopying() || self::isSavingAsCopy(); + } + + /** + * Determines whether we're batch copying an item. + * + * @return bool + */ + public static function isBatchCopying() + { + $input = Factory::getApplication()->input; + $task = $input->getCmd('task', ''); + $batchOptions = $input->post->get('batch', [], 'array'); + $cid = $input->post->get('cid', [], 'array'); + $isCopy = isset($batchOptions['move_copy']) && $batchOptions['move_copy'] === 'c'; + + return $task === 'batch' && is_array($cid) && count($cid) && $isCopy; + } + + /** + * Determines whether we're copying an item using the "Save as Copy" button. + * + * @return bool + */ + public static function isSavingAsCopy() + { + $input = Factory::getApplication()->input; + $task = $input->getCmd('task', ''); + $id = $input->getInt('id', 0); + $isCopying = $task === 'save2copy' && $id; + + return $isCopying; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Previewer/Countdown.php b/plugins/system/acf/ACF/Previewer/Countdown.php new file mode 100644 index 00000000..1d101362 --- /dev/null +++ b/plugins/system/acf/ACF/Previewer/Countdown.php @@ -0,0 +1,148 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Previewer; + +defined('_JEXEC') or die; + +class Countdown extends Field +{ + /** + * The field framework widget name. + * + * @var string + */ + protected $field = 'Countdown'; + + /** + * The theme used. + * + * @var string + */ + private $theme; + + public function __construct($data = [], $payload = []) + { + parent::__construct($data, $payload); + + // Set theme + $this->theme = $this->getTheme(); + } + + /** + * Render the field. + * + * @return string + */ + public function onSetup() + { + $this->payload = [ + // Field values + 'countdown_type' => 'evergreen', + 'timezone' => 'server', + 'dynamic_days' => '1', + 'dynamic_hours' => '12', + 'dynamic_minutes' => '59', + 'dynamic_seconds' => '59', + + // Countdown End Action + 'finish_text' => $this->fieldParams->get('finish_text', ''), + 'redirect_url' => $this->fieldParams->get('redirect_url', ''), + 'countdown_action' => 'restart', + + // Preset + 'theme' => $this->theme, + 'format' => $this->fieldParams->get('format', ''), + + // Unit Display + 'days' => $this->fieldParams->get('days') === '1', + 'days_label' => $this->fieldParams->get('days_label'), + 'hours' => $this->fieldParams->get('hours') === '1', + 'hours_label' => $this->fieldParams->get('hours_label'), + 'minutes' => $this->fieldParams->get('minutes') === '1', + 'minutes_label' => $this->fieldParams->get('minutes_label'), + 'seconds' => $this->fieldParams->get('seconds') === '1', + 'seconds_label' => $this->fieldParams->get('seconds_label'), + 'separator' => $this->fieldParams->get('separator') === '1', + 'double_zeroes_format' => $this->fieldParams->get('double_zeroes_format') === '1', + + // Unit Item + 'item_size' => $this->fieldParams->get('item_size_responsive.item_size'), + 'item_padding' => $this->fieldParams->get('item_padding_control.item_padding'), + 'gap' => $this->fieldParams->get('item_gap.gap'), + 'item_border_style' => $this->fieldParams->get('border.style'), + 'item_border_width' => $this->fieldParams->get('border.width'), + 'item_border_color' => $this->fieldParams->get('border.color'), + 'item_background_color' => $this->fieldParams->get('item_background_color'), + 'item_border_radius' => $this->fieldParams->get('item_border_radius_control.item_border_radius'), + + // Unit Digits Container + 'digits_wrapper_min_width' => $this->fieldParams->get('digits_wrapper_custom_width') === '1' ? $this->fieldParams->get('digits_wrapper_min_width') : null, + 'digits_wrapper_padding' => $this->fieldParams->get('digits_wrapper_padding_control.digits_wrapper_padding'), + 'digits_wrapper_border_radius' => $this->fieldParams->get('digits_wrapper_border_radius_control.digits_wrapper_border_radius'), + 'digits_wrapper_background_color' => $this->fieldParams->get('digits_wrapper_background_color'), + + // Unit Digit + 'digits_font_size' => $this->fieldParams->get('digits_font_size_control.digits_font_size'), + 'digits_font_weight' => $this->fieldParams->get('digits_font_weight'), + 'digit_min_width' => $this->fieldParams->get('digits_custom_width') === '1' ? $this->fieldParams->get('digits_min_width') : null, + 'digits_padding' => $this->fieldParams->get('digits_padding_control.digits_padding'), + 'digit_border_radius' => $this->fieldParams->get('digits_border_radius_control.digits_border_radius'), + 'digits_gap' => $this->fieldParams->get('digits_gap_control.digits_gap'), + 'digit_background_color' => $this->fieldParams->get('digit_background_color'), + 'digit_text_color' => $this->fieldParams->get('digit_text_color'), + + // Unit Label + 'label_font_size' => $this->fieldParams->get('label_font_size_control.label_font_size'), + 'label_font_weight' => $this->fieldParams->get('label_font_weight'), + 'unit_label_margin_top' => $this->fieldParams->get('unit_label_margin_top'), + 'unit_label_text_color' => $this->fieldParams->get('unit_label_text_color'), + ]; + + $this->widget = new \NRFramework\Widgets\Countdown(json_decode(json_encode($this->payload), true)); + } + + /** + * Return all assets used by this field. + * + * @return void + */ + public function getAssets() + { + $exclude_breakpoints = []; + + // If we are not viewing the fullscreen previewer, + // then get the widget's Custom CSS of the desktop + // due to the IFrame having small width and triggering the tablet/mobile CSS breakpoints + if (!$this->data->get('fullscreen')) + { + $exclude_breakpoints = ['tablet', 'mobile']; + } + + return [ + 'css' => \NRFramework\Widgets\Countdown::getCSS($this->theme), + 'js' => \NRFramework\Widgets\Countdown::getJS(), + 'custom_css' => $this->widget->getWidgetCSS($exclude_breakpoints) + ]; + } + + /** + * Get the theme. + * + * @return string + */ + private function getTheme() + { + $preset_source = $this->fieldParams->get('preset_source', 'preset'); + $preset = $this->fieldParams->get('preset', '1'); + + // Determine theme + return $preset_source === 'custom' ? 'custom' : ($preset === '8' ? 'oneline' : 'default'); + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Previewer/FAQ.php b/plugins/system/acf/ACF/Previewer/FAQ.php new file mode 100644 index 00000000..c309aef8 --- /dev/null +++ b/plugins/system/acf/ACF/Previewer/FAQ.php @@ -0,0 +1,103 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Previewer; + +defined('_JEXEC') or die; + +class FAQ extends Field +{ + /** + * The field framework widget name. + * + * @var string + */ + protected $field = 'FAQ'; + + /** + * Render the field. + * + * @return string + */ + public function onSetup() + { + $value = [ + [ + 'question' => 'Who should use EngageBox?', + 'answer' => 'EngageBox is the most powerful popup engine in the Joomla! market used by marketing agencies, bloggers, eCommerce websites, and all small businesses. If you want to grow your email list, improve your website conversions, and reduce cart abandonment, then you need EngageBox.' + ], + [ + 'question' => 'What\'s required to use EngageBox?', + 'answer' => 'EngageBox can be used in both Joomla 3 and Joomla 4. In detail, you will need an up-to-date version of Joomla 3.8.0 or higher, PHP 7.0.0 or higher, and MySQL 5 or higher.' + ], + [ + 'question' => 'Do I need coding skills to use EngageBox?', + 'answer' => 'Absolutely not! You can create and customize beautiful popups without any coding knowledge. We made it extremely user-friendly, so you can build a high-converting popup without hiring a developer.' + ], + [ + 'question' => 'What type of conversions can I expect?', + 'answer' => 'It will make a significant difference. Game-changing! Our average user sees over 500% increase in sales, customers base and growth in general.' + ], + [ + 'question' => 'What if a visitor has a pop-up adblocker enabled?', + 'answer' => 'Will still work! EngageBox produces popups in a way which can\'t be blocked by the browser’s pop-up blocking feature or 3rd party extensions such as AdBlock or uBlock.' + ], + ]; + + $this->payload = [ + 'value' => $value, + 'css_class' => ' template_' . $this->fieldParams->get('template'), + 'columns' => (int) $this->fieldParams->get('columns', 1), + 'item_gap' => $this->fieldParams->get('item_gap_control.item_gap', 20), + 'column_gap' => $this->fieldParams->get('column_gap_control.column_gap', 20), + 'item_background_color' => $this->fieldParams->get('background_color'), + 'item_border_radius' => $this->fieldParams->get('border_radius_control.item_border_radius'), + 'item_padding' => $this->fieldParams->get('padding_control.item_padding'), + 'question_font_size' => $this->fieldParams->get('question_font_size_control.question_font_size'), + 'question_text_color' => $this->fieldParams->get('question_text_color'), + 'answer_font_size' => $this->fieldParams->get('answer_font_size_control.answer_font_size'), + 'answer_text_color' => $this->fieldParams->get('answer_text_color'), + 'generate_faq' => $this->fieldParams->get('generate_faq', '0') === '1', + 'keep_one_question_open' => $this->fieldParams->get('keep_one_question_open', '0') === '1', + 'separator' => $this->fieldParams->get('separator', '0') === '1', + 'separator_color' => $this->fieldParams->get('separator_color'), + 'initial_state' => $this->fieldParams->get('initial_state', 'first-open') + ]; + + $show_toggle_icon = $this->fieldParams->get('show_toggle_icon', '1') === '1'; + $this->payload['show_toggle_icon'] = $show_toggle_icon; + if ($show_toggle_icon) + { + $this->payload['icon'] = $this->fieldParams->get('icon', 'arrow'); + $this->payload['icon_position'] = $this->fieldParams->get('icon_position', 'right'); + } + + $this->widget = new \NRFramework\Widgets\FAQ(json_decode(json_encode($this->payload), true)); + } + + /** + * Adds some extra CSS to the previewer's body. + * + * @return string + */ + protected function getFieldPreviewerCSS() + { + if ((int) $this->fieldParams->get('template') !== 4) + { + return; + } + + return ' + body { + background: #EBEBEB !important; + padding: 20px !important; + } + '; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/Previewer/Field.php b/plugins/system/acf/ACF/Previewer/Field.php new file mode 100644 index 00000000..7fc21f44 --- /dev/null +++ b/plugins/system/acf/ACF/Previewer/Field.php @@ -0,0 +1,235 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace ACF\Previewer; + +defined('_JEXEC') or die; + +use Joomla\Registry\Registry; +use Joomla\CMS\Uri\Uri; + +class Field +{ + /** + * The field framework widget name. + * + * @var string + */ + protected $field = ''; + + /** + * Field Editor Form Data. + * + * @var array + */ + protected $data = []; + + /** + * Field Params. + * + * @var object + */ + protected $fieldParams = []; + + /** + * The field payload used to render the field. + * + * @var array + */ + protected $payload; + + /** + * The Framework Widget. + * + * @var Widget + */ + protected $widget = null; + + public function __construct($data = [], $payload = []) + { + $this->data = new Registry($data); + + $fieldParams = isset($data['fieldparams']) ? $data['fieldparams'] : []; + $this->fieldParams = new Registry($fieldParams); + + if ($payload) + { + $this->payload = $payload; + } + } + + /** + * Saves the field's JSON data for previewer to render it. + * + * @return void + */ + protected function saveJSON() + { + // Get path to file + $file = implode(DIRECTORY_SEPARATOR, [\ACF\Helpers\Previewer::getJsonDirectory(), $this->data->get('type') . '.json']); + + // Save JSON file + file_put_contents($file, $this->getHTML()); + } + + /** + * Setup the field. + * + * @return string + */ + public function setup() + { + if (method_exists($this, 'onSetup')) + { + $this->onSetup(); + } + + $this->saveJSON(); + } + + /** + * Render the field. + * + * @return string + */ + public function render() + { + return $this->widget->render(); + } + + /** + * Field HTML output. + * + * @return string + */ + private function getHTML() + { + return '' . $this->getHead() . '' . $this->render() . ''; + } + + /** + * Returns the head of the preview HTML. + * + * @return string + */ + private function getHead() + { + if (!$assets = $this->getAssets()) + { + return; + } + + // Add Reset CSS + $head = ''; + + // Add Custom CSS + if (isset($assets['custom_css']) && !empty($assets['custom_css'])) + { + $head .= ''; + } + + $base_url = Uri::root(); + + // Add CSS + if (isset($assets['css']) && is_array($assets['css']) && count($assets['css'])) + { + foreach ($assets['css'] as $css) + { + $css = str_replace('plg_system_nrframework/', 'plg_system_nrframework/css/', $css); + + $link = $base_url . 'media/' . $css; + + $head .= ''; + } + } + + // Add Core JS + $head .= ''; + + // Add JS + if (isset($assets['js']) && is_array($assets['js']) && count($assets['js'])) + { + foreach ($assets['js'] as $js) + { + $js = str_replace('plg_system_nrframework/', 'plg_system_nrframework/js/', $js); + + $link = $base_url . 'media/' . $js; + + $head .= ''; + } + } + + return $head; + } + + /** + * Returns the field reset CSS. + * + * @return string + */ + private function getResetCSS() + { + $reset_css = ' + body { + border: 0 !important; + background: #fff !important; + padding: 0 !important; + margin: 0 !important; + overflow-x: hidden !important; + font-family: Arial; + font-size: 16px; + } + '; + + return $reset_css . $this->getFieldPreviewerCSS(); + } + + /** + * Adds some extra CSS to the previewer's body. + * + * @return string + */ + protected function getFieldPreviewerCSS() + { + } + + /** + * The CSS/JS assets that are required for the field to run. + * + * @return void + */ + public function getAssets() + { + return [ + 'css' => $this->getFieldCSS(), + 'js' => $this->getFieldJS(), + 'custom_css' => $this->widget->getOption('css_vars') . $this->widget->getOption('custom_css') + ]; + } + + /** + * Returns the field CSS files. + * + * @return array + */ + private function getFieldCSS() + { + return $this->widget && method_exists($this->widget, 'getCSS') ? $this->widget->getCSS() : []; + } + + /** + * Returns the field JS files. + * + * @return array + */ + private function getFieldJS() + { + return $this->widget && method_exists($this->widget, 'getJS') ? $this->widget->getJS() : []; + } +} \ No newline at end of file diff --git a/plugins/system/acf/ACF/index.php b/plugins/system/acf/ACF/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/system/acf/acf.php b/plugins/system/acf/acf.php new file mode 100644 index 00000000..7e1ae957 --- /dev/null +++ b/plugins/system/acf/acf.php @@ -0,0 +1,472 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +// Initialize ACF Namespace +require_once __DIR__ . '/autoload.php'; + +use NRFramework\HTML; +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Language\Text; +use Joomla\Registry\Registry; +use Joomla\CMS\HTML\HTMLHelper; +use NRFramework\Helpers\Widgets\GalleryManager as GalleryManagerHelper; + +/** + * Advanced Custom Fields System Plugin + */ +class PlgSystemACF extends CMSPlugin +{ + /** + * Auto load plugin's language file + * + * @var boolean + */ + protected $autoloadLanguage = true; + + /** + * Application Object + * + * @var object + */ + protected $app; + + /** + * The loaded indicator of helper + * + * @var boolean + */ + private $init; + + /** + * The field preview data. + * + * This is an array that holds + * both the HTML of the field as well + * as the assets that it requires. + * + * @var array + */ + private $field_preview_data = []; + + public function onAfterInitialise() + { + // YooTheme Pro Integration + \ACF\Helpers\Yoo::initFieldParser(); + } + + + /** + * After a tag has been deleted, then delete it from + * all ACF - Gallery instances. + * + * @param string $context + * @param object $item + * + * @return void + */ + public function onContentAfterDelete($context, $item) + { + // Delete all Joomla article assets + if ($context === 'com_content.article') + { + \ACF\Helpers\Fields\Gallery::deleteFilesFromItem($item->id); + \ACF\Helpers\Fields\Upload::deleteFilesFromItem($item->id); + } + + // Delete all tag assets + if ($context === 'com_tags.tag') + { + GalleryManagerHelper::deleteTagFromFieldsValues($item->id); + } + } + + /** + * onCustomFieldsBeforePrepareField Event + */ + public function onCustomFieldsBeforePrepareField($context, $item, &$field) + { + // Validate supported component/views + if (!in_array($context, [ + 'com_content.article', + 'com_dpcalendar.event' + ])) + { + return; + } + + // Get Helper + if (!$this->getHelper()) + { + return; + } + + // Only if assignments option is enabled in the plugin settings + if (!$this->params->get('assignments', true)) + { + return; + } + + if (ACFHelper::checkConditions($field) === false) + { + // According to the components/com_fields/layouts/fields/render.php file, if the field's value is empty it won't show up in the front-end. + $field->value = ''; + + // Unset rawvalue too, as it may be used in template overrides. + $field->rawvalue = ''; + } + } + + + /** + * Append publishing assignments XML to the + * + * @param Form $form The form to be altered. + * @param mixed $data The associated data for the form. + * + * @return boolean + */ + public function onContentPrepareForm(Form $form, $data) + { + + // Run Display Conditions checks for the User Profile page. + // NOTE: The System Fields plugin must be placed before this plugin in order for the $form to include the custom fields information. + if ($this->params->get('assignments', true) && $form->getName() == 'com_users.profile' && $this->app->isClient('site') && $form instanceof Form) + { + JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); + + if ($fields = FieldsHelper::getFields('com_users.user', Factory::getUser(), true)) + { + foreach ($fields as $field) + { + if (ACFHelper::checkConditions($field) === false) + { + $form->removeField($field->name, 'com_fields'); + } + } + } + } + + + // Run only on backend + if (!$this->app->isClient('administrator') || !$form instanceof Form) + { + return; + } + + $context = $form->getName(); + + + $this->loadPreviewer($context); + + + if (!in_array($context, [ + 'com_fields.field.com_users.user', + 'com_fields.field.com_content.article', + 'com_fields.field.com_contact.contact', + 'com_fields.field.com_dpcalendar.event' + ])) + { + return; + } + + + // Load Publishing Rules tab if assignments option is enabled in the plugin settings + if ($this->params->get('assignments', true)) + { + $form->loadFile(__DIR__ . '/form/conditions.xml', false); + + if ($context == 'com_fields.field.com_users.user') + { + // If plg_system_fields is not positioned before plg_system_acf, display a warning. + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('name, ordering') + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('name') . ' = ' . $db->q('plg_system_acf')) + ->orWhere($db->quoteName('name') . ' = ' . $db->q('plg_system_fields')) + ->order($db->quoteName('ordering')); + + $db->setQuery($query); + + $plugins = $db->loadObjectList(); + + if (count($plugins) == 2 && $plugins[0]->name !== 'plg_system_fields') + { + $form->setField(new SimpleXMLElement(' +
+ +
+ ')); + } + } + } + + + // Load "ACF Options" tab if the option is enabled in the plugin settings + if ($this->params->get('acf_options', true)) + { + $form->loadFile(__DIR__ . '/form/options.xml', false); + } + + + // Always load our stylesheet even if it's a non-ACF field. The Publishing Rules tab shows up on all fields. + HTMLHelper::stylesheet('plg_system_acf/acf-backend.css', ['relative' => true, 'version' => 'auto']); + + if (defined('nrJ4')) + { + HTMLHelper::stylesheet('plg_system_acf/joomla4.css', ['relative' => true, 'version' => 'auto']); + HTMLHelper::stylesheet('plg_system_nrframework/joomla4.css', ['relative' => true, 'version' => 'auto']); + HTML::fixFieldTooltips(); + } else + { + HTMLHelper::stylesheet('plg_system_acf/joomla3.css', ['relative' => true, 'version' => 'auto']); + } + + + return true; + } + + + /** + * Load previewer assets. + * + * @param string $context + * + * @return void + */ + private function loadPreviewer($context) + { + $allowed_context = [ + 'com_fields.field.com_content.article', + 'com_fields.field.com_content.categories', + 'com_fields.field.com_contact.contact', + 'com_fields.field.com_contact.mail', + 'com_fields.field.com_contact.categories' + ]; + + if (!in_array($context, $allowed_context)) + { + return; + } + + // Data passed as JS object + $doc = Factory::getDocument(); + $options = $doc->getScriptOptions('acf_js_object'); + $options = is_array($options) ? $options : []; + $options = [ + 'root_url' => Uri::base() + ]; + $doc->addScriptOptions('acf_js_object', $options); + + // Framework helper to load assets + HTMLHelper::script('plg_system_nrframework/helper.js', ['relative' => true, 'version' => 'auto']); + + // Load Field Previewer + HTMLHelper::script('plg_system_acf/field_previewer.js', ['relative' => true, 'version' => 'auto']); + } + + + /** + * Listens to AJAX requests on ?option=com_ajax&format=raw&plugin=acf + * + * @return void + */ + public function onAjaxAcf() + { + Session::checkToken('request') or jexit(Text::_('JINVALID_TOKEN')); + + // Only in backend + if (!$this->app->isClient('administrator')) + { + return; + } + + // Check if we have a valid task + $task = $this->app->input->get('task', null); + + // Check if we have a valid method task + $taskMethod = 'ajaxTask' . $task; + + if (!method_exists($this, $taskMethod)) + { + die('Task not found'); + } + + $this->$taskMethod(); + } + + /** + * Fields Previewer. + * + * @return string + */ + private function ajaxTaskFieldsPreviewer() + { + $field = $this->app->input->get('field', null); + if (!$field) + { + echo json_encode([ + 'error' => true, + 'message' => 'Missing field name.' + ]); + die(); + } + + if (!$data = json_decode(file_get_contents('php://input'))) + { + echo json_encode([ + 'error' => true, + 'message' => 'Missing field data to generate preview.' + ]); + die(); + } + + // Prepare data + $registry = new Registry(); + foreach ($data as $key => $value) + { + $key = str_replace(['jform[', ']', '['], ['', '', '.'], $key); + $registry->set($key, $value); + } + $data = $registry->toArray(); + + // We require the type of the field to save the fields data to the JSON file and be able to generate the preview + if (!isset($data['type'])) + { + echo json_encode([ + 'error' => true, + 'message' => 'Missing field type to generate preview.' + ]); + die(); + } + + // ACF Field Previewer Class + $class = '\ACF\Previewer\\' . $field; + + // Ensure class exists + if (!class_exists($class)) + { + echo json_encode([ + 'error' => true, + 'message' => 'Cannot preview field: ' . $field + ]); + die(); + } + + // Get class + $class = new $class($data); + + // Setup previewer + $class->setup(); + + echo json_encode([ + 'error' => false + ]); + } + + /** + * Fields Previewer HTML. + * + * @return string + */ + private function ajaxTaskFieldsPreviewerHTML() + { + $field = $this->app->input->get('field', null); + if (!$field) + { + echo json_encode([ + 'error' => true, + 'message' => 'Missing field name.' + ]); + die(); + } + + if (!$html = \ACF\Helpers\Previewer::getFieldPreviewData($field)) + { + return; + } + + echo $html; + } + + /** + * Loads the helper classes of plugin + * + * @return bool + */ + private function getHelper() + { + // Return if is helper is already loaded + if ($this->init) + { + return true; + } + + // Return if we are not in frontend + if (!$this->app->isClient('site')) + { + return false; + } + + // Load Novarain Framework + if (!@include_once(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + return; + } + + // Load Plugin Helper + JLoader::register('ACFHelper', __DIR__ . '/helper/helper.php'); + + return ($this->init = true); + } + + /** + * Let each condition check the value before it's savced into the database + * + * @param string $context + * @param object $article + * @param bool $isNew + * + * @return void + */ + public function onContentBeforeSave($context, $article, $isNew) + { + if (!in_array($context, ['com_fields.field'])) + { + return; + } + + if (!isset($article->params)) + { + return; + } + + $params = json_decode($article->params, true); + if (!isset($params['rules'])) + { + return; + } + + NRFramework\Conditions\ConditionsHelper::getInstance()->onBeforeSave($params['rules']); + + $article->params = json_encode($params); + } +} diff --git a/plugins/system/acf/acf.xml b/plugins/system/acf/acf.xml new file mode 100644 index 00000000..d3842821 --- /dev/null +++ b/plugins/system/acf/acf.xml @@ -0,0 +1,67 @@ + + + PLG_SYSTEM_ACF + PLG_SYSTEM_ACF_DESC + 2.8.8 + May 2017 + Copyright © 2024 Tassos Marinos All Rights Reserved + http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + Tassos Marinos + info@tassos.gr + http://www.tassos.gr + script.install.php + + acf.php + script.install.helper.php + version.php + autoload.php + ACF + media + layouts + helper + form + language + + + + http://www.tassos.gr/updates/advanced-custom-fields-pro.xml + + + + +
+ +
+
+ + + + + + + + + +
+
+
+ + css + js + data + + + + pro +
diff --git a/plugins/system/acf/autoload.php b/plugins/system/acf/autoload.php new file mode 100644 index 00000000..511f38fb --- /dev/null +++ b/plugins/system/acf/autoload.php @@ -0,0 +1,13 @@ + or later +*/ + +defined( '_JEXEC' ) or die( 'Restricted access' ); + +// Registers ACF's namespace +JLoader::registerNamespace('ACF', __DIR__ . '/ACF/', false, false, 'psr4'); \ No newline at end of file diff --git a/plugins/system/acf/form/conditions.xml b/plugins/system/acf/form/conditions.xml new file mode 100644 index 00000000..5698778a --- /dev/null +++ b/plugins/system/acf/form/conditions.xml @@ -0,0 +1,11 @@ + +
+ +
+ +
+
+
\ No newline at end of file diff --git a/plugins/system/acf/form/fields/acffieldlayoutoverrides.php b/plugins/system/acf/form/fields/acffieldlayoutoverrides.php new file mode 100644 index 00000000..e4cb0130 --- /dev/null +++ b/plugins/system/acf/form/fields/acffieldlayoutoverrides.php @@ -0,0 +1,119 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2023 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Form\Field\ListField; +use \NRFramework\Widgets\Helper; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; + +class JFormFieldACFFieldLayoutOverrides extends ListField +{ + /** + * Method to get a list of options for a list input. + * + * @return array An array of options. + */ + protected function getOptions() + { + $layouts = [ + 'default' => 'JOPTION_USE_DEFAULT' + ]; + + $field_type = (string) $this->element['field_type']; + $field_widget_name = (string) $this->element['widget_name']; + $override_widget_directory_name = $this->getOverrideWidgetDirectoryName($field_widget_name, $field_type); + if ($layout_overrides = Helper::getLayoutOverrides($override_widget_directory_name)) + { + $layouts = array_merge($layouts, $layout_overrides); + } + + foreach ($layouts as $key => $title) + { + $options[] = HTMLHelper::_('select.option', $key, Text::_($title)); + } + + return $options; + } + + public function getInput() + { + $field_type = (string) $this->element['field_type']; + $field_widget_name = (string) $this->element['widget_name']; + $override_widget_directory_name = $this->getOverrideWidgetDirectoryName($field_widget_name, $field_type); + + if (!$override_widget_directory_name) + { + return Text::_('ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST'); + } + + $original_path = implode(DIRECTORY_SEPARATOR, [JPATH_PLUGINS, 'system', 'nrframework', 'layouts', 'widgets', $override_widget_directory_name]). '/default.php'; + + $new_path = Helper::getLayoutOverridePath($override_widget_directory_name) . '/LAYOUTNAME.php'; + + $note = '
' . sprintf(Text::_('ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC'), Text::_('PLG_FIELDS_ACF' . $this->getHelpName($field_widget_name, $field_type) . '_LABEL'), $original_path, $new_path) . '
'; + + return parent::getInput() . $note; + } + + /** + * Some widgets require a different directory to be used for layout overrides. + * + * @param string $field_widget_name The widget name + * @param string $field_type The field type + * + * @return string + */ + private function getOverrideWidgetDirectoryName($field_widget_name = null, $field_type = null) + { + if (!$field_widget_name || !$field_type) + { + return; + } + + switch ($field_type) + { + case 'acfvideo': + $field_widget_name = strtolower($this->form->getData()->get('fieldparams.provider', '')); + break; + case 'acfgallery': + if ($this->form->getData()->get('fieldparams.style', '') === 'slideshow') + { + $field_widget_name = 'slideshow'; + } + break; + } + + return strtolower($field_widget_name); + } + + private function getHelpName($field_widget_name = null, $field_type = null) + { + if (!$field_widget_name || !$field_type) + { + return; + } + + switch ($field_type) + { + case 'acfaddress': + $field_widget_name = 'Address'; + break; + case 'acfmap': + $field_widget_name = 'Map'; + break; + } + + return strtoupper($field_widget_name); + } +} \ No newline at end of file diff --git a/plugins/system/acf/form/options.xml b/plugins/system/acf/form/options.xml new file mode 100644 index 00000000..00060398 --- /dev/null +++ b/plugins/system/acf/form/options.xml @@ -0,0 +1,17 @@ + +
+ +
+ + +
+
+
\ No newline at end of file diff --git a/plugins/system/acf/form/rules/acfgravatarvalidator.php b/plugins/system/acf/form/rules/acfgravatarvalidator.php new file mode 100644 index 00000000..1b57395d --- /dev/null +++ b/plugins/system/acf/form/rules/acfgravatarvalidator.php @@ -0,0 +1,63 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +use Joomla\CMS\Form\FormRule; + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\String\StringHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; + +class JFormRuleACFGravatarValidator extends FormRule +{ + /** + * Method to test the calendar value for a valid parts. + * + * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `` tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. This acts as an array container for the field. + * For example if the field has name="foo" and the group value is set to "bar" then the + * full field name would end up being "bar[foo]". + * @param Registry $input An optional Registry object with the entire data set to validate against the entire form. + * @param Form $form The form object for which the field is being tested. + * + * @return boolean True if the value is valid, false otherwise. + * + * @since 3.7.0 + */ + public function test(SimpleXMLElement $element, $value, $group = null, \Joomla\Registry\Registry $input = NULL, \Joomla\CMS\Form\Form $form = NULL) + { + // If the field is empty and not required, the field is valid. + $required = ((string) $element['required'] == 'true' || (string) $element['required'] == 'required'); + + if (!$required && empty($value)) + { + return true; + } + + // Allow Smile Pack Smart Tags + if (StringHelper::strpos($value, '{sp') !== false) + { + return true; + } + + // Validate email + if (filter_var($value, FILTER_VALIDATE_EMAIL)) + { + return true; + } + + Factory::getApplication()->enqueueMessage(Text::_('ACF_INVALID_EMAIL'), 'error'); + return false; + } +} diff --git a/plugins/system/acf/form/rules/acfrequired.php b/plugins/system/acf/form/rules/acfrequired.php new file mode 100644 index 00000000..ca0f6f6c --- /dev/null +++ b/plugins/system/acf/form/rules/acfrequired.php @@ -0,0 +1,56 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +use Joomla\CMS\Form\FormRule; + +defined('_JEXEC') or die('Restricted access'); + +/** + * Form Rule class for the Joomla Platform. + * + * @since 1.7.0 + */ +class JFormRuleACFRequired extends FormRule +{ + /** + * Method to test the calendar value for a valid parts. + * + * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `` tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. This acts as an array container for the field. + * For example if the field has name="foo" and the group value is set to "bar" then the + * full field name would end up being "bar[foo]". + * @param Registry $input An optional Registry object with the entire data set to validate against the entire form. + * @param Form $form The form object for which the field is being tested. + * + * @return boolean True if the value is valid, false otherwise. + * + * @since 3.7.0 + */ + public function test(SimpleXMLElement $element, $value, $group = null, \Joomla\Registry\Registry $input = NULL, \Joomla\CMS\Form\Form $form = NULL) + { + // If the field is empty and not required, the field is valid. + $required = ((string) $element['required'] == 'true' || (string) $element['required'] == 'required'); + + if (!$required && empty($value)) + { + return true; + } + + if (!empty($value)) + { + return true; + } + + return false; + } +} diff --git a/plugins/system/acf/helper/helper.php b/plugins/system/acf/helper/helper.php new file mode 100644 index 00000000..9eb30eb4 --- /dev/null +++ b/plugins/system/acf/helper/helper.php @@ -0,0 +1,87 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Uri\Uri; +use \NRFramework\Conditions\ConditionBuilder; + +/** + * Advanced Custom Fields Helper + */ +class ACFHelper +{ + /** + * Check field publishing assignments. + * + * @param object $field The field object + * + * @return mixed Null when the field does not have rules, boolean when the field runs checks + */ + public static function checkConditions($field) + { + $rules = $field->params->get('rules', []); + + if (empty($rules)) + { + return; + } + + // Convert object to array recursively + $rules = json_decode(json_encode($rules), true); + + return ConditionBuilder::pass($rules); + } + + public static function getFileSources($sources, $allowedExtensions = null) + { + if (!$sources) + { + return; + } + + // Support comma separated values + $sources = is_array($sources) ? $sources : explode(',', $sources); + $result = array(); + + foreach ($sources as $source) + { + if (!$pathinfo = pathinfo($source)) + { + continue; + } + + if (!isset($pathinfo['extension'])) + { + continue; + } + + if ($allowedExtensions && !in_array($pathinfo['extension'], $allowedExtensions)) + { + continue; + } + + // Add root path to local source + if (strpos($source, 'http') === false) + { + $source = Uri::root() . ltrim($source, '/'); + } + + $result[] = array( + 'ext' => $pathinfo['extension'], + 'file' => $source + ); + } + + return $result; + } +} \ No newline at end of file diff --git a/plugins/system/acf/helper/migrator.php b/plugins/system/acf/helper/migrator.php new file mode 100644 index 00000000..77a22042 --- /dev/null +++ b/plugins/system/acf/helper/migrator.php @@ -0,0 +1,412 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2021 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use NRFramework\Extension; +use Joomla\Registry\Registry; +use Joomla\Filesystem\Path; +use Joomla\CMS\Factory; + +class ACFMigrator +{ + /** + * Application object. + * + * @var object + */ + private $app; + + /** + * Database object. + * + * @var object + */ + private $db; + + /** + * Database object. + * + * @var object + */ + private $dbDestination; + + /** + * The currently installed version. + * + * @var string + */ + private $installedVersion; + + /** + * Class constructor + * + * @param string $installedVersion The current extension version + */ + public function __construct($installedVersion, $dbSource = null, $dbDestination = null) + { + $this->app = Factory::getApplication(); + $this->db = $dbSource ? $dbSource : Factory::getDbo(); + $this->dbDestination = $dbDestination ? $dbDestination : Factory::getDbo(); + $this->installedVersion = $installedVersion; + } + + public function do() + { + if (version_compare($this->installedVersion, '1.3.0', '<=')) + { + $this->uploadFieldConvertBasenameToRelativePaths(); + } + + + if (version_compare($this->installedVersion, '2.1', '<=')) + { + $this->migratePublishingRulesToConditionBuilder(); + } + + + if (version_compare($this->installedVersion, '2.7.2', '<=')) + { + $this->updateMapMaximumMarkers(); + } + + if (version_compare($this->installedVersion, '2.7.3', '<=')) + { + + $this->updateAddressDefaultMarkerImage(); + + $this->updateMapValue(); + } + } + + + /** + * ACF Address used the default marker image from the ACF OSM custom field. + * + * Old path: media/plg_fields_acfosm/img/marker.png + * + * However, since the ACF OSM map is now removed from the build, we must update it + * to point to the same file but from the ACF Address custom field. + * + * New value: media/plg_fields_acfaddress/img/marker.png + */ + public function updateAddressDefaultMarkerImage() + { + // Get all custom fields. + $db = $this->db; + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields')) + ->where($db->quoteName('type') . ' = ' . $db->q('acfaddress')); + + $db->setQuery($query); + + if (!$fields = $db->loadObjectList()) + { + return; + } + + foreach ($fields as $key => $field) + { + if (!isset($field->fieldparams)) + { + continue; + } + + $params = json_decode($field->fieldparams); + + $markerImage = $params->marker_image; + + if ($markerImage !== 'media/plg_fields_acfosm/img/marker.png') + { + continue; + } + + $params->marker_image = 'media/plg_fields_acfaddress/img/marker.png'; + + // Update field `param` with new value + $dbDestination = $this->dbDestination; + + $query = $dbDestination->getQuery(true) + ->update($dbDestination->quoteName('#__fields')) + ->set($dbDestination->quoteName('fieldparams') . '=' . $dbDestination->quote(json_encode($params))) + ->where($dbDestination->quoteName('id') . '=' . $dbDestination->quote($field->id)); + + $dbDestination->setQuery($query); + $dbDestination->execute(); + } + } + + + public function updateMapValue() + { + // Get all custom fields. + $db = $this->db; + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields')) + ->where($db->quoteName('type') . ' = ' . $db->q('acfmap')); + + $db->setQuery($query); + + if (!$fields = $db->loadObjectList()) + { + return; + } + + $count = 0; + + foreach ($fields as $key => $field) + { + // Find now all field values + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields_values')) + ->where($db->quoteName('field_id') . ' = ' . $field->id); + + $db->setQuery($query); + + if (!$values = $db->loadObjectList()) + { + return; + } + + foreach ($values as $value) + { + if (!$decoded_value = json_decode($value->value, true)) + { + continue; + } + + if (!isset($decoded_value['markers'])) + { + continue; + } + + $newValue = $decoded_value['markers']; + + $query = $db->getQuery(true); + + $fields = [ + $db->quoteName('value') . ' = ' . $db->q($newValue) + ]; + + $conditions = [ + $db->quoteName('field_id') . ' = ' . $value->field_id, + $db->quoteName('item_id') . ' = ' . $db->quote($value->item_id), + $db->quoteName('value') . ' = ' . $db->quote($value->value) + ]; + + $query->update($db->quoteName('#__fields_values'))->set($fields)->where($conditions); + + $db->setQuery($query); + $db->execute(); + + $count++; + } + } + + return $count; + } + + public function updateMapMaximumMarkers() + { + // Get all custom fields. + $db = $this->db; + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields')) + ->where($db->quoteName('type') . ' = ' . $db->q('acfmap')); + + $db->setQuery($query); + + if (!$fields = $db->loadObjectList()) + { + return; + } + + foreach ($fields as $key => $field) + { + if (!isset($field->fieldparams)) + { + continue; + } + + $params = json_decode($field->fieldparams); + + if (isset($params->maximum_markers)) + { + continue; + } + + $params->maximum_markers = 0; + + // Update field `param` with new value + $dbDestination = $this->dbDestination; + + $query = $dbDestination->getQuery(true) + ->update($dbDestination->quoteName('#__fields')) + ->set($dbDestination->quoteName('fieldparams') . '=' . $dbDestination->quote(json_encode($params))) + ->where($dbDestination->quoteName('id') . '=' . $dbDestination->quote($field->id)); + + $dbDestination->setQuery($query); + $dbDestination->execute(); + } + } + + + public function migratePublishingRulesToConditionBuilder() + { + // Get all custom fields. + $db = $this->db; + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields')); + + $db->setQuery($query); + + if (!$fields = $db->loadObjectList()) + { + return; + } + + foreach ($fields as $key => $field) + { + if (!isset($field->params)) + { + continue; + } + + $params = new Registry($field->params); + + \NRFramework\Conditions\Migrator::run($params); + + if (!isset($params['rules']) || empty($params['rules'])) + { + continue; + } + + // Update field `param` with new value + $dbDestination = $this->dbDestination; + + $query = $dbDestination->getQuery(true) + ->update($dbDestination->quoteName('#__fields')) + ->set($dbDestination->quoteName('params') . '=' . $dbDestination->quote(json_encode($params))) + ->where($dbDestination->quoteName('id') . '=' . $dbDestination->quote($field->id)); + + $dbDestination->setQuery($query); + $dbDestination->execute(); + } + } + + + /** + * Since v1.3.0, the File Upload field is no longer storing just the file's name in the database but the full relative path to Joomla's root. + * + * This migration tasks, loops through all file values in the database and prepends to them the Upload Folder as configured in the Field settings. + * + * Previous value: myAwesomePhoto.jpg + * New value: media/myimages/myAwesomePhoto.jpg + * + * @return mixed Null when the migrationt ask doesn't run, Integer when the migration run. + */ + public function uploadFieldConvertBasenameToRelativePaths() + { + // Get all upload fields. + $db = $this->db; + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields')) + ->where($db->quoteName('type') . ' = ' . $db->q('acfupload')); + + $db->setQuery($query); + + if (!$fields = $db->loadObjectList()) + { + return; + } + + $count = 0; + + foreach ($fields as $key => $field) + { + if (!isset($field->fieldparams)) + { + continue; + } + + $params = json_decode($field->fieldparams); + + if (!isset($params->upload_folder)) + { + continue; + } + + $upload_folder = $params->upload_folder; + + // Find now all field values + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__fields_values')) + ->where($db->quoteName('field_id') . ' = ' . $field->id); + + $db->setQuery($query); + + if (!$values = $db->loadObjectList()) + { + return; + } + + foreach ($values as $value) + { + if (!isset($value->value) || empty($value->value)) + { + continue; + } + + // If the path has a slash, consider it as already fixed. + if (strpos($value->value, DIRECTORY_SEPARATOR) !== false) + { + continue; + } + + $newValue = trim(Path::clean($upload_folder . '/' . $value->value)); + + $query = $db->getQuery(true); + + $fields = [ + $db->quoteName('value') . ' = ' . $db->q($newValue) + ]; + + $conditions = [ + $db->quoteName('field_id') . ' = ' . $value->field_id, + $db->quoteName('item_id') . ' = ' . $db->quote($value->item_id), + $db->quoteName('value') . ' = ' . $db->quote($value->value) + ]; + + $query->update($db->quoteName('#__fields_values'))->set($fields)->where($conditions); + + $db->setQuery($query); + $db->execute(); + + $count++; + } + } + + return $count; + } +} diff --git a/plugins/system/acf/helper/plugin.php b/plugins/system/acf/helper/plugin.php new file mode 100644 index 00000000..9ceb637b --- /dev/null +++ b/plugins/system/acf/helper/plugin.php @@ -0,0 +1,239 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +if (!@include_once(JPATH_PLUGINS . '/system/nrframework/autoload.php')) +{ + throw new RuntimeException('Novarain Framework is not installed', 500); +} + +use \NRFramework\HTML; +use \ACF\Helpers\Field; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; + +JLoader::register('ACFHelper', __DIR__ . '/helper.php'); + +class ACF_Field extends FieldsPlugin +{ + /** + * Override the field type + * + * @var string + */ + protected $overrideType; + + /** + * Whether the field is required + * + * @var bool + */ + protected $required = false; + + /** + * The validation rule will be used to validate the field on saving + * + * @var string + */ + protected $validate; + + /** + * Field's Hint Description + * + * @var string + */ + protected $hint; + + /** + * Field's Class + * + * @var string + */ + protected $class; + + /** + * Prepares the field value for the (front-end) layout + * + * @param string $context The context. + * @param stdclass $item The item. + * @param stdclass $field The field. + * + * @return string + */ + public function onCustomFieldsPrepareField($context, $item, $field) + { + if (!$field->rawvalue) + { + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + // Add Custom CSS + $custom_css = $field->params->get('acf_custom_css'); + if (!empty($custom_css)) + { + + Factory::getDocument()->addStyleDeclaration($custom_css); + } + + return parent::onCustomFieldsPrepareField($context, $item, $field); + } + + /** + * Transforms the field into a DOM XML element and appends it as a child on the given parent. + * + * @param stdClass $field The field. + * @param DOMElement $parent The field node parent. + * @param Form $form The form. + * + * @return DOMElement + * + * @since 3.7.0 + */ + public function onCustomFieldsPrepareDom($field, DOMElement $parent, Form $form) + { + if (!$fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form)) + { + return; + } + + // Load framework's fields + $form->addFieldPath(JPATH_PLUGINS . '/system/nrframework/fields'); + $form->addFieldPath(JPATH_PLUGINS . '/fields/' . $field->type . '/fields'); + + // Set Field Class + if ($this->class) + { + $fieldNode->setAttribute('class', $this->class); + } + + // Set field placeholder + if ($hint = $field->params->get('hint', $this->hint)) + { + $fieldNode->setAttribute('hint', $hint); + } + + // Set Field Type + if ($this->overrideType) + { + $fieldNode->setAttribute('type', $this->overrideType); + } + + // Set Field Required + if ($this->required) + { + $fieldNode->setAttribute('required', $this->required); + } + + // Set validation rule + if ($this->validate) + { + $form->addRulePath(JPATH_PLUGINS . '/system/nrframework/NRFramework/Rules'); + $form->addRulePath(JPATH_PLUGINS . '/system/acf/form/rules'); + $fieldNode->setAttribute('validate', $this->validate); + } + + // Set Field Description + $desc_def = Text::_(str_replace('ACF', 'ACF_', strtoupper($field->type)) . '_VALUE_DESC'); + $desc_user = $fieldNode->getAttribute('description'); + $desc = !empty($desc_user) ? $desc_user : $desc_def; + + $fieldNode->setAttribute('description', $desc); + + // Gather all Joomla 3 CSS fixes in a file and load it. + if (!defined('nrJ4')) + { + HTMLHelper::stylesheet('plg_system_nrframework/joomla3.css', ['relative' => true, 'version' => 'auto']); + HTMLHelper::stylesheet('plg_system_acf/joomla3.css', ['relative' => true, 'version' => 'auto']); + } + else + { + HTMLHelper::stylesheet('plg_system_nrframework/joomla4.css', ['relative' => true, 'version' => 'auto']); + } + + return $fieldNode; + } + + /** + * The form event. Load additional parameters when available into the field form. + * Only when the type of the form is of interest. + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return void + * + * @since 3.7.0 + */ + public function onContentPrepareForm(Form $form, $data) + { + $data = (object) $data; + + // Make sure we are manipulating the right field. + if (!isset($data->type) || (isset($data->type) && ($data->type != $this->_name))) + { + return; + } + + if (isset($data->type)) + { + // Remove "layout" from ACF - Options if the current custom field is not widget-based + $widget = Field::isWidgetBased($data->type, $data); + if (Factory::getApplication()->isClient('administrator') && !$widget) + { + $form->removeField('acf_layout_override', 'params'); + } + } + + // Load framework's fields + $form->addFieldPath(JPATH_PLUGINS . '/system/nrframework/fields'); + + // Load ACF fields + $form->addFieldPath(JPATH_PLUGINS . '/system/acf/form/fields'); + + if (Factory::getApplication()->isClient('administrator')) + { + // Set the wiget in the "acf_layout_override" field in order to retrieve all layout overrides created by the user for this field. + if (isset($widget)) + { + $form->setFieldAttribute('acf_layout_override', 'widget_name', $widget, 'params'); + if (isset($data->type)) + { + $form->setFieldAttribute('acf_layout_override', 'field_type', $data->type, 'params'); + } + } + + // load ACF backend style + HTMLHelper::stylesheet('plg_system_acf/acf-backend.css', ['relative' => true, 'version' => 'auto']); + + if (defined('nrJ4')) + { + HTMLHelper::stylesheet('plg_system_nrframework/joomla4.css', ['relative' => true, 'version' => 'auto']); + HTML::fixFieldTooltips(); + } + + // Display extension notices + \NRFramework\Notices\Notices::getInstance([ + 'ext_element' => 'acf', + 'ext_type' => 'plugin', + 'ext_xml' => 'plg_system_acf', + 'exclude' => [ + 'Geolocation' + ] + ])->show(); + } + + return parent::onContentPrepareForm($form, $data); + } +} diff --git a/plugins/system/acf/language/ca-ES/ca-ES.plg_system_acf.ini b/plugins/system/acf/language/ca-ES/ca-ES.plg_system_acf.ini new file mode 100644 index 00000000..e01dcfa5 --- /dev/null +++ b/plugins/system/acf/language/ca-ES/ca-ES.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Advanced Custom Fields" +PLG_SYSTEM_ACF="Sistema - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Utilitza el connector Advanced Custom Fields per dotar-te de control total de les dades dels teus camps personalitzats Joomla!" +ACF_RETURN_VALUE="Valor de retorn" +ACF_RETURN_VALUE_DESC="Especifica el valor de retorn a la part pública" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Trobes a faltar un camp personalitzat?" +ACF_MISSING_FIELD_DESC="Fes-m'ho saber i estaré encantat d'incloure'l a la col·lecció d'Advanced Custom Fields." +ACF_FIELDS_COLLECTION="Col·lecció de camps" +ACF_METADATA="Metadades" +ACF_CONTROLS="Controls" +ACF_CONTROLS_DESC="Especifica els controls de l'usuari que s'haurien de mostrar (com el botó de reproduir/pausa, etc)." +ACF_PRELOAD="Precàrrega" +ACF_PRELOAD_DESC="Especifica si (i com) s'hauria de carregar quan carrega la pàgina" +ACF_MUTED="Silenciat" +ACF_MUTED_DESC="Especifica si s'hauria de silenciar la sortida d'àudio" +ACF_LOOP="Bucle" +ACF_LOOP_DESC="Especifica si la reproducció ha de tornar a començar cada vegada que acaba" +ACF_AUTOPLAY="Reproducció automàtica" +ACF_AUTOPLAY_DESC="Especifica si començarà a reproduir-se l'arxiu multimèdia tan aviat com estigui disponible" +ACF_UNSUPPORTED_TAG="El teu navegador no soporta l'etiqueta %s." +ACF_WIDTH_DESC="Estableix l'amplada del reproductor" +ACF_HEIGHT_DESC="Estableix l'alçada del reproductor" +ACF_ALLOWFULLSCREEN="Permetre pantalla completa" +ACF_ALLOWFULLSCREEN_DESC="Permet que es reprodueixi el vídeo en pantalla completa." +ACF_FIXED="Fixat" +ACF_RESPONSIVE="Responsiu" +ACF_VIDEO_SIZE="Mida del vídeo" +ACF_VIDEO_SIZE_DESC="Estableix la mida del vídeo inserit.

Fixa: El vídeo s'inserirà a una mida fixa.
Responsiu: El vídeo s'inserirà de manera responsiva, per tant s'adaptarà a l'alçada i amplada del seu contenidor." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/da-DK/da-DK.plg_system_acf.ini b/plugins/system/acf/language/da-DK/da-DK.plg_system_acf.ini new file mode 100644 index 00000000..943fce9e --- /dev/null +++ b/plugins/system/acf/language/da-DK/da-DK.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Avanceret brugerdefinerede felter" +PLG_SYSTEM_ACF="System - Avancerede brugerdefinerede felter (ACF)" +PLG_SYSTEM_ACF_DESC="Anvend plugin'et Avancerede brugerdefinerede felter til at tage fuld kontorl over din Joomla brugerdefinerede feltdata." +ACF_RETURN_VALUE="Returværdi" +ACF_RETURN_VALUE_DESC="Angiv returværdien i frontend" +ACF_ASSIGNMENTS_DESC="Hvis aktiveret, så vil en fane med navnet 'Visningsbetingelser' blive vist op feltredigeringssiden. Med visningsbetingelser kan du filtrere felterne der vises i frontend efter menupunkter, dato, apparat, URLer, domænehenviser, brugergrupper, land og meget mere." +ACF_MISSING_FIELD="Mangler du et brugerdefineret felt?" +ACF_MISSING_FIELD_DESC="Lad mig endelig vide det og jeg vil være glad for at inkludere i Avancerede brugerdefinerede felter. " +ACF_FIELDS_COLLECTION="Feltsamling" +ACF_METADATA="Metadata" +ACF_CONTROLS="Knapper" +ACF_CONTROLS_DESC="Specificerer at afspiller-knapper skal vises (såsom en afspil/pause knap etc)." +ACF_PRELOAD="Præindlæs" +ACF_PRELOAD_DESC="Specificerer om og hvordan videoen skal indlæses når siden indlæser" +ACF_MUTED="Muted" +ACF_MUTED_DESC="Specificerer at audio outputtet fra videoen skal være muted" +ACF_LOOP="Loop" +ACF_LOOP_DESC="Specificerer at afspilleren vil starte forfra, hver gang den er færdig" +ACF_AUTOPLAY="Auto afspil" +ACF_AUTOPLAY_DESC="Specificerer at mediefilen vil starte med at afspille så snart den er klar" +ACF_UNSUPPORTED_TAG="Din browser understøtter ikke %s tagget." +ACF_WIDTH_DESC="Sætter bredden på afspilleren" +ACF_HEIGHT_DESC="Sætter højden på afspillerenthe height of the player" +ACF_ALLOWFULLSCREEN="Tillad fuld skærm" +ACF_ALLOWFULLSCREEN_DESC="Tillad at videoen afspilles i fuldskærmstilstand." +ACF_FIXED="Fast" +ACF_RESPONSIVE="Responsiv" +ACF_VIDEO_SIZE="Viedostørrelse" +ACF_VIDEO_SIZE_DESC="Angiv størrelsen på den indlejrede video.

Fast: Video vil blive indlejret med en fast størrelse.
Responsiv: Videon vil blive indlejret responsivt, så at størrelsen tilpasser sig til højden og bredden at dets beholder." +ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! indholdsside typer" +ACF_FIELD_PREVIEWER="Felt forhåndsviser" +ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Forhåndsvise hvordan dette brugerdefinerede felt vil se ud på dit websted." +ACF_ACF_OPTIONS="ACF indstillinger" +ACF_CUSTOM_CSS="Brugerdefineret CSS" +ACF_CUSTOM_CSS_DESC="Anvend brugerdefineret CSS på dit websted når dette brugerdefinerede felt vises." +ACF_FIELD_LAYOUT_OVERRIDES="Vælg hvilket layout der skal anvendes når dette brugerdefinerede felt bliver vist på dit websted." +ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="For at oprette en overskrivning for %s, så kopier layout filen %s til %s.

Du kan læse mere her: Hvordan oprettes et alternativt layout til Advanced Custom Fields" +ACF_WIDGET_LAYOUT="Widget layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/de-DE/de-DE.plg_system_acf.ini b/plugins/system/acf/language/de-DE/de-DE.plg_system_acf.ini new file mode 100644 index 00000000..d690d5ac --- /dev/null +++ b/plugins/system/acf/language/de-DE/de-DE.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Erweiterte benutzerdefinierte Felder" +PLG_SYSTEM_ACF="System - Erweiterte benutzerdefinierte Felder (ACF)" +PLG_SYSTEM_ACF_DESC="Verwenden Sie das Plugin\" Erweiterte benutzerdefinierte Felder \", um die vollständige Kontrolle über Ihre benutzerdefinierten Joomla-Felddaten zu erlangen." +ACF_RETURN_VALUE="Rückgabewert" +ACF_RETURN_VALUE_DESC="Rückgabewert im Frontend angeben" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Vermissen Sie ein benutzerdefiniertes Feld?" +ACF_MISSING_FIELD_DESC="Bitte lassen Sie es mich wissen, und ich bin froh, die Sammlung\" Erweiterte benutzerdefinierte Felder \"hinzuzufügen." +ACF_FIELDS_COLLECTION="Feldsammlung" +ACF_METADATA="Metadaten" +ACF_CONTROLS="Steuerelemente" +ACF_CONTROLS_DESC="Gibt an, dass die Player-Steuerelemente angezeigt werden sollen (z. B. eine Wiedergabe- / Pausentaste usw.)." +ACF_PRELOAD="Vorladen" +ACF_PRELOAD_DESC="Gibt an, ob und wie das Video beim Laden der Seite geladen werden soll." +ACF_MUTED="Stumm geschaltet" +ACF_MUTED_DESC="Gibt an, dass die Audioausgabe des Videos stummgeschaltet werden soll" +ACF_LOOP="Schleife" +ACF_LOOP_DESC="Gibt an, dass der Player nach jedem Beenden erneut gestartet wird." +ACF_AUTOPLAY="Automatische Wiedergabe" +ACF_AUTOPLAY_DESC="Gibt an, dass die Mediendatei abgespielt wird, sobald sie fertig ist." +ACF_UNSUPPORTED_TAG="Ihr Browser unterstützt das %s-Tag nicht." +ACF_WIDTH_DESC="Legt die Breite des Players fest" +ACF_HEIGHT_DESC="Legt die Höhe des Players fest" +ACF_ALLOWFULLSCREEN="Vollbild zulassen" +ACF_ALLOWFULLSCREEN_DESC="Das Video kann im Vollbildmodus abgespielt werden." +ACF_FIXED="Behoben" +ACF_RESPONSIVE="Responsive" +ACF_VIDEO_SIZE="Videogröße" +ACF_VIDEO_SIZE_DESC="Legen Sie die Größe des eingebetteten Videos fest.

Fest : Das Video wird in einer festen Größe eingebettet.
Responsive : Das Video wird reaktionsschnell eingebettet, sodass es sich an die Höhe und Breite seines Containers anpasst. " +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/el-GR/el-GR.plg_system_acf.ini b/plugins/system/acf/language/el-GR/el-GR.plg_system_acf.ini new file mode 100644 index 00000000..efd5cba2 --- /dev/null +++ b/plugins/system/acf/language/el-GR/el-GR.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +; ACF="Advanced Custom Fields" +; PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +; PLG_SYSTEM_ACF_DESC="Use the Advanced Custom Fields plugin to take full control of your Joomla custom field data." +; ACF_RETURN_VALUE="Return Value" +; ACF_RETURN_VALUE_DESC="Specify the return value on front-end" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +; ACF_MISSING_FIELD="Are you missing a custom field?" +; ACF_MISSING_FIELD_DESC="Please, do let me know and I'll be glad to include it the Advanced Custom Fields collection. " +; ACF_FIELDS_COLLECTION="Fields Collection" +; ACF_METADATA="Metadata" +; ACF_CONTROLS="Controls" +; ACF_CONTROLS_DESC="Specifies that player controls should be displayed (such as a play/pause button etc)." +; ACF_PRELOAD="Preload" +; ACF_PRELOAD_DESC="Specifies if and how the video should be loaded when the page loads" +; ACF_MUTED="Muted" +; ACF_MUTED_DESC="Specifies that the audio output of the video should be muted" +; ACF_LOOP="Loop" +; ACF_LOOP_DESC="Specifies that the player will start over again, every time it is finished" +; ACF_AUTOPLAY="Auto Play" +; ACF_AUTOPLAY_DESC="Specifies that the media file will start playing as soon as it is ready" +; ACF_UNSUPPORTED_TAG="Your browser does not support the %s tag." +; ACF_WIDTH_DESC="Sets the width of the player" +; ACF_HEIGHT_DESC="Sets the height of the player" +; ACF_ALLOWFULLSCREEN="Allow Fullscreen" +; ACF_ALLOWFULLSCREEN_DESC="Allow the video to be played in fullscreen mode." +ACF_FIXED="Διορθώθηκε" +; ACF_RESPONSIVE="Responsive" +; ACF_VIDEO_SIZE="Video Size" +; ACF_VIDEO_SIZE_DESC="Set the size of the embedded video.

Fixed: The video will be embedded at a fixed size.
Responsive: The video will be embedded responsively, so it will adapt to the height and width of its container." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/en-GB/en-GB.plg_system_acf.ini b/plugins/system/acf/language/en-GB/en-GB.plg_system_acf.ini new file mode 100644 index 00000000..0e4d1914 --- /dev/null +++ b/plugins/system/acf/language/en-GB/en-GB.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Advanced Custom Fields" +PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Use the Advanced Custom Fields plugin to take full control of your Joomla custom field data." +ACF_RETURN_VALUE="Return Value" +ACF_RETURN_VALUE_DESC="Specify the return value on front-end" +ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Are you missing a custom field?" +ACF_MISSING_FIELD_DESC="Please, do let me know and I'll be glad to include it the Advanced Custom Fields collection. " +ACF_FIELDS_COLLECTION="Fields Collection" +ACF_METADATA="Metadata" +ACF_CONTROLS="Controls" +ACF_CONTROLS_DESC="Specifies that player controls should be displayed (such as a play/pause button etc)." +ACF_PRELOAD="Preload" +ACF_PRELOAD_DESC="Specifies if and how the video should be loaded when the page loads" +ACF_MUTED="Muted" +ACF_MUTED_DESC="Specifies that the audio output of the video should be muted" +ACF_LOOP="Loop" +ACF_LOOP_DESC="Specifies that the player will start over again, every time it is finished" +ACF_AUTOPLAY="Auto Play" +ACF_AUTOPLAY_DESC="Specifies that the media file will start playing as soon as it is ready" +ACF_UNSUPPORTED_TAG="Your browser does not support the %s tag." +ACF_WIDTH_DESC="Sets the width of the player" +ACF_HEIGHT_DESC="Sets the height of the player" +ACF_ALLOWFULLSCREEN="Allow Fullscreen" +ACF_ALLOWFULLSCREEN_DESC="Allow the video to be played in fullscreen mode." +ACF_FIXED="Fixed" +ACF_RESPONSIVE="Responsive" +ACF_VIDEO_SIZE="Video Size" +ACF_VIDEO_SIZE_DESC="Set the size of the embedded video.

Fixed: The video will be embedded at a fixed size.
Responsive: The video will be embedded responsively, so it will adapt to the height and width of its container." +ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +ACF_FIELD_PREVIEWER="Field Previewer" +ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +ACF_ACF_OPTIONS="ACF Options" +ACF_CUSTOM_CSS="Custom CSS" +ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +ACF_WIDGET_LAYOUT="Widget Layout" +ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +ACF_INVALID_EMAIL="Invalid email address" \ No newline at end of file diff --git a/plugins/system/acf/language/en-GB/en-GB.plg_system_acf.sys.ini b/plugins/system/acf/language/en-GB/en-GB.plg_system_acf.sys.ini new file mode 100644 index 00000000..e318994d --- /dev/null +++ b/plugins/system/acf/language/en-GB/en-GB.plg_system_acf.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Use the Advanced Custom Fields plugin to take full control of your Joomla custom field data." \ No newline at end of file diff --git a/plugins/system/acf/language/es-ES/es-ES.plg_system_acf.ini b/plugins/system/acf/language/es-ES/es-ES.plg_system_acf.ini new file mode 100644 index 00000000..b7d6d38b --- /dev/null +++ b/plugins/system/acf/language/es-ES/es-ES.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Campos Personalizados Avanzados" +PLG_SYSTEM_ACF="Sistema - Campos Personalizados Avanzados (ACF)" +PLG_SYSTEM_ACF_DESC="Utilice el complemento Campos Personalizados Avanzados para tomar el control total de los datos de sus campos personalizados de Joomla." +ACF_RETURN_VALUE="Valor de Retorno " +ACF_RETURN_VALUE_DESC="Especifique el valor de retorno en el front-end" +ACF_ASSIGNMENTS_DESC="Si está habilitado, aparecerá una pestaña llamada 'Condiciones de visualización' en la página de edición de campo. Con las Condiciones de visualización, puede filtrar los campos que se muestran en el front-end por Elementos de menú, Fecha, Dispositivo, URL, Referencia de dominio, Grupos de usuarios, País y mucho más." +ACF_MISSING_FIELD="¿Le falta un campo personalizado?" +ACF_MISSING_FIELD_DESC="Por favor, hágamelo saber y estaré encantado de incluirlo en la colección de campos personalizados avanzados." +ACF_FIELDS_COLLECTION="Colección de Campos" +ACF_METADATA="Metadatos" +ACF_CONTROLS="Controles" +ACF_CONTROLS_DESC="Especifica que se deben mostrar los controles del reproductor (como un botón de reproducción/pausa, etc.)." +ACF_PRELOAD="Precarga" +ACF_PRELOAD_DESC="Especifica si y cómo se debe cargar el video cuando se carga la página" +ACF_MUTED="Silenciar" +ACF_MUTED_DESC="Especifica que la salida de audio del video debe silenciarse" +ACF_LOOP="Bucle" +ACF_LOOP_DESC="Especifica que el reproductor comenzará de nuevo, cada vez que termine" +ACF_AUTOPLAY="Auto-reproducción" +ACF_AUTOPLAY_DESC="Especifica que el archivo multimedia comenzará a reproducirse tan pronto como esté listo" +ACF_UNSUPPORTED_TAG="Su navegador no admite la etiqueta %s." +ACF_WIDTH_DESC="Establece el ancho del reproductor" +ACF_HEIGHT_DESC="Establece la altura del reproductor." +ACF_ALLOWFULLSCREEN="Permitir pantalla completa" +ACF_ALLOWFULLSCREEN_DESC="Permita que el video se reproduzca en modo de pantalla completa." +ACF_FIXED="Fijo" +ACF_RESPONSIVE="Adaptativo" +ACF_VIDEO_SIZE="Tamaño del Video" +ACF_VIDEO_SIZE_DESC="Establezca el tamaño del video enbebido.

Fijo:El video se incrustará en un tamaño fijo.
Adaptativo: El video se incrustará de forma receptiva, por lo que se adaptará a la altura y el ancho de su contenedor." +ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Tipos de Página de Contenido" +ACF_FIELD_PREVIEWER="Vista Previa del Campo" +ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Obtenga una vista previa de cómo se verá este campo personalizado en su sitio." +ACF_ACF_OPTIONS="Opciones de ACF" +ACF_CUSTOM_CSS="Personalizar CSS" +ACF_CUSTOM_CSS_DESC="Aplique CSS personalizado en su sitio cuando se muestre este campo personalizado." +ACF_FIELD_LAYOUT_OVERRIDES="Seleccione qué diseño usar cuando este campo personalizado se muestre en su sitio." +ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="Para crear una anulación para %s, copie el %s archivo de diseño para %s.

Puede leer más aquí:Cómo crear un diseño alternativo para campos personalizados avanzados" +ACF_WIDGET_LAYOUT="Diseño de widgets" +ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="Para garantizar que las Condiciones de visualización funcionen correctamente en la página Perfil de usuario, el complemento 'Sistema - Campos' debe colocarse antes del complemento 'Sistema - Campos personalizados avanzados (ACF)'. Al ajustar el orden de los complementos como se describe, resolverá esta advertencia." +ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Primero guarde el campo personalizado para seleccionar un diseño." +ACF_INVALID_EMAIL="Dirección de correo electrónico no válida" diff --git a/plugins/system/acf/language/es-ES/es-ES.plg_system_acf.sys.ini b/plugins/system/acf/language/es-ES/es-ES.plg_system_acf.sys.ini new file mode 100644 index 00000000..59355bdd --- /dev/null +++ b/plugins/system/acf/language/es-ES/es-ES.plg_system_acf.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_SYSTEM_ACF="Sistema - Campos Personalizados Avanzados (ACF)" +PLG_SYSTEM_ACF_DESC="Utilice el complemento Campos Personalizados Avanzados para tomar el control total de los datos de sus campos personalizados de Joomla." diff --git a/plugins/system/acf/language/fr-FR/fr-FR.plg_system_acf.ini b/plugins/system/acf/language/fr-FR/fr-FR.plg_system_acf.ini new file mode 100644 index 00000000..f069ce13 --- /dev/null +++ b/plugins/system/acf/language/fr-FR/fr-FR.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +; ACF="Advanced Custom Fields" +; PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Utilisez le plugin Advanced Custom Fields pour prendre le contrôle total de vos données de champ personnalisé Joomla." +ACF_RETURN_VALUE="Retourner la valeur" +ACF_RETURN_VALUE_DESC="Spécifier la valeur de retour sur le front-end" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Il vous manque un champ personnalisé ?" +ACF_MISSING_FIELD_DESC="S'il vous plaît, faites-le moi savoir et je serai heureux de l'inclure dans la collection Advanced Custom Fields." +ACF_FIELDS_COLLECTION="Ensemble de champs" +ACF_METADATA="Méta-données" +ACF_CONTROLS="Commandes" +ACF_CONTROLS_DESC="Spécifie si les contrôles du lecteur doivent être affichés (tels que les boutons Lecture/Pause, etc)." +ACF_PRELOAD="Préchargement" +ACF_PRELOAD_DESC="Spécifie si et comment la vidéo est chargée une fois la page chargée" +ACF_MUTED="Muté" +ACF_MUTED_DESC="Spécifie que le son sortant de la vidéo est muté" +ACF_LOOP="Boucle" +ACF_LOOP_DESC="Spécifie que la lecture recommence a chaque fois qu'il a terminé" +ACF_AUTOPLAY="Lecture automatique" +ACF_AUTOPLAY_DESC="Spécifie que le fichier multimédia commencera à être lu dès qu'il sera prêt" +ACF_UNSUPPORTED_TAG="Votre navigateur ne supporte pas le tag %s." +ACF_WIDTH_DESC="Définit la largeur du lecteur" +ACF_HEIGHT_DESC="Définit la hauteur du lecteur" +ACF_ALLOWFULLSCREEN="Autoriser le plein écran" +ACF_ALLOWFULLSCREEN_DESC="Autorise la vidéo à être jouer en mode plein-écran." +; ACF_FIXED="Fixed" +; ACF_RESPONSIVE="Responsive" +; ACF_VIDEO_SIZE="Video Size" +; ACF_VIDEO_SIZE_DESC="Set the size of the embedded video.

Fixed: The video will be embedded at a fixed size.
Responsive: The video will be embedded responsively, so it will adapt to the height and width of its container." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/it-IT/it-IT.plg_system_acf.ini b/plugins/system/acf/language/it-IT/it-IT.plg_system_acf.ini new file mode 100644 index 00000000..d3a91716 --- /dev/null +++ b/plugins/system/acf/language/it-IT/it-IT.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Advanced Custom Fields" +PLG_SYSTEM_ACF="Sistema - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Utilizza il plugin Advanced Custom Fields per acquisire il pieno controllo dei dati dei campi personalizzati di Joomla." +ACF_RETURN_VALUE="Valore di ritorno" +ACF_RETURN_VALUE_DESC="Specifica il valore per il front-end" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Non trovi un campo personalizzato?" +ACF_MISSING_FIELD_DESC="Per favore, fammelo sapere e sarò lieto di includerlo nella collezione di Advanced Custom Fields" +ACF_FIELDS_COLLECTION="Collezione di campi " +ACF_METADATA="Metadati" +ACF_CONTROLS="Controlli" +ACF_CONTROLS_DESC="Specifica che i controlli del lettore devono essere visualizzati (come il pulsante di riproduzione/pausa, ecc.)." +ACF_PRELOAD="Precarica" +ACF_PRELOAD_DESC="Specifica se e come il video deve essere caricato quando viene caricata la pagina" +ACF_MUTED="Audio disattivato" +ACF_MUTED_DESC="Specifica che l'uscita audio del video deve essere disattivata" +ACF_LOOP="Ciclo continuo" +ACF_LOOP_DESC="Specifica che il lettore ricomincerà da capo ogni volta che sarà terminato" +ACF_AUTOPLAY="Riproduzione automatica" +ACF_AUTOPLAY_DESC="Specifica che il file multimediale inizierà la riproduzione non appena sarà pronto" +ACF_UNSUPPORTED_TAG="Il tuo browser non supporta il tag %s." +ACF_WIDTH_DESC="Imposta la larghezza del lettore" +ACF_HEIGHT_DESC="Impsta l'altezza del lettore" +ACF_ALLOWFULLSCREEN="Permetti schermo intero" +ACF_ALLOWFULLSCREEN_DESC="Consenti la riproduzione del video a schermo intero." +ACF_FIXED="Fisso" +ACF_RESPONSIVE="Responsive" +ACF_VIDEO_SIZE="Dimensione video" +ACF_VIDEO_SIZE_DESC="Imposta la dimensione del video incorporato.

Fisso: il video verrà incorporato a una dimensione fissa.
Responsive: il video verrà incorporato in modo responsive, quindi si adatterà all'altezza e alla larghezza del suo contenitore." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/nl-NL/nl-NL.plg_system_acf.ini b/plugins/system/acf/language/nl-NL/nl-NL.plg_system_acf.ini new file mode 100644 index 00000000..7cac8140 --- /dev/null +++ b/plugins/system/acf/language/nl-NL/nl-NL.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Advanced Custom Fields" +PLG_SYSTEM_ACF="Systeem - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Gebruik de Advanced Custom Fields plugin om volledige controle te krijgen over de aangepaste veldgegevens van Joomla." +ACF_RETURN_VALUE="Winstwaarde" +ACF_RETURN_VALUE_DESC="Geef de retourwaarde op aan de front-end" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Mist je nog een aangepast veld?" +ACF_MISSING_FIELD_DESC="Laat het me alsjeblieft weten en ik voeg het graag toe aan de Advanced Custom Fields-collectie." +ACF_FIELDS_COLLECTION="Velden Collectie" +ACF_METADATA="Metadata" +ACF_CONTROLS="Bediening" +ACF_CONTROLS_DESC="Specificeert welke spelerbedieningen zullen worden weergegeven (zoals een afspeel- / pauzeknop enz.)." +ACF_PRELOAD="Vooraf laden" +ACF_PRELOAD_DESC="Geeft aan of en hoe de video zal worden geladen wanneer de pagina wordt geladen" +ACF_MUTED="Gedempt" +ACF_MUTED_DESC="Geeft aan dat de audio-uitvoer van de video zal worden gedempt" +ACF_LOOP="Lus" +ACF_LOOP_DESC="Geeft aan dat de speler elke keer opnieuw zal beginnen als hij klaar is" +ACF_AUTOPLAY="Automatisch Afspelen" +ACF_AUTOPLAY_DESC="Geeft aan dat het mediabestand zal zodra hij gereed is worden afgespeeld " +ACF_UNSUPPORTED_TAG="Jouw browser ondersteunt de tag %s niet." +ACF_WIDTH_DESC="Stelt de breedte van de speler in" +ACF_HEIGHT_DESC="Stelt de hoogte van de speler in" +ACF_ALLOWFULLSCREEN="Volledig Scherm Toestaan" +ACF_ALLOWFULLSCREEN_DESC="Sta toe dat de video op volledig scherm wordt afgespeeld." +ACF_FIXED="Vastgezet" +ACF_RESPONSIVE="Responsive" +ACF_VIDEO_SIZE="Videoformaat" +ACF_VIDEO_SIZE_DESC="Stel de grootte van de ingesloten video in.

Vast: de video wordt ingesloten op een vaste grootte.
Responsive: De video wordt responsief ingesloten, zodat deze zich aanpast aan de hoogte en breedte van de container." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/pt-BR/pt-BR.plg_system_acf.ini b/plugins/system/acf/language/pt-BR/pt-BR.plg_system_acf.ini new file mode 100644 index 00000000..95409fd7 --- /dev/null +++ b/plugins/system/acf/language/pt-BR/pt-BR.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Campos Personalizados Avançados" +PLG_SYSTEM_ACF="Sistema - Campos Personalizados Avançados (ACF)" +PLG_SYSTEM_ACF_DESC="Usa o plugin Advanced Custom Fields tomar o controle total de seus dados de campos personalizados do Joomla." +ACF_RETURN_VALUE="Valor de Retorno" +ACF_RETURN_VALUE_DESC="Especifique o valor de retorno no frontend." +ACF_ASSIGNMENTS_DESC="Se ativado, uma guia chamada 'Condições de Exibição' aparecerá na página de edição do campo. Com as Condições de Exibição você pode filtrar os campos exibidos no front-end por Itens de Menu, Data, Dispositivo, URLs, Referenciador de Domínio, Grupos de Usuários, País e muito mais." +ACF_MISSING_FIELD="Falta um campo personalizado?" +ACF_MISSING_FIELD_DESC="Por favor, deixe-me saber e eu ficarei feliz em incluí-lo na coleção do Advanced Custom Fields." +ACF_FIELDS_COLLECTION="Coleção de Campos" +ACF_METADATA="Metadata" +ACF_CONTROLS="Controles" +ACF_CONTROLS_DESC="Especifica que os controles do player devem ser exibidos (como um botão reproduzir/pausar, etc.)." +ACF_PRELOAD="Pré-carregar" +ACF_PRELOAD_DESC="Especifica se, e como o vídeo deve ser carregado quando a página for carregada" +ACF_MUTED="Silenciado" +ACF_MUTED_DESC="Especifica que a saída de áudio do vídeo deve ser silenciada" +ACF_LOOP="Infinito" +ACF_LOOP_DESC="Especifica que o player recomeçará sempre que terminar" +ACF_AUTOPLAY="Reprodução automática" +ACF_AUTOPLAY_DESC="Especifica que o arquivo de mídia começará a ser reproduzido assim que estiver pronto" +ACF_UNSUPPORTED_TAG="Seu navegador não suporta a %stag." +ACF_WIDTH_DESC="Define a largura do player" +ACF_HEIGHT_DESC="Define a altura do player" +ACF_ALLOWFULLSCREEN="Permitir tela cheia" +ACF_ALLOWFULLSCREEN_DESC="Permitir que o vídeo seja reproduzido em modo de tela cheia." +ACF_FIXED="Fixo" +ACF_RESPONSIVE="Responsivo" +ACF_VIDEO_SIZE="Tamanho do vídeo" +ACF_VIDEO_SIZE_DESC="Defina o tamanho do vídeo incorporado.

Fixo: o vídeo será incorporado em um tamanho fixo.
Responsivo: O vídeo será incorporado de forma responsiva, para que se adapte à altura e largura do contêiner." +ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Tipos de páginas de conteúdo" +ACF_FIELD_PREVIEWER="Pré-visualizador de campo" +ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Visualizar a aparência desse campo personalizado no site." +ACF_ACF_OPTIONS="Opções ACF" +ACF_CUSTOM_CSS="CSS personalizado" +ACF_CUSTOM_CSS_DESC="Aplique CSS personalizado no site quando este campo personalizado for exibido." +ACF_FIELD_LAYOUT_OVERRIDES="Selecione qual layout usar quando este campo personalizado for exibido no site." +ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="Para criar uma substituição para%s , copie o %sarquivo de layout para %s.

Você pode ler mais aqui: Como criar um layout alternativo para Campos Personalizados Avançados" +ACF_WIDGET_LAYOUT="Esquema de widgets" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/ru-RU/ru-RU.plg_system_acf.ini b/plugins/system/acf/language/ru-RU/ru-RU.plg_system_acf.ini new file mode 100644 index 00000000..904271f7 --- /dev/null +++ b/plugins/system/acf/language/ru-RU/ru-RU.plg_system_acf.ini @@ -0,0 +1,37 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Расширенные пользовательские поля " +PLG_SYSTEM_ACF="Система - Расширенные пользовательские поля (ACF)" +PLG_SYSTEM_ACF_DESC="Используйте плагин Advanced Custom Fields, чтобы получить полный контроль над данными пользовательских полей Joomla." +ACF_RETURN_VALUE="Возвращаемое значение" +ACF_RETURN_VALUE_DESC="Указать возвращаемое значение в интерфейсе" +ACF_ASSIGNMENTS="Назначения публикации" +ACF_ASSIGNMENTS_DESC="Если включено, на странице редактирования пользовательских полей появится новая вкладка под названием« Назначения публикации ». С помощью назначений публикации вы можете фильтровать поля, отображаемые в интерфейсе, по пунктам меню, дате, устройству, URL-адресам, рефереру домена. , Группы пользователей, Страна и многое другое. " +ACF_MISSING_FIELD="Вам не хватает настраиваемого поля?" +ACF_MISSING_FIELD_DESC="Пожалуйста, дайте мне знать, и я буду рад включить его в коллекцию Advanced Custom Fields." +ACF_FIELDS_COLLECTION="Коллекция полей" +ACF_METADATA="Метаданные" +ACF_CONTROLS="Управление" +ACF_CONTROLS_DESC="Указывает, что элементы управления плеером должны отображаться (например, кнопка воспроизведения / паузы и т. Д.)." +ACF_PRELOAD="Предзагрузка" +ACF_PRELOAD_DESC="Указывает, нужно ли и как загружать видео при загрузке страницы" +ACF_MUTED="Отключено" +ACF_MUTED_DESC="Указывает, что аудиовыход видео должен быть отключен" +ACF_LOOP="Цикл" +ACF_LOOP_DESC="Указывает, что игрок будет начинать сначала, каждый раз, когда он закончится" +ACF_AUTOPLAY="Автовоспроизведение" +ACF_AUTOPLAY_DESC="Указывает, что медиафайл начнет воспроизводиться, как только он будет готов" +ACF_UNSUPPORTED_TAG="Ваш браузер не поддерживает тег %s." +ACF_WIDTH_DESC="Устанавливает ширину плеера" +ACF_HEIGHT_DESC="Устанавливает высоту игрока" +ACF_ALLOWFULLSCREEN="Разрешить полный экран" +ACF_ALLOWFULLSCREEN_DESC="Разрешить воспроизведение видео в полноэкранном режиме." +ACF_FIXED="Фиксированный" +ACF_RESPONSIVE="Адаптивный" +ACF_VIDEO_SIZE="Размер видео" +ACF_VIDEO_SIZE_DESC="Установить размер встроенного видео.

Исправлено . Видео будет встроено в фиксированный размер.
Отзывчивый : Видео будет встроено быстро, поэтому оно будет адаптироваться к высоте и ширине контейнера. " diff --git a/plugins/system/acf/language/sv-SE/sv-SE.plg_system_acf.ini b/plugins/system/acf/language/sv-SE/sv-SE.plg_system_acf.ini new file mode 100644 index 00000000..e93c720e --- /dev/null +++ b/plugins/system/acf/language/sv-SE/sv-SE.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Advanced Custom Fields" +PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Använd Advanced Custom Fields pluginen för att ta full kontroll över dina Joomla anpassade fältdata." +ACF_RETURN_VALUE="Returvärde" +ACF_RETURN_VALUE_DESC="Ange returvärdet i frontend." +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +ACF_MISSING_FIELD="Saknar du ett anpassat fält?" +ACF_MISSING_FIELD_DESC="Snälla, låt mig veta så kommer jag gärna att inkludera den i Advanced Custom Fields-samlingen." +ACF_FIELDS_COLLECTION="Fält-samling" +ACF_METADATA="Metadata" +ACF_CONTROLS="Kontroller" +ACF_CONTROLS_DESC="Anger att spelarkontroller ska visas (t.ex. en uppspelning / paus-knapp osv.)." +ACF_PRELOAD="Förladdning" +ACF_PRELOAD_DESC="Anger om och hur videon ska laddas när sidan laddas." +ACF_MUTED="Tyst" +ACF_MUTED_DESC="Anger att ljudet på videon ska stängas av." +ACF_LOOP="Repetera" +ACF_LOOP_DESC="Anger att spelaren ska börja om varje gång den har spelat klart." +ACF_AUTOPLAY="Auto Play" +ACF_AUTOPLAY_DESC="Anger att mediefilen kommer att börja spelas så snart den är klar." +ACF_UNSUPPORTED_TAG="Din webbläsare stöder inte taggen %s." +ACF_WIDTH_DESC="Anger spelarens bredd" +ACF_HEIGHT_DESC="Anger spelarens höjd" +ACF_ALLOWFULLSCREEN="Tillåt helskärm" +ACF_ALLOWFULLSCREEN_DESC="Tillåt att videon spelas upp i helskärmsläge." +ACF_FIXED="Fast" +ACF_RESPONSIVE="Responsiv" +ACF_VIDEO_SIZE="Videons storlek" +ACF_VIDEO_SIZE_DESC="Ange storleken på den inbäddade videon.

Fast: Videon kommer att bäddas in i en fast storlek.
Responsiv: Videon kommer att bäddas in responsivt, så den kommer att anpassas till behållarens höjd och bredd." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/sv-SE/sv-SE.plg_system_acf.sys.ini b/plugins/system/acf/language/sv-SE/sv-SE.plg_system_acf.sys.ini new file mode 100644 index 00000000..065e4daa --- /dev/null +++ b/plugins/system/acf/language/sv-SE/sv-SE.plg_system_acf.sys.ini @@ -0,0 +1,9 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +PLG_SYSTEM_ACF_DESC="Använd Advanced Custom Fields pluginen för att ta full kontroll över dina Joomla anpassade fältdata." diff --git a/plugins/system/acf/language/tr-TR/tr-TR.plg_system_acf.ini b/plugins/system/acf/language/tr-TR/tr-TR.plg_system_acf.ini new file mode 100644 index 00000000..decb083d --- /dev/null +++ b/plugins/system/acf/language/tr-TR/tr-TR.plg_system_acf.ini @@ -0,0 +1,48 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +; ACF="Advanced Custom Fields" +; PLG_SYSTEM_ACF="System - Advanced Custom Fields (ACF)" +; PLG_SYSTEM_ACF_DESC="Use the Advanced Custom Fields plugin to take full control of your Joomla custom field data." +; ACF_RETURN_VALUE="Return Value" +ACF_RETURN_VALUE_DESC="Ön uçtaki dönüş değeri belirtin" +; ACF_ASSIGNMENTS_DESC="If enabled, a tab called 'Display Conditions' will appear in the Field editing page. With the Display Conditions you can filter the fields displayed on the front-end by Menu Items, Date, Device, URLs, Domain Referrer, User Groups, Country and much more." +; ACF_MISSING_FIELD="Are you missing a custom field?" +; ACF_MISSING_FIELD_DESC="Please, do let me know and I'll be glad to include it the Advanced Custom Fields collection. " +; ACF_FIELDS_COLLECTION="Fields Collection" +; ACF_METADATA="Metadata" +; ACF_CONTROLS="Controls" +; ACF_CONTROLS_DESC="Specifies that player controls should be displayed (such as a play/pause button etc)." +; ACF_PRELOAD="Preload" +; ACF_PRELOAD_DESC="Specifies if and how the video should be loaded when the page loads" +; ACF_MUTED="Muted" +; ACF_MUTED_DESC="Specifies that the audio output of the video should be muted" +; ACF_LOOP="Loop" +; ACF_LOOP_DESC="Specifies that the player will start over again, every time it is finished" +; ACF_AUTOPLAY="Auto Play" +; ACF_AUTOPLAY_DESC="Specifies that the media file will start playing as soon as it is ready" +; ACF_UNSUPPORTED_TAG="Your browser does not support the %s tag." +; ACF_WIDTH_DESC="Sets the width of the player" +; ACF_HEIGHT_DESC="Sets the height of the player" +; ACF_ALLOWFULLSCREEN="Allow Fullscreen" +; ACF_ALLOWFULLSCREEN_DESC="Allow the video to be played in fullscreen mode." +; ACF_FIXED="Fixed" +; ACF_RESPONSIVE="Responsive" +; ACF_VIDEO_SIZE="Video Size" +; ACF_VIDEO_SIZE_DESC="Set the size of the embedded video.

Fixed: The video will be embedded at a fixed size.
Responsive: The video will be embedded responsively, so it will adapt to the height and width of its container." +; ACF_ASSIGNMENT_CONTENT_PAGE_TYPES="Joomla! Content Page Types" +; ACF_FIELD_PREVIEWER="Field Previewer" +; ACF_FIELD_PREVIEWER_INFO_ICON_TITLE="Preview how this custom field will look like on your site." +; ACF_ACF_OPTIONS="ACF Options" +; ACF_CUSTOM_CSS="Custom CSS" +; ACF_CUSTOM_CSS_DESC="Apply custom CSS on your site when this custom field is displayed." +; ACF_FIELD_LAYOUT_OVERRIDES="Select which layout to use when this custom field is displayed on your site." +; ACF_OVERRIDE_WIDGET_BASED_FIELD_LAYOUT_DESC="To create an override for %s, copy the %s layout file to %s.

You can read more here: How to create an alternative layout for Advanced Custom Fields" +; ACF_WIDGET_LAYOUT="Widget Layout" +; ACF_USER_DISPLAY_CONDITIONS_WRONG_PLUGIN_ORDER="To ensure that the Display Conditions function correctly on the User Profile page, the 'System - Fields' plugin should be positioned before the 'System - Advanced Custom Fields (ACF)' plugin. By adjusting the plugin ordering as described, you'll resolve this warning." +; ACF_OVERRIDE_WIDGET_LAYOUT_PLEASE_SAVE_FIRST="Please save the custom field first in order to select a layout." +; ACF_INVALID_EMAIL="Invalid email address" diff --git a/plugins/system/acf/language/uk-UA/uk-UA.plg_system_acf.ini b/plugins/system/acf/language/uk-UA/uk-UA.plg_system_acf.ini new file mode 100644 index 00000000..c366d548 --- /dev/null +++ b/plugins/system/acf/language/uk-UA/uk-UA.plg_system_acf.ini @@ -0,0 +1,37 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos - http://www.tassos.gr/joomla-extensions +; @copyright Copyright (c) 2019 Tassos Marinos. All rights reserved. +; @license http://www.tassos.gr + +ACF="Розширені спеціальні поля" +PLG_SYSTEM_ACF="Система - розширені спеціальні поля (ACF)" +PLG_SYSTEM_ACF_DESC="Використовуйте плагін Advanced Custom Fields для повного контролю ваших даних спеціального поля Joomla." +ACF_RETURN_VALUE="Повернути значення" +ACF_RETURN_VALUE_DESC="Вкажіть повернене значення на передній частині" +ACF_ASSIGNMENTS="Видавничі завдання" +ACF_ASSIGNMENTS_DESC="Якщо це ввімкнено, на сторінці редагування спеціальних полів з'явиться нова вкладка"_QQ_" Публікаційні призначення "_QQ_". За допомогою завдань публікації ви можете відфільтрувати поля, відображені на передній панелі, за пунктами меню, датою, пристроєм, URL-адресами, доменним посиланням , Групи користувачів, країна та багато іншого "_QQ_"" +ACF_MISSING_FIELD="Не вистачає спеціального поля?" +ACF_MISSING_FIELD_DESC="Будь ласка, дайте мені знати, і я буду рада включити його до колекції Advanced Custom Fields." +ACF_FIELDS_COLLECTION="Колекція полів" +ACF_METADATA="Метадані" +ACF_CONTROLS="Управління" +ACF_CONTROLS_DESC="Вказує, що слід відображати елементи керування програвачами (наприклад, кнопка відтворення / паузи тощо)." +ACF_PRELOAD="Попередня завантаження" +ACF_PRELOAD_DESC="Вказує, якщо і як слід завантажувати відео при завантаженні сторінки" +ACF_MUTED="Відключено" +ACF_MUTED_DESC="Вказує, що аудіо вихід відео має бути відключений" +ACF_LOOP="Цикл" +ACF_LOOP_DESC="Вказує, що гравець почнеться знову, щоразу, коли його закінчувати" +ACF_AUTOPLAY="Автоматичне відтворення" +ACF_AUTOPLAY_DESC="Вказує, що медіа-файл почне відтворюватися, як тільки він буде готовий" +ACF_UNSUPPORTED_TAG="Ваш браузер не підтримує тег %s." +ACF_WIDTH_DESC="Встановлює ширину програвача" +ACF_HEIGHT_DESC="Встановлює висоту гравця" +ACF_ALLOWFULLSCREEN="Дозволити повноекранний" +ACF_ALLOWFULLSCREEN_DESC="Дозволити відтворення відео в повноекранному режимі." +ACF_FIXED="Виправлено" +ACF_RESPONSIVE="Чуйний" +ACF_VIDEO_SIZE="Розмір відео" +ACF_VIDEO_SIZE_DESC="Встановити розмір вбудованого відео.

Виправлено : відео буде вставлено у фіксованому розмірі.
Чуйний : Відео буде вставлено чуйно, тож воно адаптується до висоти та ширини контейнера "_QQ_"" diff --git a/plugins/system/acf/layouts/dashboard.php b/plugins/system/acf/layouts/dashboard.php new file mode 100644 index 00000000..1fb2f633 --- /dev/null +++ b/plugins/system/acf/layouts/dashboard.php @@ -0,0 +1,129 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Factory; +use Joomla\Registry\Registry; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Layout\FileLayout; + +if (!@include_once(JPATH_PLUGINS . '/system/nrframework/autoload.php')) +{ + throw new RuntimeException('Novarain Framework is not installed', 500); +} + +if (defined('nrJ4')) +{ + HTMLHelper::stylesheet('plg_system_nrframework/joomla4.css', ['relative' => true, 'version' => 'auto']); +} else +{ + HTMLHelper::_('behavior.modal'); +} + +Factory::getDocument()->addStyleDeclaration(' + .stars:hover { + text-decoration: none; + } + .icon-star { + color: #fcac0a; + width: ' . (defined('nrJ4') ? '14' : '7') . 'px; + } +'); + +/** + * Get list of all available fields + * + * @return array + */ +function getFieldsCollection() +{ + // Load XML file + $xmlfile = __DIR__ . '/fieldscollection.xml'; + + if (!is_file($xmlfile)) + { + return; + } + + if (!$xmlItems = simplexml_load_file($xmlfile)) + { + return; + } + + $fields = array(); + + foreach ($xmlItems as $key => $item) + { + $item = (array) $item; + $item = new Registry($item["@attributes"]); + + $extensionName = 'acf' . $item->get("name"); + $extensionID = NRFramework\Functions::getExtensionID($extensionName, 'fields'); + $backEndURL = "index.php?option=com_plugins&task=plugin.edit&extension_id=" . $extensionID; + + $url = $item->get("proonly", null) ? NRFramework\Functions::getUTMURL($item->get("url", "https://www.tassos.gr/joomla-extensions/advanced-custom-fields")) : Uri::base() . $backEndURL; + + $path = JPATH_PLUGINS . '/fields/acf' . $item->get("name"); + NRFramework\Functions::loadLanguage('plg_fields_acf' . $item->get("name"), $path); + + $obj = array( + "label" => isset($item['label']) ? $item['label'] : str_replace('ACF - ', '', Text::_('PLG_FIELDS_ACF' . strtoupper($item->get("name")) . '_LABEL')), + "description" => isset($item['description']) ? $item['description'] : Text::_('ACF_' . strtoupper($item->get("name")) . '_DESC'), + "backendurl" => Uri::base() . $backEndURL, + "extensionid" => $extensionID, + "proonly" => $item->get("proonly", null), + "comingsoon" => $item->get("comingsoon", false), + 'docurl' => 'https://www.tassos.gr/joomla-extensions/advanced-custom-fields/docs/' . $item->get('doc') + ); + + $fields[] = $obj; + } + + asort($fields); + + $layout = new FileLayout('fieldscollection', __DIR__); + return $layout->render($fields); +} + +?> + +
+
+

+

+ + + + +

+

+ + + + + + + +

+ + +
+ + + + +
+

+
\ No newline at end of file diff --git a/plugins/system/acf/layouts/fieldscollection.php b/plugins/system/acf/layouts/fieldscollection.php new file mode 100644 index 00000000..99c8c686 --- /dev/null +++ b/plugins/system/acf/layouts/fieldscollection.php @@ -0,0 +1,93 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('JPATH_PLATFORM') or die; + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; + +if (!defined('nrJ4')) +{ + HTMLHelper::_('behavior.modal'); +} + +$modalRel = 'rel="{handler: \'iframe\', size: {x: 1000, y: 630}}"'; +$product_url = 'https://www.tassos.gr/joomla-extensions/advanced-custom-fields'; + +?> + +
+ + + + + + + + $field) { ?> + + + + + + + + + +
+

+ +
+ +
+
+ +
+ + + +
+
+ + \ No newline at end of file diff --git a/plugins/system/acf/layouts/fieldscollection.xml b/plugins/system/acf/layouts/fieldscollection.xml new file mode 100644 index 00000000..0fcca2e7 --- /dev/null +++ b/plugins/system/acf/layouts/fieldscollection.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/system/acf/media/css/acf-backend.css b/plugins/system/acf/media/css/acf-backend.css new file mode 100644 index 00000000..ce708ba4 --- /dev/null +++ b/plugins/system/acf/media/css/acf-backend.css @@ -0,0 +1,3 @@ +body.acf-field-previewer-open{overflow:hidden}.acf-field-previewer{-webkit-box-sizing:border-box;box-sizing:border-box;position:fixed;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;bottom:46px;right:16px;min-width:500px;border:1px solid #ccc;border-radius:8px;padding:16px;-webkit-box-shadow:0 0 12px 0 rgba(0,0,0,0.1);box-shadow:0 0 12px 0 rgba(0,0,0,0.1);background:#fff;z-index:9999}.acf-field-previewer.fullscreen{width:100% !important;height:100% !important;top:0 !important;left:0 !important;right:auto;bottom:auto;z-index:99999;border-radius:0;padding:0;background:#f5f5f5}.acf-field-previewer.fullscreen .resizers{display:none}.acf-field-previewer.fullscreen .title{padding:8px;background:#fff;-webkit-box-shadow:0px 0px 2px 3px rgba(0,0,0,0.05);box-shadow:0px 0px 2px 3px rgba(0,0,0,0.05);z-index:1}.acf-field-previewer.fullscreen .title .fullscreen-actions svg:nth-child(1){display:none}.acf-field-previewer.fullscreen .title .acf-responsive-controls{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;gap:8px}.acf-field-previewer.fullscreen .title>*{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.acf-field-previewer.fullscreen .title>.left .acf-field-previewer-loading-indicator{margin-right:auto}.acf-field-previewer.fullscreen .title>.actions .fullscreen-actions{margin-left:auto}.acf-field-previewer.fullscreen .body{height:100% !important;padding:0;margin-top:0;overflow-y:scroll}.acf-field-previewer.fullscreen .body iframe{margin:0 auto;position:relative;top:auto;left:auto;border-width:8px;border-color:#fff;background:#fff;padding:16px}.acf-field-previewer.fullscreen[data-device="desktop"] .title .acf-field-preview-responsive-device[data-device="desktop"]{opacity:1}.acf-field-previewer.fullscreen[data-device="desktop"] .body{background:#fff}.acf-field-previewer.fullscreen[data-device="desktop"] .body iframe{height:100% !important}.acf-field-previewer.fullscreen[data-device="tablet"] .body iframe,.acf-field-previewer.fullscreen[data-device="mobile"] .body iframe{margin:28px auto;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.05);box-shadow:0 4px 10px rgba(0,0,0,0.05);border-radius:16px}.acf-field-previewer.fullscreen[data-device="tablet"] .title .acf-field-preview-responsive-device[data-device="tablet"]{opacity:1}.acf-field-previewer.fullscreen[data-device="tablet"] .body iframe{width:768px;height:1024px !important}.acf-field-previewer.fullscreen[data-device="mobile"] .title .acf-field-preview-responsive-device[data-device="mobile"]{opacity:1}.acf-field-previewer.fullscreen[data-device="mobile"] .body iframe{width:375px;height:667px !important}.acf-field-previewer:not(.fullscreen) .fullscreen-actions svg:nth-child(2){display:none}.acf-field-previewer .acf-field-previewer-loading-indicator{opacity:0;visibility:hidden;z-index:-1;font-size:10px;position:relative;text-indent:-9999em;border-top:2px solid #333;border-right:2px solid #333;border-bottom:2px solid #333;border-left:2px solid #ffffff;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.acf-field-previewer .acf-field-previewer-loading-indicator.is-visible{opacity:1;visibility:visible;z-index:5;-webkit-animation:acf-field-previewer-loading 1.1s infinite linear;animation:acf-field-previewer-loading 1.1s infinite linear}@-webkit-keyframes acf-field-previewer-loading{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes acf-field-previewer-loading{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.acf-field-previewer .acf-field-previewer-loading-indicator,.acf-field-previewer .acf-field-previewer-loading-indicator:after{border-radius:50%;width:12px;height:12px}.acf-field-previewer>.title{display:-webkit-box;display:-ms-flexbox;display:flex;font-weight:bold;text-transform:uppercase;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;gap:32px;font-size:13px}.acf-field-previewer>.title .info-icon,.acf-field-previewer>.title .left{gap:4px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.acf-field-previewer>.title .acf-responsive-controls{display:none}.acf-field-previewer>.title .acf-responsive-controls .acf-field-preview-responsive-device{cursor:pointer}.acf-field-previewer>.title .acf-responsive-controls .acf-field-preview-responsive-device:not(.active){opacity:.5}.acf-field-previewer>.title .acf-responsive-controls .acf-field-preview-responsive-device:hover{opacity:1}.acf-field-previewer>.title .actions{display:-webkit-box;display:-ms-flexbox;display:flex;gap:16px}.acf-field-previewer>.title .actions .fullscreen-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-right:1px solid #ccc;padding-right:16px}.acf-field-previewer>.title .actions svg,.acf-field-previewer>.title .actions a{color:#333;cursor:pointer}.acf-field-previewer>.title .actions svg:hover,.acf-field-previewer>.title .actions a:hover{color:#000}.acf-field-previewer>.title .actions a{display:inline-block;position:relative;width:16px;height:16px}.acf-field-previewer>.title .actions a:before,.acf-field-previewer>.title .actions a:after{position:absolute;content:' ';right:7px;height:17px;width:1px;background-color:#333}.acf-field-previewer>.title .actions a:before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.acf-field-previewer>.title .actions a:after{opacity:0;visibility:hidden;z-index:-1}.acf-field-previewer>.body{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;margin-top:8px;padding-top:16px;border-top:1px solid #ccc;width:100%;overflow:hidden;-webkit-box-sizing:content-box;box-sizing:content-box}.acf-field-previewer>.body iframe{width:100%;border:0;margin:0;padding:0;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.acf-field-previewer>.body iframe.refreshing{-webkit-transition:none;transition:none;opacity:0;visibility:hidden;z-index:-1}.acf-field-previewer.is-hidden{min-width:auto !important}.acf-field-previewer.is-hidden .resizers{display:none}.acf-field-previewer.is-hidden .body,.acf-field-previewer.is-hidden .actions .fullscreen-actions{display:none}.acf-field-previewer.is-hidden .actions a:after{opacity:1;visibility:visible;z-index:2}.acf-field-previewer.is-hidden-full{display:none !important}@media only screen and (max-width: 576px){.acf-field-previewer{min-width:auto;right:8px;left:8px;bottom:8px}}.spacer .acf label{font-weight:bold;font-size:13px;margin-top:20px;border-left:dotted 1px #a9a9a9;padding-left:8px;cursor:default;text-transform:uppercase;letter-spacing:1px}.spacer .acf-l-b label{font-weight:bold}#attrib-conditions{padding:30px}#attrib-conditions #fieldset-conditions{padding:0;margin:0;border:none}#attrib-conditions #fieldset-conditions>legend{display:none}.well.assign{margin:0;border-color:#fff;padding:15px}.acfupload-edit-modal-content{padding:16px}.acfupload-edit-modal-content .acfupload-edit-modal-editing-item{margin-bottom:16px;font-size:13px}.acfupload-edit-modal-content .acfupload-edit-modal-editing-item .acfupload-edit-modal-editing-item-name{font-style:italic} + + diff --git a/plugins/system/acf/media/css/joomla3.css b/plugins/system/acf/media/css/joomla3.css new file mode 100644 index 00000000..de71701e --- /dev/null +++ b/plugins/system/acf/media/css/joomla3.css @@ -0,0 +1,3 @@ +.acfupload,.cfup-file{font-size:14px}#attrib-conditions{padding:0}.acf-address-field-location-details input[type="text"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:13px 8px;width:100%}#acfUploadItemEditModal input[type="text"]{width:100%;-webkit-box-sizing:border-box;box-sizing:border-box;height:auto}.acf-countdown-item-edit-wrapper .controls{margin:0 !important}.tf-phone-control{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);border-radius:3px;padding:4px 6px} + + diff --git a/plugins/system/acf/media/css/joomla4.css b/plugins/system/acf/media/css/joomla4.css new file mode 100644 index 00000000..fb4f015d --- /dev/null +++ b/plugins/system/acf/media/css/joomla4.css @@ -0,0 +1,3 @@ +.nrtoggle{margin:6px 0} + + diff --git a/plugins/system/acf/media/css/responsive_embed.css b/plugins/system/acf/media/css/responsive_embed.css new file mode 100644 index 00000000..386be52e --- /dev/null +++ b/plugins/system/acf/media/css/responsive_embed.css @@ -0,0 +1,3 @@ +.acf-responsive-embed{position:relative;padding-bottom:56.25%;height:0;overflow:hidden}.acf-responsive-embed iframe,.acf-responsive-embed object,.acf-responsive-embed embed{position:absolute;top:0;left:0;width:100%;height:100%} + + diff --git a/plugins/system/acf/media/data/previewer/.htaccess b/plugins/system/acf/media/data/previewer/.htaccess new file mode 100644 index 00000000..cb7f1227 --- /dev/null +++ b/plugins/system/acf/media/data/previewer/.htaccess @@ -0,0 +1,6 @@ + + # Block direct PHP access + + deny from all + + \ No newline at end of file diff --git a/plugins/system/acf/media/data/previewer/index.html b/plugins/system/acf/media/data/previewer/index.html new file mode 100644 index 00000000..3af63015 --- /dev/null +++ b/plugins/system/acf/media/data/previewer/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/system/acf/media/js/field_previewer.js b/plugins/system/acf/media/js/field_previewer.js new file mode 100644 index 00000000..4b5d1837 --- /dev/null +++ b/plugins/system/acf/media/js/field_previewer.js @@ -0,0 +1,2 @@ +var ACF_Field_Previewer=function(){function e(e){this.field=e,this.field_name="acf"+e.replace(/\s+/g,"").toLowerCase(),this.wrapper=".acf-field-previewer",this.ACFFieldsPreviewerData=window.ACFFieldsPreviewerData||{},this.JoomlaOptions=null,this.root_url=this.getOption("root_url"),this.base_url=this.root_url.replace("/administrator",""),this.app_ajax_url="?option=com_ajax&format=raw&plugin=acf&task=FieldsPreviewer",this.preview_ajax_url="?option=com_ajax&format=raw&plugin=acf&task=FieldsPreviewerHTML",this.run()}var t=e.prototype;return t.run=function(){if(window.MutationObserver){var e=document.querySelector('select[id="jform_type"]');if(e)if(e.value!==this.field_name){var t=this,i=new MutationObserver(function(e){if(e)for(m in e)e[m].target.value.startsWith(t.field_name)&&(t.previewerInit(),i.disconnect())});i.observe(e,{childList:!0,subtree:!0,attributes:!0})}else this.previewerInit()}},t.previewerInit=function(){var e=this;this.addPreviewerToPage(),this.isPreviewerVisible()&&this.updatePreviewer(),setTimeout(function(){e.togglePreviewerByTab()},100),this.initEvents()},t.initEvents=function(){function t(e,t,i){r||(r=!0,setTimeout(function(){e(i),r=!1},t))}var r=!1;document.addEventListener("click",function(e){this.toggleFullscreen(e),this.toggleOpenClose(e),this.selectResponsiveControl(e),this.J3togglePreviewerOnTab(e),this.J4togglePreviewerOnTab(e)}.bind(this)),document.addEventListener("blur",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this)),jQuery(document).on("blur","input.minicolors",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this)),jQuery(document).on("change",".tfHasChosen",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this)),document.addEventListener("change",function(e){t(this.updatePreviewer.bind(this),150,e)}.bind(this))},t.J3togglePreviewerOnTab=function(e){var t=e.target.closest(".nav.nav-tabs");t&&t.parentElement.classList.contains("form-horizontal")&&e.target.closest("a")&&this.togglePreviewerByTab()},t.J4togglePreviewerOnTab=function(e){e.target.closest("joomla-tab")&&e.target.closest('button[type="button"]')&&this.togglePreviewerByTab()},t.togglePreviewerByTab=function(){this.isJ3GeneralTab()||this.isJ4GeneralTab()?this.showPreviewer():this.hidePreviewer(!0)},t.isJ3GeneralTab=function(){var e=document.querySelector("form > .form-horizontal > .nav.nav-tabs > li.active > a");if(e)return"#general"===e.getAttribute("href")},t.isJ4GeneralTab=function(){var e=document.querySelector('form joomla-tab button[aria-expanded="true"]');if(e)return"general"===e.getAttribute("aria-controls")},t.toggleFullscreen=function(e){var t=e.target.closest(".acf-field-previewer-fullscreen-toggle");if(t){document.body.classList.toggle("acf-field-previewer-open");var i=this.getPreviewer(t);i.classList.toggle("fullscreen"),i.classList.contains("fullscreen")||(i.querySelector(".body iframe").height=""),this.updatePreviewer(e)}},t.toggleOpenClose=function(e){var t=e.target.closest(".acf-field-previewer-show-hide-btn");if(t){e.preventDefault();var i=this.getPreviewer(t);document.body.classList.remove("acf-field-previewer-open"),i.classList.remove("fullscreen");var r=!i.classList.contains("is-hidden");i.querySelector(".body iframe").height="",localStorage.setItem("acf-field-previewer-hidden",r),r?this.hidePreviewer():this.showPreviewer()}},t.isPreviewerVisible=function(){return!this.getPreviewer().classList.contains("is-hidden")},t.showPreviewer=function(){var e=this.getPreviewer(),t=localStorage.getItem("acf-field-previewer-hidden");e.classList.contains("is-hidden")&&("true"!==t&&e.classList.remove("is-hidden"),e.classList.remove("is-hidden-full"),this.updatePreviewer())},t.hidePreviewer=function(e){void 0===e&&(e=!1);var t=this.getPreviewer();e&&t.classList.add("is-hidden-full"),t.classList.contains("is-hidden")||t.classList.add("is-hidden")},t.selectResponsiveControl=function(e){var t=e.target.closest(".acf-field-preview-responsive-device");t&&this.getPreviewer(t).setAttribute("data-device",t.dataset.device)},t.addPreviewerToPage=function(){var e=document.createElement("div"),t=this.wrapper.substring(1),i=localStorage.getItem("acf-field-previewer-hidden");(this.ACFFieldsPreviewerData.hidden&&!i||"true"===i)&&(t+=" is-hidden"),this.ACFFieldsPreviewerData.width&&(e.style.minWidth=this.ACFFieldsPreviewerData.width+"px"),this.ACFFieldsPreviewerData.height&&(e.style.minHeight=this.ACFFieldsPreviewerData.height+"px"),e.className=t,e.dataset.device="desktop";var r=document.createElement("div");r.className="title";var s=document.createElement("div");s.className="left";var n='
';if(s.innerHTML=n,s.innerHTML+=window.parent.Joomla.Text._("ACF_FIELD_PREVIEWER"),s.innerHTML+='
',r.appendChild(s),this.ACFFieldsPreviewerData.responsiveControls){var o=document.createElement("div");o.className="acf-responsive-controls",o.innerHTML+='',o.innerHTML+='',o.innerHTML+='',r.appendChild(o)}var a=document.createElement("div");a.className="actions",this.ACFFieldsPreviewerData.fullscreenActions&&(a.innerHTML='
'),a.innerHTML+='',r.appendChild(a),e.appendChild(r);var l=document.createElement("div");l.className="body";var c=document.createElement("iframe");c.src=this.getIFrameURL(),l.appendChild(c),e.appendChild(l),document.body.appendChild(e)},t.getIFrameURL=function(){return this.root_url+this.preview_ajax_url+"&field="+this.field_name+"&"+this.getJoomlaOption("csrf.token")+"=1"},t.updatePreviewer=function(e){if(!(e&&e.target&&e.target.name&&-1===e.target.name.indexOf("jform[fieldparams]"))){var t=this.getPreviewer();if(!t.classList.contains("is-hidden")){var i=new FormData(document.querySelector('form[name="adminForm"]'));(i=Object.fromEntries(i)).fullscreen=this.ACFFieldsPreviewerData.fullscreenActions&&t.classList.contains("fullscreen");var r=this,s=this.root_url+this.app_ajax_url+"&field="+this.field+"&"+this.getJoomlaOption("csrf.token")+"=1";this.showLoadingIndicator();var n=this.getIFrame();n.classList.add("refreshing"),n.height="",fetch(s,{method:"post",body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){e.error?alert(e.message):r.updatePreviewerContents(function(){r.hideLoadingIndicator()})}).catch(function(e){alert(e)})}}},t.showLoadingIndicator=function(){document.querySelector(".acf-field-previewer-loading-indicator").classList.add("is-visible")},t.hideLoadingIndicator=function(){document.querySelector(".acf-field-previewer-loading-indicator").classList.remove("is-visible")},t.updatePreviewerContents=function(e){void 0===e&&(e=null);var t=this.getIFrame();t.src=this.getIFrameURL(),t.height="",t.onload=function(){t.classList.remove("refreshing"),t.height=t.contentWindow.document.body.scrollHeight+"px","function"==typeof e&&e()}},t.getIFrame=function(){return document.querySelector(".acf-field-previewer iframe")},t.getJoomlaOption=function(e,t){if(!this.JoomlaOptions){var i=document.querySelector(".joomla-script-options");if(!i)return;this.JoomlaOptions=JSON.parse(i.text||i.textContent)}return void 0!==this.JoomlaOptions[e]?this.JoomlaOptions[e]:t},t.getOption=function(e,t){t=void 0===t?"":t;var i=this.getJoomlaOption("acf_js_object");return i&&void 0!==i[e]?i[e]:t},t.getPreviewer=function(e){return void 0===e&&(e=""),e?e.closest(this.wrapper):document.querySelector(this.wrapper)},e}(); + diff --git a/plugins/system/acf/media/js/helper.js b/plugins/system/acf/media/js/helper.js new file mode 100644 index 00000000..7deeff7c --- /dev/null +++ b/plugins/system/acf/media/js/helper.js @@ -0,0 +1,2 @@ +!function(window,document){"use strict";var helper={asyncScript:function(js_script){document.addEventListener("DOMContentLoaded",function(){window.addEventListener("load",function(){eval(js_script)})})},lazyloadIframes:function(){document.querySelectorAll("iframe.lazyload[data-src]").forEach(function(t){t.setAttribute("src",t.getAttribute("data-src")),t.removeAttribute("data-src")})}};window.ACFHelper=helper}(window,document); + diff --git a/plugins/system/acf/script.install.helper.php b/plugins/system/acf/script.install.helper.php new file mode 100644 index 00000000..313822f7 --- /dev/null +++ b/plugins/system/acf/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgSystemAcfInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/system/acf/script.install.php b/plugins/system/acf/script.install.php new file mode 100644 index 00000000..686e9e34 --- /dev/null +++ b/plugins/system/acf/script.install.php @@ -0,0 +1,33 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgSystemACFInstallerScript extends PlgSystemACFInstallerScriptHelper +{ + public $name = 'ACF'; + public $alias = 'acf'; + public $extension_type = 'plugin'; + + public function onAfterInstall() + { + if ($this->install_type == 'update') + { + require_once __DIR__ . '/helper/migrator.php'; + + $migrator = new ACFMigrator($this->installedVersion); + $migrator->do(); + } + } +} \ No newline at end of file diff --git a/plugins/system/acf/version.php b/plugins/system/acf/version.php new file mode 100644 index 00000000..ab387de6 --- /dev/null +++ b/plugins/system/acf/version.php @@ -0,0 +1,16 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2019 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die('Restricted Access'); +$NR_PRO = "1"; +$RELEASE_DATE = "2024-12-02"; +?> \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/AI/TextGeneration/ImageToText.php b/plugins/system/nrframework/NRFramework/AI/TextGeneration/ImageToText.php new file mode 100644 index 00000000..4ee4ec82 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/AI/TextGeneration/ImageToText.php @@ -0,0 +1,108 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\AI\TextGeneration; + +defined('_JEXEC') or die; + +use Joomla\CMS\Http\HttpFactory; + +class ImageToText +{ + /** + * Generate a caption for an image + * + * @param string $imageUrl + * + * @return array + */ + public function generate($imageUrl = '') + { + if (!$imageUrl) + { + return [ + 'error' => true, + 'message' => 'Image URL is required', + ]; + } + + $apiKey = \NRFramework\Helpers\Settings::getValue('openai_api_key'); + $apiEndpoint = 'https://api.openai.com/v1/chat/completions'; + + // Disable SSL verification for local files + $context = stream_context_create([ + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]); + + $imageData = base64_encode(file_get_contents($imageUrl, false, $context)); + $url = "data:image/jpeg;base64,{$imageData}"; + + $data = [ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'user', + 'content' => [ + [ + 'type' => 'text', + 'text' => 'Describe this image with maximum 120 characters, spaces included.' + ], + [ + 'type' => 'image_url', + 'image_url' => [ + 'url' => $url, + ], + ], + ], + ], + ], + 'max_tokens' => 50, + ]; + + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $apiKey, + ]; + + $error = false; + $message = ''; + + try { + $http = HttpFactory::getHttp(); + $response = $http->post($apiEndpoint, json_encode($data), $headers); + + if ($response->code == 200) + { + $result = json_decode($response->body); + $message = $result->choices[0]->message->content; + } + else + { + $error = true; + + $decodedResponse = json_decode($response->body, true); + + $message = isset($decodedResponse['error']['message']) ? $decodedResponse['error']['message'] : 'An error occurred while generating the caption.'; + } + } + catch (\Exception $e) + { + $error = true; + $message = "Error: " . $e->getMessage(); + } + + return [ + 'error' => $error, + 'message' => $message + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Assignments.php b/plugins/system/nrframework/NRFramework/Assignments.php new file mode 100644 index 00000000..e0a7ab7d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Assignments.php @@ -0,0 +1,316 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework; + +use NRFramework\Factory; +use NRFramework\Conditions\ConditionsHelper; + +defined('_JEXEC') or die; + +// @deprecated - Use \NRFramework\Condnitions\ConditionsHelper; +class Assignments +{ + /** + * Assignment Type Aliases + * + * @var array + * + * @deprecated To be removed on Jan 1st 2023. + */ + public $typeAliases = array( + 'device|devices' => 'Device', + 'urls|url' => 'URL', + 'os' => 'OS', + 'browsers|browser' => 'Browser', + 'referrer' => 'Referrer', + 'php' => 'PHP', + 'timeonsite' => 'TimeOnSite', + 'pageviews|user_pageviews' => 'Pageviews', + 'lang|language|languages' => 'Joomla\Language', + 'usergroups|usergroup|user_groups' => 'Joomla\UserGroup', + 'user_id|userid' => 'Joomla\UserID', + 'menu' => 'Joomla\Menu', + 'components|component' => 'Joomla\Component', + 'datetime|daterange|date' => 'Date\Date', + 'weekday|days|day' => 'Date\Day', + 'months|month' => 'Date\Month', + 'timerange|time' => 'Date\Time', + 'acymailing' => 'AcyMailing', + 'akeebasubs' => 'AkeebaSubs', + 'engagebox|onotherbox' => 'EngageBox', + 'convertforms' => 'ConvertForms', + 'geo_country|country|countries' => 'Geo\Country', + 'geo_continent|continent|continents' => 'Geo\Continent', + 'geo_city|city|cities' => 'Geo\City', + 'geo_region|region|regions' => 'Geo\Region', + 'cookiename|cookie' => 'Cookie', + 'ip_addresses|iprange|ip' => 'IP', + 'k2_items|k2item' => 'Component\K2Item', + 'k2_cats|k2category' => 'Component\K2Category', + 'k2_tags|k2tag' => 'Component\K2Tag', + 'k2_pagetypes|k2pagetype' => 'Component\K2Pagetype', + 'contentcats|category' => 'Component\ContentCategory', + 'contentarticles|article' => 'Component\ContentArticle', + 'contentview' => 'Component\ContentView', + 'eventbookingsingle' => 'Component\EventBookingSingle', + 'eventbookingcategory' => 'Component\EventBookingCategory', + 'j2storesingle' => 'Component\J2StoreSingle', + 'j2storecategory' => 'Component\J2StoreCategory', + 'hikashopsingle' => 'Component\HikashopSingle', + 'hikashopcategory' => 'Component\HikashopCategory', + 'sppagebuildersingle' => 'Component\SPPageBuilderSingle', + 'sppagebuildercategory' => 'Component\SPPageBuilderCategory', + 'virtuemartcategory' => 'Component\VirtueMartCategory', + 'virtuemartsingle' => 'Component\VirtueMartSingle', + 'jshoppingsingle' => 'Component\JShoppingSingle', + 'jshoppingcategory' => 'Component\JShoppingCategory', + 'rsblogsingle' => 'Component\RSBlogSingle', + 'rsblogcategory' => 'Component\RSBlogCategory', + 'rseventsprosingle' => 'Component\RSEventsProSingle', + 'rseventsprocategory' => 'Component\RSEventsProCategory', + 'easyblogcategory' => 'Component\EasyBlogCategory', + 'easyblogsingle' => 'Component\EasyBlogSingle', + 'zoosingle' => 'Component\ZooSingle', + 'zoocategory' => 'Component\ZooCategory', + 'eshopcategory' => 'Component\EshopCategory', + 'eshopsingle' => 'Component\EshopSingle', + 'jeventssingle' => 'Component\JEventsSingle', + 'jeventscategory' => 'Component\JEventsCategory', + 'djcatalog2category' => 'Component\DJCatalog2Category', + 'djcatalog2single' => 'Component\DJCatalog2Single', + 'quixsingle' => 'Component\QuixSingle', + 'djclassifiedssingle' => 'Component\DJClassifiedsSingle', + 'djclassifiedscategory' => 'Component\DJClassifiedsCategory', + 'sobiprocategory' => 'Component\SobiProCategory', + 'sobiprosingle' => 'Component\SobiProSingle', + 'gridboxcategory' => 'Component\GridboxCategory', + 'gridboxsingle' => 'Component\GridboxSingle', + 'djeventscategory' => 'Component\DJEventsCategory', + 'djeventssingle' => 'Component\DJEventsSingle', + 'jcalprocategory' => 'Component\JCalProCategory', + 'jcalprosingle' => 'Component\JCalProSingle', + 'dpcalendarcategory' => 'Component\DPCalendarCategory', + 'dpcalendarsingle' => 'Component\DPCalendarSingle', + 'icagendacategory' => 'Component\ICagendaCategory', + 'icagendasingle' => 'Component\ICagendaSingle', + 'jbusinessdirectorybusinesscategory' => 'Component\JBusinessDirectoryBusinessCategory', + 'jbusinessdirectorybusinesssingle' => 'Component\JBusinessDirectoryBusinessSingle', + 'jbusinessdirectoryeventcategory' => 'Component\JBusinessDirectoryEventCategory', + 'jbusinessdirectoryeventsingle' => 'Component\JBusinessDirectoryEventSingle', + 'jbusinessdirectoryoffercategory' => 'Component\JBusinessDirectoryOfferCategory', + 'jbusinessdirectoryoffersingle' => 'Component\JBusinessDirectoryOfferSingle', + 'jreviewscategory' => 'Component\JReviewsCategory', + 'jreviewssingle' => 'Component\JReviewsSingle' + ); + + /** + * Factory object + * + * @var \NRFramework\Factory + */ + protected $factory; + + /** + * Class constructor + */ + public function __construct($factory = null) + { + $this->factory = is_null($factory) ? new Factory() : $factory; + } + + /** + * Legacy method to check a set of rules. + * + * At the moment of writing this and the moment we're going to release a new version for EngageBox + * which is going to introduce the new ConditionBuilder field, ACF and GSD will still be using the passAll() method. + * + * This forces us to keep this method for backwards compatibiliy reasons. + * Additionally, it helps us to catch a special case where both ACF and GSD expect to pass all rules even if the array passed is null. + * + * @param array|object $assignments_info Array/Object containing assignment info + * @param string $match_method The matching method (and|or) - Deprecated + * @param bool $debug Set to true to request additional debug information about assignments + * + * @deprecated Use passSets() instead. To be removed on Jan 1st 2023 + */ + public function passAll($assignments_info, $match_method = 'and') + { + $assignments = $this->prepareAssignments($assignments_info, $match_method); + + $ch = new ConditionsHelper($this->factory); + $pass = $ch->passSets($assignments); + + // If the checks return null, consider this as Success. This is required for both ACF and GSD. + return is_null($pass) ? true : $pass; + } + + /** + * Returns the classname for a given assignment alias + * + * @param string $alias + * @return string|void + * + * @deprecated To be removed on Jan 1st 2023 + */ + public function aliasToClassname($alias) + { + $alias = strtolower($alias); + foreach ($this->typeAliases as $aliases => $type) + { + if (strtolower($type) == $alias) + { + return $type; + } + + $aliases = explode('|', strtolower($aliases)); + if (in_array($alias, $aliases)) + { + return $type; + } + } + + return null; + } + + /** + * Checks and prepares the given array of assignment information + * + * @param array $assignments_info + * @return array + * + * @deprecated To be removed on Jan 1st 2023 + */ + protected function prepareAssignments($data, $matching_method = 'all') + { + if (is_object($data)) + { + return $this->prepareAssignmentsFromObject($data, $matching_method); + } + + if (!is_array($data) OR empty($data)) + { + return; + } + + $rules = array_pop($data); + + if (!is_array($rules) OR empty($rules)) + { + return; + } + + foreach ($rules as &$rule) + { + if (is_array($rule)) + { + foreach ($rule as &$_rule) + { + $_rule = $this->prepareAssignmentRule($_rule); + } + } + else + { + $rule = $this->prepareAssignmentRule($rule); + } + } + + $data = [ + [ + 'matching_method' => $matching_method == 'and' ? 'all' : 'any', + 'rules' => $rules + ] + ]; + + return $data; + } + + /** + * Prepares the assignment rule. + * + * @param object $rule + * + * @return object + */ + private function prepareAssignmentRule($rule) + { + return [ + 'name' => $this->aliasToClassname($rule->alias), + 'operator' => (int) $rule->assignment_state == 1 ? 'includes' : 'not_includes', + 'value' => isset($rule->value) ? $rule->value : null, + 'params' => isset($rule->params) ? $rule->params : null, + ]; + } + + /** + * Converts an object of assignment information to an array of groups + * Used by existing extensions + * + * @param object $assignments_info + * @param string $matching_method + * + * @deprecated To be removed on Jan 1st 2023 + */ + public function prepareAssignmentsFromObject($assignments_info, $matching_method) + { + if (!isset($assignments_info->params)) + { + return []; + } + + $params = json_decode($assignments_info->params); + + if (!is_object($params)) + { + return []; + } + + $assignments_info = []; + + foreach ($this->typeAliases as $aliases => $type) + { + $aliases = explode('|', $aliases); + + foreach ($aliases as $alias) + { + if (!isset($params->{'assign_' . $alias}) || !$params->{'assign_' . $alias}) + { + continue; + } + + // Discover assignment params + $assignment_params = new \stdClass(); + foreach ($params as $key => $value) + { + if (strpos($key, "assign_" . $alias . "_param") !== false) + { + $key = str_replace("assign_" . $alias . "_param_", "", $key); + $assignment_params->$key = $value; + } + } + + $assignments_info[] = [ + 'name' => $this->aliasToClassname($alias), + 'operator' => (int) $params->{'assign_' . $alias} == 1 ? 'includes' : 'not_includes', + 'value' => isset($params->{'assign_' . $alias . '_list'}) ? $params->{'assign_' . $alias . '_list'} : [], + 'params' => $assignment_params + ]; + } + } + + $data = [ + [ + 'matching_method' => $matching_method == 'and' ? 'all' : 'any', + 'rules' => $assignments_info + ] + ]; + + return $data; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Cache.php b/plugins/system/nrframework/NRFramework/Cache.php new file mode 100644 index 00000000..507303d3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Cache.php @@ -0,0 +1,115 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +/** + * This file is deprecated. Use CacheManager instead of Cache. + */ + +namespace NRFramework; + +defined('_JEXEC') or die; + +use \NRFramework\CacheManager; +use \Joomla\CMS\Factory; + +/** + * Caching mechanism + */ +class Cache +{ + /** + * Check if has alrady exists in memory + * + * @param string $hash The hash string + * + * @return boolean + */ + static public function has($hash) + { + $cache = CacheManager::getInstance(Factory::getCache('tassos', '')); + return $cache->has($hash); + } + + /** + * Returns hash value + * + * @param string $hash The hash string + * @param string $clone Why the hell we clone objects here? + * + * @return mixed False on error, Object on success + */ + static public function get($hash, $clone = true) + { + $cache = CacheManager::getInstance(Factory::getCache('tassos', '')); + return $cache->get($hash, $clone); + } + + /** + * Sets on memory the hash value + * + * @param string $hash The hash string + * @param mixed $data Can be string or object + * + * @return mixed + */ + static public function set($hash, $data) + { + $cache = CacheManager::getInstance(Factory::getCache('tassos', '')); + return $cache->set($hash, $data); + } + + /** + * Reads hash value from memory or file + * + * @param string $hash The hash string + * @param boolean $force If true, the filesystem will be used as well on the /cache/ folder + * + * @return mixed The hash object valuw + */ + static public function read($hash, $force = false) + { + $cache = CacheManager::getInstance(Factory::getCache('tassos', '')); + return $cache->read($hash, $force); + } + + /** + * Writes hash value in cache folder + * + * @param string $hash The hash string + * @param mixed $data Can be string or object + * @param integer $ttl Expiration duration in milliseconds + * + * @return mixed The hash object value + */ + static public function write($hash, $data, $ttl = 0) + { + $cache = CacheManager::getInstance(Factory::getCache('tassos', '')); + return $cache->write($hash, $data, $ttl); + } + + /** + * Memoize a function to run once per runtime + * + * @param string $key The key to store the result of the callback + * @param callback $callback The callable anonymous function to call + * + * @return mixed + */ + static public function memo($key, callable $callback) + { + $hash = md5($key); + + if (Cache::has($hash)) + { + return Cache::get($hash); + } + + return Cache::set($hash, $callback()); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/CacheManager.php b/plugins/system/nrframework/NRFramework/CacheManager.php new file mode 100644 index 00000000..dc64eec9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/CacheManager.php @@ -0,0 +1,143 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +defined('_JEXEC') or die; + +/** + * Cache Manager + * + * Singleton + */ +class CacheManager +{ + /** + * 'static' cache array + * @var array + */ + protected $cache = []; + + /** + * Cache mechanism object + * @var object + */ + protected $cache_mechanism = null; + + /** + * Construct + */ + protected function __construct($cache_mechanism) + { + $this->cache_mechanism = $cache_mechanism; + } + + static public function getInstance($cache_mechanism) + { + static $instance = null; + + if ($instance === null) + { + $instance = new CacheManager($cache_mechanism); + } + + return $instance; + } + + /** + * Check if a hash already exists in memory + * + * @param string $hash The hash string + * + * @return boolean + */ + public function has($hash) + { + return isset($this->cache[$hash]); + } + + /** + * Returns a hash's value + * + * @param string $hash The hash string + * @param string $clone Why the hell we clone objects here? + * + * @return mixed False on error, Object on success + */ + public function get($hash, $clone = true) + { + if (!$this->has($hash)) + { + return false; + } + + return is_object($this->cache[$hash]) && $clone ? clone $this->cache[$hash] : $this->cache[$hash]; + } + + /** + * Sets a hash value + * + * @param string $hash The hash string + * @param mixed $data Can be string or object + * + * @return mixed + */ + public function set($hash, $data) + { + $this->cache[$hash] = $data; + return $data; + } + + /** + * Reads a hash value from memory or file + * + * @param string $hash The hash string + * @param boolean $force If true, the filesystem will be used as well on the /cache/ folder + * + * @return mixed The hash object value + */ + public function read($hash, $force = false) + { + if ($this->has($hash)) + { + return $this->get($hash); + } + + if ($force) + { + $this->cache_mechanism->setCaching(true); + } + + return $this->cache_mechanism->get($hash); + } + + /** + * Writes hash value in cache folder + * + * @param string $hash The hash string + * @param mixed $data Can be string or object + * @param integer $ttl Expiration duration in minutes. Default 1440 minutes = 1 day. + * + * @return mixed The hash object value + */ + public function write($hash, $data, $ttl = 1440) + { + if ($ttl > 0) + { + $this->cache_mechanism->setLifeTime($ttl); + } + + $this->cache_mechanism->setCaching(true); + $this->cache_mechanism->store($data, $hash); + + $this->set($hash, $data); + + return $data; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Condition.php b/plugins/system/nrframework/NRFramework/Conditions/Condition.php new file mode 100644 index 00000000..703e6bec --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Condition.php @@ -0,0 +1,455 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Conditions; + +defined('_JEXEC') or die; + +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\CMS\Language\Text; + +/** + * Assignment Class + */ +class Condition +{ + /** + * Application Object + * + * @var object + */ + protected $app; + + /** + * Document Object + * + * @var object + */ + protected $doc; + + /** + * Date Object + * + * @var object + */ + protected $date; + + /** + * Database Object + * + * @var object + */ + protected $db; + + /** + * User Object + * + * @var object + */ + protected $user; + + /** + * Assignment Selection + * + * @var mixed + */ + protected $selection; + + /** + * Assignment Parameters + * + * @var mixed + */ + protected $params; + + /** + * Assignment State (Include|Exclude) + * + * @var string + */ + public $assignment; + + /** + * Options + * + * @var object + */ + public $options; + + /** + * Framework factory object + * + * @var object + */ + public $factory; + + /** + * The default operator that will be used to compare haystack with needle. + * + * @var string + */ + protected $operator; + + /** + * Class constructor + * + * @param array $options The rule options. Expected properties: selection, value, params + * @param object $factory The framework's factory class. + */ + public function __construct($options = null, $factory = null) + { + $this->factory = is_null($factory) ? new \NRFramework\Factory() : $factory; + + // Set General Joomla Objects + $this->db = $this->factory->getDbo(); + $this->app = $this->factory->getApplication(); + $this->doc = $this->factory->getDocument(); + $this->user = $this->factory->getUser(); + + $this->options = new Registry($options); + + $this->setParams($this->options->get('params')); + $this->setOperator($this->options->get('operator', 'includesSome')); + + // For performance reasons we might move this inside the pass() method + $this->setSelection($this->options->get('selection', '')); + } + + /** + * Set the rule's user selected value + * + * @param mixed $selection + * @return object + */ + public function setSelection($selection) + { + $this->selection = $selection; + + if (method_exists($this, 'prepareSelection')) + { + $this->selection = $this->prepareSelection(); + } + + return $this; + } + + /** + * Undocumented function + * + * @return void + */ + public function getSelection() + { + return $this->selection; + } + + /** + * Set the operator that will be used for the comparison + * + * @param string $operator + * @return object + */ + public function setOperator($operator) + { + $this->operator = $operator; + return $this; + } + + /** + * Set the rule's parameters + * + * @param array $params + */ + public function setParams($params) + { + $this->params = new Registry($params); + } + + public function getParams() + { + return $this->params; + } + + /** + * Checks the validitity of two values based on the given operator. + * + * Consider converting this method as a Trait. + * + * @param mixed $value + * @param mixed $selection + * @param string $operator + * @param array $options ignoreCase: true,false + * + * @return bool + */ + public function passByOperator($value = null, $selection = null, $operator = null, $options = null) + { + $value = is_null($value) ? $this->value() : $value; + + if (!is_null($selection)) + { + $this->setSelection($selection); + } + + $selection = $this->getSelection(); + + $options = new Registry($options); + $ignoreCase = $options->get('ignoreCase', true); + + if (is_object($value)) + { + $value = (array) $value; + } + + if (is_object($selection)) + { + $selection = (array) $selection; + } + + if ($ignoreCase) + { + if (is_string($value)) + { + $value = strtolower($value); + } + + if (is_string($selection)) + { + $selection = strtolower($selection); + } + + if (is_array($value)) + { + $value = array_map('strtolower', $value); + } + + if (is_array($selection)) + { + $selection = array_map(function($str) + { + return is_null($str) ? '' : strtolower($str); + }, $selection); + } + } + + $operator = (is_null($operator) OR empty($operator)) ? $this->operator : $operator; + $pass = false; + + switch ($operator) + { + case 'exists': + $pass = !is_null($value); + break; + + // Determines whether haystack is empty. Accepts: array, string + case 'empty': + if (is_array($value)) + { + $pass = empty($value); + } + + if (is_string($value)) + { + $pass = $value == '' || trim($value) == ''; + } + + if (is_bool($value)) + { + $pass = !$value; + } + break; + + case 'equals': + if (is_array($selection) || is_array($value)) + { + $pass = $this->passByOperator($value, $selection, 'includesSome', $options); + } + else + { + $pass = $value == $selection; + } + break; + + case 'contains': + if (is_string($value) && is_string($selection)) + { + $pass = strlen($selection) > 0 && strpos($value, $selection) !== false; + } + + break; + // Determine whether haystack is less than needle. + case 'less_than': + case 'lowerthan': + case 'lt': + $pass = $value < $selection; + break; + + // Determine whether haystack is less than or equal to needle. + case 'less_than_or_equal_to': + case 'lowerthanequal': + case 'lte': + $pass = $value <= $selection; + break; + + // Determine whether haystack is greater than needle. + case 'greater_than': + case 'greaterthan': + case 'gt': + $pass = $value > $selection; + break; + + // Determine whether haystack is greater than or equal to needle. + case 'greater_than_or_equal_to': + case 'greterthanequal': + case 'gte': + $pass = $value >= $selection; + break; + + // Determine whether haystack contains all elements in needle. + case 'includesAll': + case 'containsall': + $pass = count(array_intersect((array) $selection, (array) $value)) == count((array) $selection); + break; + + // Determine whether haystack contains at least one element from needle. + case 'includesSome': + case 'containsany': + $pass = !empty(array_intersect((array) $value, (array) $selection)); + break; + + // Determine whether haystack contains at least one element from needle. Accepts; string, array. + case 'includes': + if (is_string($value) && $value != '' && is_string($selection) && $selection != '') + { + if (StringHelper::strpos($value, $selection) !== false) + { + $pass = true; + } + } + + if (is_array($value) || is_array($selection)) + { + $pass = $this->passByOperator($value, $selection, 'includesSome', $options); + } + + break; + + // Determine whether haystack starts with needle. Accepts: string + case 'starts_with': + $pass = StringHelper::substr($value, 0, StringHelper::strlen($selection)) === $selection; + break; + + // Determine whether haystack ends with needle. Accepts: string + case 'ends_with': + $pass = StringHelper::substr($value, -StringHelper::strlen($selection)) === $selection; + break; + + // Determine whether value is in given range + case 'range': + $value1 = isset($selection['value1']) ? (float) $selection['value1'] : false; + $value2 = isset($selection['value2']) ? (float) $selection['value2'] : false; + + $pass = $value1 && $value2 ? (($value >= $value1) && ($value <= $value2)) : false; + break; + + // Determine whether haystack equals to needle. Accepts any object. + default: + $pass = $value == $selection; + } + + return $pass; + } + + /** + * Base assignment check + * + * @return bool + */ + public function pass() + { + return $this->passByOperator(); + } + + /** + * Returns all parent rows + * + * This method doesn't belong here. Move it to Functions.php. + * + * @param integer $id Row primary key + * @param string $table Table name + * @param string $parent Parent column name + * @param string $child Child column name + * + * @return array Array with IDs + */ + public function getParentIds($id = 0, $table = 'menu', $parent = 'parent_id', $child = 'id') + { + if (!$id) + { + return []; + } + + $cache = $this->factory->getCache(); + $hash = md5('getParentIds_' . $id . '_' . $table . '_' . $parent . '_' . $child); + + if ($cache->has($hash)) + { + return $cache->get($hash); + } + + $parent_ids = array(); + + while ($id) + { + $query = $this->db->getQuery(true) + ->select('t.' . $parent) + ->from('#__' . $table . ' as t') + ->where('t.' . $child . ' = ' . (int) $id); + $this->db->setQuery($query); + $id = $this->db->loadResult(); + + // Break if no parent is found or parent already found before for some reason + if (!$id || in_array($id, $parent_ids)) + { + break; + } + + $parent_ids[] = $id; + } + + return $cache->set($hash, $parent_ids); + } + + /** + * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. + * + * @return string + */ + public function getValueHint() + { + $value = $this->value(); + + // If the rule returns an array, use the 1st one. + $value = is_array($value) ? $value[0] : $value; + + return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_' . strtoupper($this->getName()), ucfirst(strtolower($value))); + } + + /** + * Return the rule name + * + * @return string + */ + protected function getName() + { + $classParts = explode('\\', get_called_class()); + return array_pop($classParts); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/ConditionBuilder.php b/plugins/system/nrframework/NRFramework/Conditions/ConditionBuilder.php new file mode 100644 index 00000000..20dc428e --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/ConditionBuilder.php @@ -0,0 +1,393 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions; + +defined('_JEXEC') or die; + +use Joomla\CMS\Layout\LayoutHelper; +use NRFramework\Conditions\ConditionsHelper; +use NRFramework\Extension; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Form\Form; + +class ConditionBuilder +{ + public static function pass($rules) + { + $rules = self::prepareRules($rules); + + if (empty($rules)) + { + return true; + } + + return ConditionsHelper::getInstance()->passSets($rules); + } + + /** + * Prepare rules object to run checks + * + * @return void + */ + public static function prepareRules($rules = []) + { + if (!is_array($rules)) + { + return []; + } + + $rules_ = []; + + foreach ($rules as $key => $group) + { + if (isset($group['enabled']) AND !(bool) $group['enabled']) + { + continue; + } + + // A group without rules, doesn't make sense. + if (!isset($group['rules']) OR (isset($group['rules']) AND empty($group['rules']))) + { + continue; + } + + $validRules = []; + + foreach ($group['rules'] as $rule) + { + // Make sure rule has a name. + if (!isset($rule['name']) OR (isset($rule['name']) AND empty($rule['name']))) + { + continue; + } + + // Rule is invalid if both value and params properties are empty + if (!isset($rule['value']) && !isset($rule['params'])) + { + continue; + } + + // Skip disabled rules + if (isset($rule['enabled']) && !(bool) $rule['enabled']) + { + continue; + } + + // We don't need this property. + unset($rule['enabled']); + + // Prepare rule value if necessary + if (isset($rule['value'])) + { + $rule['value'] = self::prepareTFRepeaterValue($rule['value']); + } + + // Verify operator + if (!isset($rule['operator']) OR (isset($rule['operator']) && empty($rule['operator']))) + { + $rule['operator'] = isset($rule['params']['operator']) ? $rule['params']['operator'] : ''; + } + + $validRules[] = $rule; + } + + if (count($validRules) > 0) + { + $group['rules'] = $validRules; + + if (!isset($group['matching_method']) OR (isset($group['matching_method']) AND empty($group['matching_method']))) + { + $group['matching_method'] = 'all'; + } + + unset($group['enabled']); + $rules_[] = $group; + } + } + + return $rules_; + } + + /** + * Parse the value of the TF Repeater Input field. + * + * @param array $selection + * + * @return mixed + */ + public static function prepareTFRepeaterValue($selection) + { + // Only proceed when we have an array of arrays selection. + if (!is_array($selection)) + { + return $selection; + } + + $first = array_values($selection)[0]; + + if (!is_array($first)) + { + return $selection; + } + + if (!isset($first['value'])) + { + return $selection; + } + + $new_selection = []; + + foreach ($selection as $value) + { + /** + * We expect a `value` key for TFInputRepeater fields or a key,value pair + * for plain arrays. + */ + if (!isset($value['value'])) + { + /** + * If no value exists, it means that the passed $assignment->selection is a key,value pair array so we use the value + * as our returned selection. + * + * This happens when we pass a key,value pair array as $assignment->selection when we expect a TFInputRepeater value + * so we need to take this into consideration. + */ + $new_selection[] = $value; + continue; + } + + // value must not be empty + if (is_scalar($value['value']) && empty(trim($value['value']))) + { + continue; + } + + $new_selection[] = count($value) === 1 ? $value['value'] : $value; + } + + return $new_selection; + } + + /** + * Returns the TGeoIP plugin modal. + * + * @return string + */ + public static function getGeoModal() + { + // Do not proceed if the database is up-to-date + if (!\NRFramework\Extension::geoPluginNeedsUpdate()) + { + return; + } + + HTMLHelper::_('bootstrap.modal'); + + $modalName = 'tf-geodbchecker-modal'; + + // The TGeoIP Plugin URL + $url = Uri::base(true) . '/index.php?option=com_plugins&view=plugin&tmpl=component&layout=modal&extension_id=' . \NRFramework\Functions::getExtensionID('tgeoip', 'system'); + + $options = [ + 'title' => Text::_('NR_EDIT'), + 'url' => $url, + 'height' => '400px', + 'backdrop' => 'static', + 'bodyHeight' => '70', + 'modalWidth' => '70', + 'footer' => ' + ', + ]; + + return HTMLHelper::_('bootstrap.renderModal', $modalName, $options); + } + + /** + * Prepares the given rules list. + * + * @param array $list + * + * @return array + */ + public static function prepareXmlRulesList($list) + { + if (is_array($list)) + { + $list = implode(',', array_map('trim', $list)); + } + else if (is_string($list)) + { + $list = str_replace(' ', '', $list); + } + + return $list; + } + + /** + * Adds a new condition item or group. + * + * @param string $controlGroup The name of the input used to store the data. + * @param string $groupKey The group index ID. + * @param string $conditionKey The added condition item index ID. + * @param array $condition The condition name we are adding. + * @param string $include_rules The list of included conditions that override the available conditions. + * @param string $exclude_rules The list of excluded conditions that override the available conditions. + * @param bool $exclude_rules_pro Whether the excluded rules should appear as Pro missing features. + * + * @return string + */ + public static function add($controlGroup, $groupKey, $conditionKey, $condition = null, $include_rules = [], $exclude_rules = [], $exclude_rules_pro = false) + { + $controlGroup_ = $controlGroup . "[$groupKey][rules][$conditionKey]"; // @Todo - rename input namespace to 'conditions' + $form = self::getForm('conditionbuilder/base.xml', $controlGroup_, $condition); + $form->setFieldAttribute('name', 'include_rules', is_array($include_rules) ? implode(',', $include_rules) : $include_rules); + $form->setFieldAttribute('name', 'exclude_rules', is_array($exclude_rules) ? implode(',', $exclude_rules) : $exclude_rules); + $form->setFieldAttribute('name', 'exclude_rules_pro', $exclude_rules_pro); + + $options = [ + 'name' => $controlGroup_, + 'enabled' => !isset($condition['enabled']) ? true : (string) $condition['enabled'] == '1', + 'toolbar' => $form, + 'groupKey' => $groupKey, + 'conditionKey' => $conditionKey, + 'options' => '' + ]; + + if (isset($condition['name'])) + { + $optionsHTML = self::renderOptions($condition['name'], $controlGroup_, $condition); + $options['condition_name'] = $condition['name']; + $options['options'] = $optionsHTML; + } + + return self::getLayout('conditionbuilder_row', $options); + } + + /** + * Render condition item settings. + * + * @param string $name The name of the condition item. + * @param string $controlGroup The name of the input used to store the data. + * @param object $formData The data that will be bound to the form. + * + * @return string + */ + public static function renderOptions($name, $controlGroup = null, $formData = null) + { + if (!$form = self::getForm('conditions/' . strtolower(str_replace('\\', '/', $name)) . '.xml', $controlGroup, $formData)) + { + return; + } + + $form->setFieldAttribute('note', 'ruleName', $name); + + return $form->renderFieldset('general'); + } + + /** + * Handles loading condition builder given a payload. + * + * @param array $payload + * + * @return string + */ + public static function initLoad($payload = []) + { + if (!$payload) + { + return; + } + + if (!isset($payload['data']) && + !isset($payload['name'])) + { + return; + } + + if (!$data = json_decode($payload['data'])) + { + return; + } + + // transform object to assosiative array + $data = json_decode(json_encode($data), true); + + // html of condition builder + $html = ''; + + $include_rules = isset($payload['include_rules']) ? $payload['include_rules'] : []; + $exclude_rules = isset($payload['exclude_rules']) ? $payload['exclude_rules'] : []; + $exclude_rules_pro = isset($payload['exclude_rules_pro']) ? $payload['exclude_rules_pro'] : false; + + foreach ($data as $groupKey => $groupConditions) + { + $payload = [ + 'name' => $payload['name'], + 'groupKey' => $groupKey, + 'groupConditions' => $groupConditions, + 'include_rules' => $include_rules, + 'exclude_rules' => $exclude_rules, + 'exclude_rules_pro' => $exclude_rules_pro + ]; + + $html .= self::getLayout('conditionbuilder_group', $payload); + + } + + return $html; + } + + /** + * Render a layout given its name and payload. + * + * @param string $name + * @param array $payload + * + * @return string + */ + public static function getLayout($name, $payload) + { + return LayoutHelper::render($name, $payload, JPATH_PLUGINS . '/system/nrframework/layouts'); + } + + /** + * Returns the form by binding given data. + * + * @param string $name + * @param string $controlGroup + * @param array $data + * + * @return object + */ + private static function getForm($name, $controlGroup, $data = null) + { + if (!file_exists(JPATH_PLUGINS . '/system/nrframework/xml/' . $name)) + { + return; + } + + $form = new Form('cb', ['control' => $controlGroup]); + + $form->addFieldPath(JPATH_PLUGINS . '/system/nrframework/fields'); + $form->loadFile(JPATH_PLUGINS . '/system/nrframework/xml/' . $name); + + if (!is_null($data)) + { + $form->bind($data); + } + + return $form; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/AcyMailing.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/AcyMailing.php new file mode 100644 index 00000000..77dfba7f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/AcyMailing.php @@ -0,0 +1,97 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class AcyMailing extends Condition +{ + /** + * Returns the assignment's value + * + * @return array AcyMailing lists + */ + public function value() + { + return $this->getSubscribedLists(); + } + + /** + * Returns all AcyMailing lists the user is subscribed to + * + * @return array AcyMailing lists + */ + private function getSubscribedLists() + { + if (!$user = $this->user->id) + { + return false; + } + + // Get a db connection. + $db = $this->db; + + // Create a new query object. + $query = $db->getQuery(true); + + $lists = []; + + // Read AcyMailing v5 lists + if (\NRFramework\Extension::isInstalled('com_acymailing')) + { + $query + ->select(array('list.listid')) + ->from($db->quoteName('#__acymailing_listsub', 'list')) + ->join('INNER', $db->quoteName('#__acymailing_subscriber', 'sub') . ' ON (' . $db->quoteName('list.subid') . '=' . $db->quoteName('sub.subid') . ')') + ->where($db->quoteName('list.status') . ' = 1') + ->where($db->quoteName('sub.userid') . ' = ' . $user) + ->where($db->quoteName('sub.confirmed') . ' = 1') + ->where($db->quoteName('sub.enabled') . ' = 1'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + if ($cols = $db->loadColumn()) + { + $lists = array_merge($lists, $cols); + } + } + + // Read AcyMailing > v5 lists + if (\NRFramework\Extension::isInstalled('com_acym')) + { + // Create a new query object. + $query = $db->getQuery(true); + + $query + ->select(['list.id']) + ->from($db->quoteName('#__acym_user_has_list', 'userlist')) + ->join('INNER', $db->quoteName('#__acym_list', 'list') . ' ON (' . $db->quoteName('list.id') . '=' . $db->quoteName('userlist.list_id') . ')') + ->join('INNER', $db->quoteName('#__acym_user', 'user') . ' ON (' . $db->quoteName('user.id') . '=' . $db->quoteName('userlist.user_id') . ')') + ->where($db->quoteName('user.cms_id') . ' = ' . $user) + ->where($db->quoteName('userlist.status') . ' = 1') + ->where($db->quoteName('userlist.unsubscribe_date') . ' IS NULL'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $cols = $db->loadColumn(); + + foreach ($cols as $value) + { + $lists[] = '6:' . $value; + } + } + + return $lists; + } +} diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/AkeebaSubs.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/AkeebaSubs.php new file mode 100644 index 00000000..53195c74 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/AkeebaSubs.php @@ -0,0 +1,71 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class AkeebaSubs extends Condition +{ + /** + * Returns the assignment's value + * + * @return array Akeeba Subscriptions + */ + public function value() + { + return $this->getlevels(); + } + + /** + * Returns all user's active subscriptions + * + * @param int $userid User's id + * + * @return array Akeeba Subscriptions + */ + private function getLevels() + { + if (!$user = $this->user->id) + { + return false; + } + + if (!defined('FOF30_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof30/include.php')) + { + return false; + } + + // Get the Akeeba Subscriptions container. Also includes the autoloader. + $container = \FOF30\Container\Container::getInstance('com_akeebasubs'); + + $subscriptionsModel = $container->factory->model('Subscriptions')->tmpInstance(); + + $items = $subscriptionsModel + ->user_id($user) + ->enabled(1) + ->get(); + + if (!$items->count()) + { + return false; + } + + $levels = array(); + + foreach ($items as $subscription) + { + $levels[] = $subscription->akeebasubs_level_id; + } + + return array_unique($levels); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Browser.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Browser.php new file mode 100644 index 00000000..05387e9a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Browser.php @@ -0,0 +1,27 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class Browser extends Condition +{ + /** + * Returns the assignment's value + * + * @return string Browser name + */ + public function value() + { + return $this->factory->getBrowser()['name']; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ComponentBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ComponentBase.php new file mode 100644 index 00000000..c0e6a84c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ComponentBase.php @@ -0,0 +1,249 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +use NRFramework\Functions; + +/** + * Base class used by component-based assignments. Class properties defaults to com_content. + */ +abstract class ComponentBase extends Condition +{ + /** + * The component's Category Page view name + * + * @var string + */ + protected $viewCategory = 'category'; + + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'article'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_content'; + + /** + * Request information + * + * @var mixed + */ + protected $request = null; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options = null, $factory = null) + { + parent::__construct($options, $factory); + + $request = new \stdClass; + + $request->view = $this->app->input->get('view'); + $request->task = $this->app->input->get('task'); + $request->option = $this->app->input->get('option'); + $request->layout = $this->app->input->get('layout'); + $request->id = $this->app->input->getInt('id'); + + // Check if request is forwarded + if ($context = $this->app->input->get('forward_context')) + { + if (isset($context['request'])) + { + $request = (object) $context['request']; + } + } + + $this->request = $request; + } + + /** + * Returns the assignment's value + * + * @return array Category IDs + */ + public function value() + { + return $this->getCategoryIds(); + } + + /** + * Indicates whether the current view concerns a Category view + * + * @return boolean + */ + protected function isCategoryPage() + { + return ($this->request->view == $this->viewCategory); + } + + /** + * Indicates whether the current view concerncs a Single Page view + * + * @return boolean + */ + public function isSinglePage() + { + return ($this->request->view == $this->viewSingle); + } + + /** + * Check if we are in the right context and we're manipulating the correct component + * + * @return bool + */ + protected function passContext() + { + return ($this->request->option == $this->component_option); + } + + /** + * Returns category IDs based + * + * @return array + */ + protected function getCategoryIDs() + { + $id = $this->request->id; + + // Make sure we have an ID. + if (empty($id)) + { + return; + } + + // If this is a Category page, return the Category ID from the Query String + if ($this->isCategoryPage()) + { + return (array) $id; + } + + // If this is a Single Page, return all assosiated Category IDs. + if ($this->isSinglePage()) + { + return $this->getSinglePageCategories($id); + } + } + + /** + * Checks whether the current page is within the selected categories + * + * @param string $ref_table The referenced table + * @param string $ref_parent_column The name of the parent column in the referenced table + * + * @return boolean + */ + protected function passCategories($ref_table = 'categories', $ref_parent_column = 'parent_id') + { + if (empty($this->selection) || !$this->passContext()) + { + return false; + } + + // Include Children switch: 0 = No, 1 = Yes, 2 = Child Only + $inc_children = $this->params->get('inc_children'); + + // Setup supported views + $view_single = $this->params->get('view_single', true); + $view_category = $this->params->get('view_category', false); + + // Check if we are in a valid context + if (!($view_category && $this->isCategoryPage()) && !($view_single && $this->isSinglePage())) + { + return false; + } + + // Start Checks + $pass = false; + + // Get current page assosiated category IDs. It can be a single ID of the current Category view or multiple IDs assosiated to active item. + $catids = $this->getCategoryIDs(); + $catids = is_array($catids) ? $catids : (array) $catids; + + foreach ($catids as $catid) + { + $pass = in_array($catid, $this->selection); + + if ($pass) + { + // If inc_children is either disabled or set to 'Also on Childs', there's no need for further checks. + // The condition is already passed. + if (in_array($this->params->get('inc_children'), [0, 1])) + { + break; + } + + // We are here because we need childs only. Disable pass and continue checking parent IDs. + $pass = false; + } + + // Pass check for child items + if (!$pass && $this->params->get('inc_children')) + { + $parent_ids = $this->getParentIDs($catid, $ref_table, $ref_parent_column); + + foreach ($parent_ids as $id) + { + if (in_array($id, $this->selection)) + { + $pass = true; + break 2; + } + } + + unset($parent_ids); + } + } + + return $pass; + } + + /** + * Check whether this page passes the validation + * + * @return void + */ + protected function passSinglePage() + { + // Make sure we are in the right context + if (empty($this->selection) || !$this->passContext() || !$this->isSinglePage()) + { + return false; + } + + if (!is_array($this->selection)) + { + $this->selection = Functions::makeArray($this->selection); + } + + return parent::pass(); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * @return array + */ + abstract protected function getSinglePageCategories($id); +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentArticle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentArticle.php new file mode 100644 index 00000000..f43e5362 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentArticle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ContentArticle extends ContentBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['article']; + + /** + * Pass check for Joomla! Articles + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int Article ID + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentBase.php new file mode 100644 index 00000000..f605d73d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentBase.php @@ -0,0 +1,120 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; + +class ContentBase extends ComponentBase +{ + /** + * Get single page's assosiated categories + * + * @param integer The Single Page id + * + * @return integer + */ + protected function getSinglePageCategories($id) + { + // If the article is not assigned to any menu item, the cat id should be available in the query string. Let's check it. + if ($requestCatID = $this->app->input->getInt('catid', null)) + { + return $requestCatID; + } + + // Apparently, the catid is not available in the Query String. Let's ask Article model. + $item = $this->getItem($id); + + if (is_object($item) && isset($item->catid)) + { + return $item->catid; + } + } + + /** + * Load a Joomla article data object. + * + * @return object + */ + public function getItem($id = null) + { + $id = is_null($id) ? $this->request->id : $id; + + // Sanity check + if (is_null($id)) + { + return; + } + + $hash = md5('contentItem' . $id); + $cache = $this->factory->getCache(); + + if ($cache->has($hash)) + { + return $cache->get($hash); + } + + // Prevent "Article not found" error on J3. + if (!defined('nrJ4')) + { + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__content')) + ->where($db->quoteName('id') . ' = ' . $db->q((int) $id)); + + $db->setQuery($query); + + if (!$db->loadResult()) + { + return $cache->set($hash, null); + } + } + + // Use try catch to prevent fatal errors in case the article is not found + try + { + $model = $this->getArticleModel(); + $item = $model->getItem($id); + + if ($item) + { + $item->images = is_string($item->images) ? json_decode($item->images) : $item->images; + $item->urls = is_string($item->urls) ? json_decode($item->urls) : $item->urls; + $item->attribs = is_string($item->attribs) ? json_decode($item->attribs) : $item->attribs; + } + + return $cache->set($hash, $item); + } catch (\Throwable $th) + { + return null; + } + } + + /** + * Return the Article's model. + * + * @return object + */ + private function getArticleModel() + { + if (defined('nrJ4')) + { + $mvcFactory = Factory::getApplication()->bootComponent('com_content')->getMVCFactory(); + return $mvcFactory->createModel('Article', 'Administrator'); + } + + // Joomla 3 + BaseDatabaseModel::addIncludePath(JPATH_SITE . '/components/com_content/models'); + return BaseDatabaseModel::getInstance('Article', 'ContentModel'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentCategory.php new file mode 100644 index 00000000..40ff566b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ContentCategory extends ContentBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentView.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentView.php new file mode 100644 index 00000000..3fb02046 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ContentView.php @@ -0,0 +1,44 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ContentView extends ContentBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['contentview']; + + /** + * Pass check for Joomla! Articles + * + * @return bool + * @return bool + */ + public function pass() + { + // Make sure we are in the right context + if (empty($this->selection) || !$this->passContext()) + { + return false; + } + + // In the Joomla Content component, the 'view' query parameter equals to 'category' in both Category List and Category Blog views. + // In order to distinguish them we are using the 'layout' parameter as well. + if ($this->request->view == 'category' && $this->request->layout) + { + $this->request->view .= '_' . $this->request->layout; + } + + return $this->passByOperator($this->request->view, $this->selection, 'includes'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Base.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Base.php new file mode 100644 index 00000000..07825022 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Base.php @@ -0,0 +1,57 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJCatalog2Base extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'item'; + + /** + * The component's Category Page view name + * + * @var string + */ + protected $viewCategory = 'items'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_djcatalog2'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('category_id')) + ->from($db->quoteName('#__djc2_items_categories')) + ->where($db->quoteName('item_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Category.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Category.php new file mode 100644 index 00000000..76f8a88d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Category.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJCatalog2Category extends DJCatalog2Base +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['djcatalog2.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('djc2_categories'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Single.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Single.php new file mode 100644 index 00000000..bdd8fcdb --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJCatalog2Single.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJCatalog2Single extends DJCatalog2Base +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['djcatalog2.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsBase.php new file mode 100644 index 00000000..4d1034db --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJClassifiedsBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'item'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_djclassifieds'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('cat_id') + ->from('#__djcf_items') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsCategory.php new file mode 100644 index 00000000..d7f15364 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJClassifiedsCategory extends DJClassifiedsBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['djclassifieds.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('djcf_categories', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsSingle.php new file mode 100644 index 00000000..6d9c6d9f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJClassifiedsSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJClassifiedsSingle extends DJClassifiedsBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['djclassifieds.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsBase.php new file mode 100644 index 00000000..c22e56d2 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJEventsBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'event'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_djevents'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('cat_id')) + ->from('#__djev_events') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsCategory.php new file mode 100644 index 00000000..fbbc7c80 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJEventsCategory extends DJEventsBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['djevents.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('djev_cats', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsSingle.php new file mode 100644 index 00000000..86c660eb --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DJEventsSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DJEventsSingle extends DJEventsBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['djevents.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarBase.php new file mode 100644 index 00000000..03b9c30f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DPCalendarBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'event'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_dpcalendar'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('catid')) + ->from('#__dpcalendar_events') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarCategory.php new file mode 100644 index 00000000..c43f84d5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarCategory.php @@ -0,0 +1,26 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DPCalendarCategory extends DPCalendarBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('categories', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarSingle.php new file mode 100644 index 00000000..2c116540 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/DPCalendarSingle.php @@ -0,0 +1,35 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class DPCalendarSingle extends DPCalendarBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogBase.php new file mode 100644 index 00000000..ca028c6f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EasyBlogBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'entry'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_easyblog'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__easyblog_post') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogCategory.php new file mode 100644 index 00000000..0de977d9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EasyBlogCategory extends EasyBlogBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['easyblog.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('easyblog_category', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogSingle.php new file mode 100644 index 00000000..f64d8de9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EasyBlogSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EasyBlogSingle extends EasyBlogBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['easyblog.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EcommerceBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EcommerceBase.php new file mode 100644 index 00000000..3ce39227 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EcommerceBase.php @@ -0,0 +1,400 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +class EcommerceBase extends ComponentBase +{ + /** + * Pass method for "Amount In Cart" condition. + * + * @return bool + */ + public function passAmountInCart() + { + // Whether we exclude shipping cost + $exclude_shipping_cost = $this->params->get('exclude_shipping_cost', '0') === '1'; + + $shipping_total = 0; + $amount = 0; + + switch ($this->params->get('total', 'total')) + { + case 'total': + $amount = $this->getCartTotal(); + if ($exclude_shipping_cost) + { + $shipping_total = -$this->getShippingTotal(); + } + break; + + case 'subtotal': + $amount = $this->getCartSubtotal(); + if (!$exclude_shipping_cost) + { + $shipping_total = $this->getShippingTotal(); + } + break; + } + + // Calculate final amount + $amount = $amount + $shipping_total; + + $operator = $this->options->get('operator', 'equal'); + + $selection = (float) $this->selection; + + // Range selection + if ($operator === 'range') + { + $selection = [ + 'value1' => $selection, + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return $this->passByOperator($amount, $selection, $operator); + } + + /** + * Pass method for "Products In Cart" condition. + * + * @param string $cart_product_item_id_key + * + * @return bool + */ + protected function passProductsInCart($cart_product_item_id_key = ['id'], $product_prop_key = 'quantity') + { + // Get cart products + if (!$cartProducts = $this->getCartProducts()) + { + return false; + } + + // Get condition products + if (!$conditionProducts = $this->selection) + { + return false; + } + + if (!is_array($conditionProducts)) + { + return false; + } + + // Ensure all condition's products exist in the cart + $foundCartProducts = array_filter( + $cartProducts, + function ($prod) use ($conditionProducts, $cart_product_item_id_key, $product_prop_key) + { + $prod = (array) $prod; + + // Check the ID first + foreach ($cart_product_item_id_key as $id_key) + { + $valid = array_filter($conditionProducts, function($item) use ($prod, $id_key) { + return isset($item['value']) && (int) $item['value'] === (int) $prod[$id_key]; + }); + + if ($valid) + { + break; + } + } + + // If not valid, abort + if (!$valid) + { + return; + } + + // Get valid product + $valid_product = reset($valid); + + // Ensure product has property + $product_property_value = isset($prod[$product_prop_key]) ? (int) $prod[$product_prop_key] : false; + if (!$product_property_value) + { + return $valid; + } + + // We need an operator other than "any" + if (!isset($valid_product['operator']) || $valid_product['operator'] === 'any') + { + return $valid; + } + + // Ensure value 1 is valid + $product_value1 = isset($valid_product['value1']) ? (int) $valid_product['value1'] : false; + if (!$product_value1) + { + return $valid; + } + + $product_value2 = isset($valid_product['value2']) ? (int) $valid_product['value2'] : false; + + // Default selection + $selection = $product_value1; + + // Range selection + if ($valid_product['operator'] === 'range') + { + $selection = [ + 'value1' => $product_value1, + 'value2' => $product_value2 + ]; + } + + return $this->passByOperator($product_property_value, $selection, $valid_product['operator']); + } + ); + + return count($foundCartProducts); + } + + /** + * Pass method for "Last Purchase Date" condition. + * + * @return bool + */ + protected function passLastPurchaseDate() + { + if (!$user = Factory::getUser()) + { + return; + } + + if (!$user->id) + { + return; + } + + if (!$purchase_date = $this->getLastPurchaseDate($user->id)) + { + return; + } + + $purchaseDate = new \DateTime('@' . $purchase_date); + $purchaseDate->setTimezone(new \DateTimeZone('UTC')); + $purchaseDate->setTime(0,0); + + $currentDate = new \DateTime('now', new \DateTimeZone('UTC')); + + $pass = false; + + $operator = $this->options->get('params.operator', 'within_hours'); + + switch ($operator) + { + case 'within_hours': + case 'within_days': + case 'within_weeks': + case 'within_months': + if (!$within_value = intval($this->options->get('params.within_value'))) + { + return; + } + $period = str_replace('within_', '', $operator); + + $timeframe = strtoupper($period[0]); + + // Hours requires a "T" + if ($timeframe === 'H') + { + $within_value = 'T' . $within_value; + } + + $interval = new \DateInterval("P{$within_value}{$timeframe}"); + $purchaseDateXDaysAgo = (clone $purchaseDate)->add($interval); + $interval->invert = 1; // Set invert to 1 to indicate past time + + $pass = $purchaseDateXDaysAgo >= $currentDate; + + break; + + case 'equal': + if (!$this->selection) + { + return; + } + + $selectionDate = new \DateTime($this->selection, new \DateTimeZone('UTC')); + + $pass = $purchaseDate->format('Y-m-d') === $selectionDate->format('Y-m-d'); + break; + case 'before': + if (!$this->selection) + { + return; + } + + $selectionDate = new \DateTime($this->selection, new \DateTimeZone('UTC')); + + $pass = $purchaseDate < $selectionDate; + + break; + case 'after': + if (!$this->selection) + { + return; + } + + $selectionDate = new \DateTime($this->selection, new \DateTimeZone('UTC')); + + $pass = $purchaseDate > $selectionDate; + break; + case 'range': + if (!$secondDate = $this->options->get('params.value2')) + { + return; + } + + if (!$this->selection) + { + return; + } + + $startDate = new \DateTime($this->selection, new \DateTimeZone('UTC')); + $endDate = new \DateTime($secondDate, new \DateTimeZone('UTC')); + + $pass = $purchaseDate >= $startDate && $purchaseDate <= $endDate; + break; + } + + return $pass; + } + + /** + * Pass method for "Current Product Price" condition. + * + * @return bool + */ + public function passCurrentProductPrice() + { + // Ensure we are viewing a product page + if (!$this->isSinglePage()) + { + return; + } + + if (!$this->selection) + { + return; + } + + // Get current product data + if (!$product_data = $this->getCurrentProductData()) + { + return; + } + + // Get value 1 + $selection = (float) $this->options->get('selection'); + + // Range selection + if ($this->operator === 'range') + { + $value2 = (float) $this->options->get('params.value2'); + + $selection = [ + 'value1' => $selection, + 'value2' => $value2 + ]; + } + + return $this->passByOperator($product_data['price'], $selection, $this->operator); + } + + /** + * Pass method for "Current Product Stock" condition. + * + * @return bool + */ + public function passCurrentProductStock() + { + // Ensure we are viewing a product page + if (!$this->isSinglePage()) + { + return; + } + + if (!$this->selection) + { + return; + } + + $current_product_id = $this->request->id; + + if (!$product_stock = $this->getProductStock($current_product_id)) + { + return; + } + + // Get value 1 + $selection = (int) $this->options->get('selection'); + + // Range selection + if ($this->operator === 'range') + { + $value2 = (int) $this->options->get('params.value2'); + + $selection = [ + 'value1' => $selection, + 'value2' => $value2 + ]; + } + + return $this->passByOperator($product_stock, $selection, $this->operator); + } + + protected function getPreparedSelection() + { + $selection = $this->getSelection(); + + if (!is_array($selection)) + { + return $selection; + } + + foreach ($selection as &$value) + { + if (!is_array($value)) + { + continue; + } + + $params = isset($value['params']) ? $value['params'] : []; + if ($params) + { + if (isset($params['value'])) + { + $params['value1'] = $params['value']; + } + unset($params['value']); + unset($value['params']); + } + $value = array_merge($value, $params); + } + + return $selection; + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) {} +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopBase.php new file mode 100644 index 00000000..3d69595c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EshopBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'product'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_eshop'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__eshop_productcategories') + ->where($db->quoteName('product_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopCategory.php new file mode 100644 index 00000000..b175152a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EshopCategory extends EshopBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['eshop.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('eshop_categories', 'category_parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopSingle.php new file mode 100644 index 00000000..6816079d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EshopSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EshopSingle extends EshopBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['eshop.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingBase.php new file mode 100644 index 00000000..67474860 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EventBookingBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'event'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_eventbooking'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__eb_event_categories') + ->where($db->quoteName('event_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingCategory.php new file mode 100644 index 00000000..c595f404 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EventBookingCategory extends EventBookingBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['eventbookingcategory']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('eb_categories', 'parent'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingSingle.php new file mode 100644 index 00000000..9db7c292 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/EventBookingSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class EventBookingSingle extends EventBookingBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['eventbookingsingle']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxBase.php new file mode 100644 index 00000000..c0e38114 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class GridboxBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'page'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_gridbox'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('page_category') + ->from('#__gridbox_pages') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxCategory.php new file mode 100644 index 00000000..43f6a7f1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class GridboxCategory extends GridboxBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['gridbox.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('gridbox_categories', 'parent'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxSingle.php new file mode 100644 index 00000000..da40bd17 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/GridboxSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class GridboxSingle extends GridboxBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['gridbox.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopBase.php new file mode 100644 index 00000000..1d7aaab3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopBase.php @@ -0,0 +1,208 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikaShopBase extends EcommerceBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'product'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_hikashop'; + + /** + * The request ID used to retrieve the ID of the product + * + * @var string + */ + protected $request_id = 'product_id'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + $this->request->id = $this->app->input->get('cid', $this->app->input->getInt('product_id')); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__hikashop_product_category') + ->where($db->quoteName('product_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } + + /** + * Returns Hikashop cart data + * + * @return mixed + */ + protected function getCart() + { + @include_once(implode(DIRECTORY_SEPARATOR, [JPATH_ADMINISTRATOR, 'components', 'com_hikashop', 'helpers', 'helper.php'])); + @include_once(implode(DIRECTORY_SEPARATOR, [JPATH_ADMINISTRATOR, 'components', 'com_hikashop', 'helpers', 'checkout.php'])); + + if (!class_exists('hikashopCheckoutHelper')) + { + return; + } + + $checkoutHelper = \hikashopCheckoutHelper::get(); + return $checkoutHelper->getCart(true); + } + + /** + * Returns the products in the cart + * + * @return array + */ + protected function getCartProducts() + { + if (!$cart = $this->getCart()) + { + return []; + } + + return $cart->cart_products; + } + + /** + * Returns the current user's last purchase date in format: d/m/Y H:i:s and in UTC. + * + * @param int $user_id + * + * @return string + */ + protected function getLastPurchaseDate($user_id = null) + { + if (!$user_id) + { + return; + } + + $db = $this->db; + + $query = $this->db->getQuery(true) + ->clear() + ->select('o.order_created') + ->from('#__hikashop_order_product AS op') + ->leftJoin('#__hikashop_order AS o ON o.order_id = op.order_id') + ->leftJoin('#__hikashop_user AS u ON u.user_id = o.order_user_id') + ->where('o.order_status IN ("confirmed", "shipped")') + ->where('u.user_cms_id = ' . (int) $user_id) + ->order('o.order_created DESC') + ->setLimit(1); + + $db->setQuery($query); + + return $db->loadResult(); + } + + /** + * Returns the current product. + * + * @return object + */ + protected function getCurrentProduct() + { + if (!$this->request->id) + { + return; + } + + if (!function_exists('hikashop_get')) + { + return; + } + + $productClass = hikashop_get('class.product'); + return $productClass->get($this->request->id); + } + + /** + * Returns the current product data. + * + * @return object + */ + protected function getCurrentProductData() + { + if (!$product = $this->getCurrentProduct()) + { + return; + } + + return [ + 'id' => $product->product_id, + 'price' => (float) $product->product_msrp + ]; + } + + /** + * Returns the product stock. + * + * @param int $id + * + * @return int + */ + public function getProductStock($id = null) + { + if (!$id) + { + return; + } + + if (!function_exists('hikashop_get')) + { + return; + } + + $productClass = hikashop_get('class.product'); + if (!$product = $productClass->get($id)) + { + return; + } + + // Means infinite + if ($product->product_quantity === -1) + { + return PHP_INT_MAX; + } + + return (int) $product->product_quantity; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartContainsProducts.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartContainsProducts.php new file mode 100644 index 00000000..ff3318d3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartContainsProducts.php @@ -0,0 +1,35 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCartContainsProducts extends HikashopBase +{ + public function prepareSelection() + { + return $this->getPreparedSelection(); + } + + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['hikashop.cart_contains_products']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passProductsInCart(['product_id', 'cart_product_parent_id'], 'cart_product_quantity'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartContainsXProducts.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartContainsXProducts.php new file mode 100644 index 00000000..dce3a962 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartContainsXProducts.php @@ -0,0 +1,43 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCartContainsXProducts extends HikashopBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['hikashop.cart_contains_x_products']; + + public function prepareSelection() + { + if ($this->operator === 'range') + { + return [ + 'value1' => (float) $this->options->get('selection'), + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return (float) $this->selection; + } + + public function value() + { + if (!$cartProducts = $this->getCartProducts()) + { + return false; + } + + return count($cartProducts); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartValue.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartValue.php new file mode 100644 index 00000000..0f5ee039 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCartValue.php @@ -0,0 +1,124 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCartValue extends HikashopBase +{ + public function prepareSelection() + { + if ($this->operator === 'range') + { + return [ + 'value1' => (float) $this->options->get('selection'), + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return (float) $this->options->get('selection'); + } + + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['hikashopcartvalue']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passAmountInCart(); + } + + /** + * Returns the cart total. + * + * @return float + */ + public function getCartTotal() + { + if (!$cart = $this->getCart()) + { + return 0; + } + + if (!isset($cart->full_total->prices[0]->price_value_with_tax)) + { + return 0; + } + + return $cart->full_total->prices[0]->price_value_with_tax; + } + + /** + * Returns the cart subtotal. + * + * @return float + */ + public function getCartSubtotal() + { + if (!$cart = $this->getCart()) + { + return 0; + } + + if (isset($cart->full_total->prices[0]->price_value_without_shipping)) + { + return $cart->full_total->prices[0]->price_value_without_shipping; + } + + if (isset($cart->full_total->prices[0]->price_value_without_payment)) + { + return $cart->full_total->prices[0]->price_value_without_payment; + } + + return 0; + } + + /** + * Returns the shipping total. + * + * @return float + */ + protected function getShippingTotal() + { + if (!$cart = $this->getCart()) + { + return 0; + } + + if (!isset($cart->shipping)) + { + return 0; + } + + if (!is_array($cart->shipping)) + { + return 0; + } + + if (!count($cart->shipping)) + { + return 0; + } + + $total_fees = 0; + foreach ($cart->shipping as $item) + { + $total_fees += (float) $item->shipping_price; + } + + return $total_fees; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCategory.php new file mode 100644 index 00000000..ddf363c8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCategory.php @@ -0,0 +1,45 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCategory extends HikashopBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['hikashopcategory']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('hikashop_category', 'category_parent_id'); + } + + /** + * Returns all parent rows + * + * @param integer $id Row primary key + * @param string $table Table name + * @param string $parent Parent column name + * @param string $child Child column name + * + * @return array Array with IDs + */ + public function getParentIds($id = 0, $table = 'hikashop_category', $parent = 'category_parent_id', $child = 'category_id') + { + return parent::getParentIds($id, $table, $parent, $child); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCategoryView.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCategoryView.php new file mode 100644 index 00000000..c582b2fb --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCategoryView.php @@ -0,0 +1,48 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCategoryView extends HikashopBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + if (!$this->isCategoryPage()) + { + return false; + } + + $this->params->set('view_category', true); + $this->params->set('view_single', false); + + return $this->passCategories('hikashop_category', 'category_parent_id'); + } + + /** + * Returns all parent rows + * + * @param integer $id Row primary key + * @param string $table Table name + * @param string $parent Parent column name + * @param string $child Child column name + * + * @return array Array with IDs + */ + public function getParentIds($id = 0, $table = 'hikashop_category', $parent = 'category_parent_id', $child = 'category_id') + { + return parent::getParentIds($id, $table, $parent, $child); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCurrentProductPrice.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCurrentProductPrice.php new file mode 100644 index 00000000..b77b88b1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCurrentProductPrice.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCurrentProductPrice extends HikashopBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCurrentProductPrice(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCurrentProductStock.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCurrentProductStock.php new file mode 100644 index 00000000..76ea537b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopCurrentProductStock.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopCurrentProductStock extends HikashopBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCurrentProductStock(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopLastPurchasedDate.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopLastPurchasedDate.php new file mode 100644 index 00000000..38287dae --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopLastPurchasedDate.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopLastPurchasedDate extends HikashopBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passLastPurchaseDate(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopPurchasedProduct.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopPurchasedProduct.php new file mode 100644 index 00000000..3bb09edb --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopPurchasedProduct.php @@ -0,0 +1,69 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopPurchasedProduct extends HikashopBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + if (!is_array($this->selection) || empty($this->selection)) + { + return; + } + + return $this->hasPurchased($this->selection); + } + + /** + * Returns which given products has the current logged-in user purchased. + * + * @param array $product_ids + * + * @return array + */ + private function hasPurchased($product_ids = []) + { + if (!$product_ids) + { + return; + } + + if (!$user = $this->factory->getUser()) + { + return; + } + + if (!$user->id) + { + return; + } + + $query = $this->db->getQuery(true) + ->clear() + ->select('DISTINCT op.order_id') + ->from('#__hikashop_order_product AS op') + ->leftJoin('#__hikashop_order AS o ON o.order_id = op.order_id') + ->leftJoin('#__hikashop_user AS u ON u.user_id = o.order_user_id') + ->where('op.product_id IN (' . implode(',', $product_ids) . ')') + ->where('o.order_status IN ("confirmed", "shipped")') + ->where('u.user_cms_id = ' . (int) $user->id); + + $this->db->setQuery($query); + + return $this->db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopSingle.php new file mode 100644 index 00000000..1914522b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopSingle extends HikashopBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['hikashopsingle']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopTotalSpend.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopTotalSpend.php new file mode 100644 index 00000000..2ea0dd82 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/HikashopTotalSpend.php @@ -0,0 +1,60 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class HikashopTotalSpend extends HikashopBase +{ + public function prepareSelection() + { + if ($this->operator === 'range') + { + return [ + 'value1' => (float) $this->options->get('selection'), + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return (float) $this->options->get('selection'); + } + + /** + * Returns the condtion value. + * + * @return float + */ + public function value() + { + if (!$user = $this->factory->getUser()) + { + return; + } + + if (!$user->id) + { + return; + } + + $db = $this->db; + + $query = $db->getQuery(true) + ->clear() + ->select('SUM(o.order_full_price) AS total') + ->from('#__hikashop_order AS o') + ->leftJoin('#__hikashop_user AS u ON u.user_id = o.order_user_id') + ->where('o.order_status IN (\'shipped\', \'confirmed\')') + ->where('u.user_cms_id = ' . (int) $user->id); + + $db->setQuery($query); + + return (float) $db->loadResult(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaBase.php new file mode 100644 index 00000000..ade5434d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ICagendaBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'event'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_icagenda'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('catid')) + ->from('#__icagenda_events') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaCategory.php new file mode 100644 index 00000000..4755b97a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaCategory.php @@ -0,0 +1,45 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ICagendaCategory extends ICagendaBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['icagenda.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('icagenda_category', ''); + } + + /* + * Returns all parent rows + * + * @param integer $id Row primary key + * @param string $table Table name + * @param string $parent Parent column name + * @param string $child Child column name + * + * @return array Array with IDs + */ + public function getParentIds($id = 0, $table = 'icagenda_category', $parent = '', $child = '') + { + return []; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaSingle.php new file mode 100644 index 00000000..4133bd5a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ICagendaSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ICagendaSingle extends ICagendaBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['icagenda.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreBase.php new file mode 100644 index 00000000..f61187e4 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreBase.php @@ -0,0 +1,70 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class J2StoreBase extends ComponentBase +{ + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_j2store'; + + /** + * Indicates whether the page is a category page + * + * @return boolean + */ + protected function isCategory() + { + return is_null($this->request->task); + } + + /** + * Indicates whether the page is a single page + * + * @return boolean + */ + public function isSinglePage() + { + return (in_array($this->request->view, ['products', 'producttags']) && $this->request->task == 'view'); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + // Get product information + require_once JPATH_ADMINISTRATOR . '/components/com_j2store/helpers/product.php'; + + // Make sure J2Store is loaded + if (!class_exists('J2Product')) + { + return; + } + + $item = \J2Product::getInstance()->setId($this->request->id)->getProduct(); + + if (!is_object($item) || !isset($item->source)) + { + return; + } + + return $item->source->catid; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreCategory.php new file mode 100644 index 00000000..ca22ce0f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class J2StoreCategory extends J2StoreBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['j2storecategory']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreSingle.php new file mode 100644 index 00000000..b605da3b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/J2StoreSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class J2StoreSingle extends J2StoreBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['j2storesingle']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBase.php new file mode 100644 index 00000000..63522f37 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'companies'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_jbusinessdirectory'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('categoryId')) + ->from('#__jbusinessdirectory_company_category') + ->where($db->quoteName('companyId') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessBase.php new file mode 100644 index 00000000..bfee5c09 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessBase.php @@ -0,0 +1,28 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryBusinessBase extends JBusinessDirectoryBase +{ + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options = null, $factory = null) + { + parent::__construct($options, $factory); + + $this->request->id = (int) $this->app->input->getInt('companyId'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessCategory.php new file mode 100644 index 00000000..a508c30c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryBusinessCategory extends JBusinessDirectoryBusinessBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbusinessdirectory.business_category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('jbusinessdirectory_categories', 'parent_id'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessSingle.php new file mode 100644 index 00000000..af257b30 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryBusinessSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryBusinessSingle extends JBusinessDirectoryBusinessBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbuisnessdirectory.business_single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventBase.php new file mode 100644 index 00000000..63ef9bd7 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventBase.php @@ -0,0 +1,56 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryEventBase extends JBusinessDirectoryBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'event'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options = null, $factory = null) + { + parent::__construct($options, $factory); + + $this->request->id = (int) $this->app->input->getInt('eventId'); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('categoryId')) + ->from('#__jbusinessdirectory_company_event_category') + ->where($db->quoteName('eventId') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventCategory.php new file mode 100644 index 00000000..e88c1796 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryEventCategory extends JBusinessDirectoryEventBase +{ +/** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbuisnessdirectory.event_category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('jbusinessdirectory_categories', 'parent_id'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventSingle.php new file mode 100644 index 00000000..d20eca5d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryEventSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryEventSingle extends JBusinessDirectoryEventBase +{ +/** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbuisnessdirectory.event_single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferBase.php new file mode 100644 index 00000000..efbb4354 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferBase.php @@ -0,0 +1,56 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryOfferBase extends JBusinessDirectoryBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'offer'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options = null, $factory = null) + { + parent::__construct($options, $factory); + + $this->request->id = (int) $this->app->input->getInt('offerId'); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->quoteName('categoryId')) + ->from('#__jbusinessdirectory_company_offer_category') + ->where($db->quoteName('offerId') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferCategory.php new file mode 100644 index 00000000..256c9e09 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryOfferCategory extends JBusinessDirectoryOfferBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbuisnessdirectory.offer_category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('jbusinessdirectory_categories', 'parent_id'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferSingle.php new file mode 100644 index 00000000..3b3e78af --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JBusinessDirectoryOfferSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JBusinessDirectoryOfferSingle extends JBusinessDirectoryOfferBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbuisnessdirectory.offer_single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProBase.php new file mode 100644 index 00000000..a32d1666 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JCalProBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'event'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_jcalpro'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__jcalpro_event_categories') + ->where($db->quoteName('event_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProCategory.php new file mode 100644 index 00000000..42f74470 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JCalProCategory extends JCalProBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jcalpro.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('categories', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProSingle.php new file mode 100644 index 00000000..2b314daf --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JCalProSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JCalProSingle extends JCalProBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jbuisnessdirectory.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsBase.php new file mode 100644 index 00000000..76d6bda3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsBase.php @@ -0,0 +1,73 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JEventsBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'icalrepeat'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_jevents'; + + /** + * Indicates whether the page is a single page + * + * @return boolean + */ + public function isSinglePage() + { + return ($this->request->task == 'icalrepeat.detail' || $this->request->task == 'icalrepeat'); + } + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + $this->request->id = $this->app->input->get('evid'); + $this->request->task = $this->app->input->get('jevtask'); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('catid') + ->from('#__jevents_vevent') + ->where($db->quoteName('ev_id') . '=' . $id); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsCategory.php new file mode 100644 index 00000000..8cc294a3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JEventsCategory extends JEventsBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jevents.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsSingle.php new file mode 100644 index 00000000..80ac1cb9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JEventsSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JEventsSingle extends JEventsBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jevents.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsBase.php new file mode 100644 index 00000000..c8800b7c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsBase.php @@ -0,0 +1,86 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JReviewsBase extends ComponentBase +{ + protected $viewSingle = 'article'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_content'; + + /** + * Indicates whether the page is a category page + * + * @return boolean + */ + protected function isCategory() + { + return is_null($this->request->task); + } + + /** + * Indicates whether the page is a single page + * + * @return boolean + */ + public function isSinglePage() + { + if (!class_exists('\ClassRegistry')) + { + return; + } + + if (!$listingModel = \ClassRegistry::getClass('EverywhereComContentModel')) + { + return; + } + + if (!$listing = $listingModel->getListingById($this->request->id)) + { + return; + } + + return $this->request->view === 'article' && $this->request->option === 'com_content' && $listing; + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + if (!class_exists('\ClassRegistry')) + { + return; + } + + if (!$listingModel = \ClassRegistry::getClass('EverywhereComContentModel')) + { + return; + } + + if (!$listing = $listingModel->getListingById($id)) + { + return; + } + + return isset($listing['Category']['cat_id']) ? $listing['Category']['cat_id'] : null; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsCategory.php new file mode 100644 index 00000000..74e2b245 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsCategory.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JReviewsCategory extends JReviewsBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsSingle.php new file mode 100644 index 00000000..67ab2753 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JReviewsSingle.php @@ -0,0 +1,35 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JReviewsSingle extends JReviewsBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingBase.php new file mode 100644 index 00000000..099069f9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingBase.php @@ -0,0 +1,63 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JShoppingBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'product'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_jshopping'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + $this->request->view = $this->app->input->get('view', $this->app->input->get('controller')); + $this->request->id = $this->app->input->getInt('product_id'); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__jshopping_products_to_categories') + ->where($db->quoteName('product_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingCategory.php new file mode 100644 index 00000000..e7a828e5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingCategory.php @@ -0,0 +1,45 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JShoppingCategory extends JShoppingBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jshopping.catagory']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('jshopping_categories', 'category_parent_id'); + } + + /** + * Returns all parent rows + * + * @param integer $id Row primary key + * @param string $table Table name + * @param string $parent Parent column name + * @param string $child Child column name + * + * @return array Array with IDs + */ + public function getParentIds($id = 0, $table = 'jshopping_categories', $parent = 'category_parent_id', $child = 'category_id') + { + return parent::getParentIds($id, $table, $parent, $child); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingSingle.php new file mode 100644 index 00000000..480713e9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/JShoppingSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class JShoppingSingle extends JShoppingBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['jshopping.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Base.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Base.php new file mode 100644 index 00000000..626279cb --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Base.php @@ -0,0 +1,142 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class K2Base extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'item'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_k2'; + + /** + * Get single page's assosiated categories + * + * @param integer The Single Page id + * + * @return integer + */ + protected function getSinglePageCategories($id) + { + $item = $this->getK2Item(); + + return isset($item->catid) ? $item->catid : null; + } + + /** + * Indicates whether the current view concerns a Category view + * + * @return boolean + */ + protected function isCategoryPage() + { + return ($this->request->layout == 'category' || $this->request->task == 'category' || $this->request->view == 'latest'); + } + + /** + * Returns a K2 item + * + * @return object|null + */ + public function getK2Item() + { + $cache = $this->factory->getcache(); + $hash = md5('k2assitem'); + + if ($cache->has($hash)) + { + return $cache->get($hash); + } + + // K2 doesn't have a Joomla 4+ version, bail early + if (!class_exists('JModelLegacy')) + { + return; + } + + // Ignore JModelLegacy here because K2 doesn't have a J4+ version, so we don't care. + return $cache->set($hash, \JModelLegacy::getInstance('Item', 'K2Model')->getData()); + } + + /** + * Return tags of a K2 item + * + * @param int $id K2 item ID + * + * @return array + */ + public function getK2tags($id = null) + { + $id = is_null($id) ? $this->request->id : $id; + + if (!$id) + { + return []; + } + + $cache = $this->factory->getcache(); + $hash = md5('k2_item_tags' . $id); + + if ($cache->has($hash)) + { + return $cache->get($hash); + } + + $q = $this->db->getQuery(true) + ->select('t.id') + ->from('#__k2_tags_xref AS tx') + ->join('LEFT', '#__k2_tags AS t ON t.id = tx.tagID') + ->where('tx.itemID = ' . $this->db->q($id)) + ->where('t.published = 1'); + + $this->db->setQuery($q); + + return $cache->set($hash, $this->db->loadColumn()); + } + + /** + * Get current view layout string + * + * @return string + */ + public function getPageType() + { + $view = $this->request->view; + $layout = $this->request->layout; + + if (is_null($layout)) + { + switch ($view) + { + case 'item': + $layout = 'item'; + break; + default: + $layout = $this->request->task; + break; + } + } + + $pagetype = $view . '_' . $layout; + + return $pagetype; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Category.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Category.php new file mode 100644 index 00000000..cbced803 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Category.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class K2Category extends K2Base +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['k2_cats', 'k2category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('k2_categories', 'parent'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Item.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Item.php new file mode 100644 index 00000000..8602d14c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Item.php @@ -0,0 +1,92 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +use NRFramework\Functions; + +defined('_JEXEC') or die; + +class K2Item extends K2Base +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['k2_items', 'k2item']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + $pass = $this->passSinglePage(); + + // Keywords Checking + $contentKeywords = $this->params->get('cont_keywords', ''); + $metaKeywords = $this->params->get('meta_keywords', ''); + + // If both are empty, do not maky any further check + if (empty($contentKeywords) && empty($metaKeywords)) + { + return $pass; + } + + // Load current K2 Item object + if (!$item = $this->getK2Item()) + { + return false; + } + + // check items's text + if (!empty($contentKeywords)) + { + $pass = $this->passArrayInString($contentKeywords, $item->introtext . $item->fulltext); + } + + // check item's metakeywords + if (!empty($metaKeywords)) + { + $pass = $this->passArrayInString($metaKeywords, $item->metakey); + } + + return $pass; + } + + /** + * Returns the assignment's value + * + * @return int Article ID + */ + public function value() + { + return $this->request->id; + } + + /** + * Checks if an array of values (needle) exists in a text (haystack). + * + * @param array $needle The searched array of values. + * @param string $haystack The text + * + * @return bool + */ + private function passArrayInString($needle, $haystack) + { + if (empty($needle) || empty($haystack)) + { + return false; + } + + $needle = Functions::makeArray($needle); + + return \NRFramework\Functions::strpos_arr($needle, $haystack); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Pagetype.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Pagetype.php new file mode 100644 index 00000000..4f08216f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Pagetype.php @@ -0,0 +1,45 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class K2Pagetype extends K2Base +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['k2_pagetypes', 'k2pagetype']; + + /** + * Pass check for K2 page types + * + * @return bool + */ + public function pass() + { + if (empty($this->selection) || !$this->passContext()) + { + return false; + } + + return parent::pass(); + } + + /** + * Returns the assignment's value + * + * @return string Pagetype + */ + public function value() + { + return $this->getPageType(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Tag.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Tag.php new file mode 100644 index 00000000..9a138118 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/K2Tag.php @@ -0,0 +1,45 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class K2Tag extends K2Base +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['k2_tags', 'k2tag']; + + /** + * Pass check for K2 Tags + * + * @return bool + */ + public function pass() + { + if (empty($this->selection) || !$this->passContext()) + { + return false; + } + + return parent::pass(); + } + + /** + * Returns the assignment's value + * + * @return array K2 item tags + */ + public function value() + { + return $this->getK2tags(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/QuixBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/QuixBase.php new file mode 100644 index 00000000..2c8f9430 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/QuixBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class QuixBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'page'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_quix'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('catid') + ->from('#__quix') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/QuixSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/QuixSingle.php new file mode 100644 index 00000000..4a8f58ca --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/QuixSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class QuixSingle extends QuixBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['quix.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogBase.php new file mode 100644 index 00000000..6d64dbc0 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class RSBlogBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'post'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_rsblog'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('cat_id') + ->from('#__rsblog_posts_categories') + ->where($db->quoteName('post_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogCategory.php new file mode 100644 index 00000000..35fe398d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class RSBlogCategory extends RSBlogBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['rsblog.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('rsblog_categories', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogSingle.php new file mode 100644 index 00000000..965b1677 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSBlogSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class RSBlogSingle extends RSBlogBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['rsblog.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProBase.php new file mode 100644 index 00000000..d18f0d2f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class RSEventsProBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'rseventspro'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_rseventspro'; + + /** + * Get single events's assosiated categories + * + * @param Integer The Single Event id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('id') + ->from('#__rseventspro_taxonomy') + ->where($db->quoteName('ide') . '=' . $db->q($id)) + ->where($db->quoteName('type') . '=' . $db->q('category')); + + $db->setQuery($query); + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProCategory.php new file mode 100644 index 00000000..ceae9d53 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class RSEventsProCategory extends RSEventsProBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['rseventspro.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('categories', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProSingle.php new file mode 100644 index 00000000..3348a66d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/RSEventsProSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class RSEventsProSingle extends RSEventsProBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['rseventspro.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderBase.php new file mode 100644 index 00000000..4b9e752c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderBase.php @@ -0,0 +1,50 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class SPPageBuilderBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'page'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_sppagebuilder'; + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('catid') + ->from('#__sppagebuilder') + ->where($db->quoteName('id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderCategory.php new file mode 100644 index 00000000..776312f0 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class SPPageBuilderCategory extends SPPageBuilderBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['sppagebuilder.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('categories', 'parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderSingle.php new file mode 100644 index 00000000..993f61a1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SPPageBuilderSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class SPPageBuilderSingle extends SPPageBuilderBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['sppagebuilder.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProBase.php new file mode 100644 index 00000000..5eabe939 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProBase.php @@ -0,0 +1,82 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class SobiProBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'entry'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_sobipro'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + $this->request->view = 'entry'; + + // Make sure SPRequest is loaded + if (!class_exists('SPRequest')) + { + return; + } + + $this->request->id = (int) \SPRequest::sid(); + } + + /** + * Indicates whether the page is a single page + * + * @return boolean + */ + public function isSinglePage() + { + return (parent::isSinglePage() && $this->request->task == 'entry.details'); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + + $db = $this->db; + + $query = $db->getQuery(true) + ->select('pid') + ->from('#__sobipro_relations') + ->where($db->quoteName('id') . '=' . $db->q($id)) + ->where($db->quoteName('oType') . " = 'entry'"); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProCategory.php new file mode 100644 index 00000000..c70abe44 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class SobiProCategory extends SobiProBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['sobipro.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('sobipro_relations', 'id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProSingle.php new file mode 100644 index 00000000..0980b91f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/SobiProSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class SobiProSingle extends SobiProBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['sobipro.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartBase.php new file mode 100644 index 00000000..9785ae16 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartBase.php @@ -0,0 +1,236 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartBase extends EcommerceBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'productdetails'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_virtuemart'; + + /** + * The request ID used to retrieve the ID of the product + * + * @var string + */ + protected $request_id = 'virtuemart_product_id'; + + /** + * The request ID used to retrieve the ID of the product category. + * + * @var string + */ + protected $category_request_id = 'virtuemart_category_id'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + + $this->request->id = $this->app->input->getInt($this->request_id, $this->app->input->getInt($this->category_request_id)); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('virtuemart_category_id') + ->from('#__virtuemart_product_categories') + ->where($db->quoteName($this->request_id) . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } + + /** + * Returns Virtuemart cart data + * + * @return mixed + */ + protected function getCart() + { + // load the configuration wherever required as its not available everywhere + if (!class_exists('VmConfig')) + { + @include_once JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/config.php'; + + \VmConfig::loadConfig(); + } + + @include_once JPATH_SITE . '/components/com_virtuemart/helpers/cart.php'; + + if (!class_exists('VirtueMartCart')) + { + return; + } + + $cart = \VirtueMartCart::getCart(); + $cart->prepareCartData(); + + return $cart; + } + + /** + * Returns the products in the cart + * + * @return array + */ + protected function getCartProducts() + { + if (!$cart = $this->getCart()) + { + return []; + } + + return $cart->products; + } + + /** + * Returns the current user's last purchase date in format: d/m/Y H:i:s and in UTC. + * + * @param int $user_id + * + * @return string + */ + protected function getLastPurchaseDate($user_id = null) + { + if (!$user_id) + { + return; + } + + $db = $this->db; + + $query = $this->db->getQuery(true) + ->clear() + ->select('created_on') + ->from('#__virtuemart_orders') + ->where('order_status IN ("C", "S", "F")') + ->where('virtuemart_user_id = ' . (int) $user_id) + ->order('created_on DESC') + ->setLimit(1); + + $db->setQuery($query); + + return strtotime($db->loadResult()); + } + + /** + * Returns the current product. + * + * @return object + */ + protected function getCurrentProduct() + { + if (!$this->request->id) + { + return; + } + + return $this->getProductById($this->request->id); + } + + protected function getProductById($id = null) + { + if (!$id) + { + return; + } + + if (!class_exists('VmModel')) + { + return; + } + + return \VmModel::getModel('Product')->getProduct($id); + } + + /** + * Returns the current product data. + * + * @return object + */ + protected function getCurrentProductData() + { + if (!$product = $this->getCurrentProduct()) + { + return; + } + + return [ + 'id' => $product->virtuemart_product_id, + 'price' => isset($product->prices['salesPrice']) ? (float) $product->prices['salesPrice'] : 0 + ]; + } + + /** + * Returns the product stock. + * + * @param int $id + * + * @return int + */ + public function getProductStock($id = null) + { + if (!$id) + { + return; + } + + + if (!$product = $this->getProductById($id)) + { + return; + } + + return $product->product_in_stock; + } + + /* + * Returns all parent rows + * + * @param integer $id Row primary key + * @param string $table Table name + * @param string $parent Parent column name + * @param string $child Child column name + * + * @return array Array with IDs + */ + public function getParentIds($id = 0, $table = 'virtuemart_categories', $parent = 'category_parent_id', $child = 'virtuemart_category_id') + { + return parent::getParentIds($id, $table, $parent, $child); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartContainsProducts.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartContainsProducts.php new file mode 100644 index 00000000..eca5320b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartContainsProducts.php @@ -0,0 +1,35 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCartContainsProducts extends VirtueMartBase +{ + public function prepareSelection() + { + return $this->getPreparedSelection(); + } + + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['virtumart.cart_contains_products']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passProductsInCart(['virtuemart_product_id', 'product_parent_id']); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartContainsXProducts.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartContainsXProducts.php new file mode 100644 index 00000000..a5388fa6 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartContainsXProducts.php @@ -0,0 +1,43 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCartContainsXProducts extends VirtueMartBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['virtuemart.contains_x_products']; + + public function prepareSelection() + { + if ($this->operator === 'range') + { + return [ + 'value1' => (float) $this->options->get('selection'), + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return (float) $this->options->get('selection'); + } + + public function value() + { + if (!$cartProducts = $this->getCartProducts()) + { + return false; + } + + return count($cartProducts); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartValue.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartValue.php new file mode 100644 index 00000000..a564b7c8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCartValue.php @@ -0,0 +1,121 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCartValue extends VirtueMartBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['virtuemart.cart_value']; + + public function prepareSelection() + { + if ($this->operator === 'range') + { + return [ + 'value1' => (float) $this->options->get('selection'), + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return (float) $this->options->get('selection'); + } + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passAmountInCart(); + } + + /** + * Returns the cart total billable cost + * + * @return float + */ + protected function getCartTotal() + { + if (!$cart = $this->getCart()) + { + return 0; + } + + if (!isset($cart->cartPrices['billTotal'])) + { + return 0; + } + + @include_once JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/currencydisplay.php'; + + if (!class_exists('CurrencyDisplay')) + { + return 0; + } + + $currency = \CurrencyDisplay::getInstance(); + + return $currency->roundByPriceConfig($cart->cartPrices['billTotal']); + } + + /** + * Returns the cart subtotal. + * + * @return float + */ + public function getCartSubtotal() + { + if (!$cart = $this->getCart()) + { + return 0; + } + + if (!isset($cart->cartPrices['basePrice'])) + { + return 0; + } + + @include_once JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/currencydisplay.php'; + + if (!class_exists('CurrencyDisplay')) + { + return 0; + } + + $currency = \CurrencyDisplay::getInstance(); + + return $currency->roundByPriceConfig($cart->cartPrices['basePrice']); + } + + /** + * Returns the shipping total. + * + * @return float + */ + protected function getShippingTotal() + { + if (!$cart = $this->getCart()) + { + return 0; + } + + if (!isset($cart->cartPrices['shipmentValue']) || !isset($cart->cartPrices['shipmentTax'])) + { + return 0; + } + + return $cart->cartPrices['shipmentValue'] + $cart->cartPrices['shipmentTax']; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCategory.php new file mode 100644 index 00000000..e1c1166b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCategory.php @@ -0,0 +1,31 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCategory extends VirtueMartBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['virtuemart.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('virtuemart_categories', 'category_parent_id'); + } + +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCategoryView.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCategoryView.php new file mode 100644 index 00000000..cf53ea45 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCategoryView.php @@ -0,0 +1,33 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCategoryView extends VirtueMartBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + if (!$this->isCategoryPage()) + { + return false; + } + + $this->params->set('view_category', true); + $this->params->set('view_single', false); + + return $this->passCategories('virtuemart_categories', 'category_parent_id'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCurrentProductPrice.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCurrentProductPrice.php new file mode 100644 index 00000000..743101a8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCurrentProductPrice.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCurrentProductPrice extends VirtueMartBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCurrentProductPrice(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCurrentProductStock.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCurrentProductStock.php new file mode 100644 index 00000000..a2ccf630 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartCurrentProductStock.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartCurrentProductStock extends VirtueMartBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCurrentProductStock(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartLastPurchasedDate.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartLastPurchasedDate.php new file mode 100644 index 00000000..0f64e5e8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartLastPurchasedDate.php @@ -0,0 +1,25 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartLastPurchasedDate extends VirtueMartBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passLastPurchaseDate(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartPurchasedProduct.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartPurchasedProduct.php new file mode 100644 index 00000000..d2dd61ac --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartPurchasedProduct.php @@ -0,0 +1,70 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +Use Joomla\CMS\Factory; + +class VirtueMartPurchasedProduct extends VirtueMartBase +{ + /** + * Pass check + * + * @return bool + */ + public function pass() + { + if (!is_array($this->selection) || empty($this->selection)) + { + return; + } + + return $this->hasPurchased($this->selection); + } + + /** + * Returns which given products has the current logged-in user purchased. + * + * @param array $product_ids + * + * @return array + */ + private function hasPurchased($product_ids = []) + { + if (!$product_ids) + { + return; + } + + if (!$user = Factory::getUser()) + { + return; + } + + if (!$user->id) + { + return; + } + + $query = $this->db->getQuery(true) + ->clear() + ->select('DISTINCT o.virtuemart_order_id') + ->from('#__virtuemart_orders AS o') + ->leftJoin('#__virtuemart_order_items AS oi ON oi.virtuemart_order_id = o.virtuemart_order_id') + ->where('oi.virtuemart_product_id IN (' . implode(',', $product_ids) . ')') + ->where('o.order_status IN ("C", "S", "F")') + ->where('o.virtuemart_user_id = ' . (int) $user->id); + + $this->db->setQuery($query); + + return $this->db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartSingle.php new file mode 100644 index 00000000..46565d98 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartSingle extends VirtueMartBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['virtuemart.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartTotalSpend.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartTotalSpend.php new file mode 100644 index 00000000..cd57ad76 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/VirtueMartTotalSpend.php @@ -0,0 +1,59 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class VirtueMartTotalSpend extends VirtueMartBase +{ + public function prepareSelection() + { + if ($this->operator === 'range') + { + return [ + 'value1' => (float) $this->options->get('selection'), + 'value2' => (float) $this->options->get('params.value2', false) + ]; + } + + return (float) $this->options->get('selection'); + } + + /** + * Returns the condtion value. + * + * @return float + */ + public function value() + { + if (!$user = $this->factory->getUser()) + { + return; + } + + if (!$user->id) + { + return; + } + + $db = $this->db; + + $query = $db->getQuery(true) + ->clear() + ->select('SUM(paid) AS total') + ->from('#__virtuemart_orders') + ->where('order_status IN ("C", "S", "F")') + ->where('virtuemart_user_id = ' . (int) $user->id); + + $db->setQuery($query); + + return round((float) $db->loadResult(), 2); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooBase.php new file mode 100644 index 00000000..8d4f51ef --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooBase.php @@ -0,0 +1,68 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ZooBase extends ComponentBase +{ + /** + * The component's Single Page view name + * + * @var string + */ + protected $viewSingle = 'item'; + + /** + * The component's option name + * + * @var string + */ + protected $component_option = 'com_zoo'; + + /** + * Class Constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + + $this->request->view = $this->app->input->get('view', $this->app->input->get('task')); + + // Normally the item's id can be read by the request parameters BUT if the item + // is assosiated to a menu item the item_id parameter is not yet available and + // we can only find it out through the menu's parameters. + $this->request->id = (int) $this->app->input->getInt('item_id', $this->app->getMenu()->getActive()->getParams()->get('item_id')); + } + + /** + * Get single page's assosiated categories + * + * @param Integer The Single Page id + * + * @return array + */ + protected function getSinglePageCategories($id) + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select('category_id') + ->from('#__zoo_category_item') + ->where($db->quoteName('item_id') . '=' . $db->q($id)); + + $db->setQuery($query); + + return $db->loadColumn(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooCategory.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooCategory.php new file mode 100644 index 00000000..472aeae0 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooCategory.php @@ -0,0 +1,30 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ZooCategory extends ZooBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['zoo.category']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passCategories('zoo_category', 'parent'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooSingle.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooSingle.php new file mode 100644 index 00000000..2899bede --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Component/ZooSingle.php @@ -0,0 +1,40 @@ + or later +*/ + +namespace NRFramework\Conditions\Conditions\Component; + +defined('_JEXEC') or die; + +class ZooSingle extends ZooBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['zoo.single']; + + /** + * Pass check + * + * @return bool + */ + public function pass() + { + return $this->passSinglePage(); + } + + /** + * Returns the assignment's value + * + * @return int + */ + public function value() + { + return $this->request->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/ConvertForms.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/ConvertForms.php new file mode 100644 index 00000000..9a2953e8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/ConvertForms.php @@ -0,0 +1,46 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class ConvertForms extends Condition +{ + /** + * Returns the assignment's value + * + * @return array List of campaign IDs + */ + public function value() + { + return $this->getCampaigns(); + } + + /** + * Returns campaigns list visitor is subscribed to + * If the user is logged in, we try to get the campaigns by user's ID + * Otherwise, the visitor cookie ID will be used instead + * + * @return array List of campaign IDs + */ + private function getCampaigns() + { + $class = '\ConvertForms\Helper'; + + if (!class_exists($class)) + { + return; + } + + return $class::getVisitorCampaigns(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/ConvertFormsForm.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/ConvertFormsForm.php new file mode 100644 index 00000000..67ee8304 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/ConvertFormsForm.php @@ -0,0 +1,52 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class ConvertFormsForm extends Condition +{ + /** + * Returns the condition value. + * + * @return array + */ + public function value() + { + return $this->getForms(); + } + + /** + * Returns all form IDs submitted by the visitor. + * If the user is logged in, we try to get the forms by user's ID + * Otherwise, the visitor cookie ID will be used instead. + * + * @return array + */ + private function getForms() + { + $class = '\ConvertForms\Helper'; + + if (!class_exists($class)) + { + return; + } + + // Sanity check + if (!method_exists($class, 'getVisitorSubmittedForms')) + { + return; + } + + return $class::getVisitorSubmittedForms(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Cookie.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Cookie.php new file mode 100644 index 00000000..0dec1685 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Cookie.php @@ -0,0 +1,48 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class Cookie extends Condition +{ + /** + * When we need to compare the user's value with the cookie value, we change the $selection to the value entered by the user. + * + * @return void + */ + public function prepareSelection() + { + if (in_array($this->operator, ['exists', 'empty'])) + { + return $this->getSelection(); + } + + return $this->params->get('content', ''); + } + + /** + * Return the value of the cookie as stored in the user's browser + * + * @return string The value of the cookie + */ + public function value() + { + /** + * $this->selection is not used here as prepareSelection() above, called in \NRFramework\Conditions\Condition->setSelection() method changes its value + * and thus we do not always have the correct Cookie Name to search for. + * + * $this->options->get('selection') will always have the correct cookie name. + */ + return $this->factory->getCookie($this->options->get('selection')); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Date.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Date.php new file mode 100644 index 00000000..184d1d0a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Date.php @@ -0,0 +1,63 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Date; + +defined('_JEXEC') or die; + +class Date extends DateBase +{ + /** + * Checks if current date passes the given date range. + * Dates must be always passed in format: Y-m-d H:i:s + * + * @return bool + */ + public function pass() + { + $publish_up = $this->params->get('publish_up'); + $publish_down = $this->params->get('publish_down'); + + // No valid dates + if (!$publish_up && !$publish_up) + { + return false; + } + + $up = $publish_up ? $this->getDate($publish_up) : null; + $down = $publish_down ? $this->getDate($publish_down) : null; + + return $this->checkRange($up, $down); + } + + /** + * Returns the assignment's value + * + * @return \Date Current date + */ + public function value() + { + return $this->date; + } + + /** + * This method is called before the value of the condition is stored into the database. + * + * Dates should be always stored in the database in GMT. Thus, we remove the timezone offset from the date. + * + * @param array $rule The condition object. + * + * @return void + */ + public function onBeforeSave(&$rule) + { + \NRFramework\Functions::fixDateOffset($rule['params']['publish_up']); + \NRFramework\Functions::fixDateOffset($rule['params']['publish_down']); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/DateBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/DateBase.php new file mode 100644 index 00000000..c881aad4 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/DateBase.php @@ -0,0 +1,109 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Date; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class DateBase extends Condition +{ + /** + * Server's Timezone + * + * @var DateTimeZone + */ + protected $tz; + + /** + * If set to True, dates will be constructed with modified offset based on the passed timezone + * + * @var Boolean + */ + protected $modify_offset = true; + + /** + * Class constructor + * + * @param object $assignment + */ + public function __construct($assignment = null, $factory = null) + { + parent::__construct($assignment, $factory); + + // Set timezone + if ($timezone = $this->params->get('timezone')) + { + $this->tz = new \DateTimeZone($timezone); + } + else + { + $this->tz = new \DateTimeZone($this->app->getCfg('offset', 'GMT')); + } + + // Set modify offset switch + $this->modify_offset = $this->params->get('modify_offset', true); + + // Set now date + $now = $this->params->get('now', 'now'); + $this->date = $this->getDate($now); + } + + /** + * Checks if the current datetime is between the specified range + * + * @param JDate &$up_date + * @param JDate &$down_date + * + * @return bool + */ + protected function checkRange(&$up_date, &$down_date) + { + if (!$up_date && !$down_date) + { + return false; + } + + $now = $this->date->getTimestamp(); + + if (((bool)$up_date && $up_date->getTimestamp() > $now) || + ((bool)$down_date && $down_date->getTimestamp() < $now)) + { + return false; + } + + return true; + } + + /** + * Create a date object based on the given string and apply timezone. + * + * @param String $date + * + * @return void + */ + protected function getDate($date = 'now') + { + // Fix the date string + \NRFramework\Functions::fixDate($date); + + if ($this->modify_offset) + { + // Create date, set timezone and modify offset + $date = $this->factory->getDate($date)->setTimeZone($this->tz); + } else + { + // Create date and set timezone without modifyig offset + $date = $this->factory->getDate($date, $this->tz); + } + + return $date; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Day.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Day.php new file mode 100644 index 00000000..7aa84a23 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Day.php @@ -0,0 +1,66 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Date; + +defined('_JEXEC') or die; + +class Day extends DateBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['weekday']; + + /** + * Cover special cases where the user checks whether the current day is a Weekday or Weekend. + * + * @param mixed $selection The current selection + * + * @return array + */ + public function prepareSelection() + { + $selection = (array) $this->getSelection(); + + foreach ($selection as $str) + { + $str = strtolower($str ?? ''); + + if (strpos($str, 'weekday') !== false) + { + $selection = array_merge($selection, range(1, 5)); + continue; + } + + if (strpos($str, 'weekend') !== false) + { + $selection = array_merge($selection, [6, 7]); + } + } + + return $selection; + } + + /** + * Return a list with all different formats of the current day. + * + * This returns the day in non-localized strings. + * + * @return array + */ + public function value() + { + return [ + $this->date->format('l', true, false), // 'Friday' + $this->date->format('D', true, false), // 'Fri' + $this->date->format('N', true, false), // '1' (Monday) to '7' (Sunday) + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Month.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Month.php new file mode 100644 index 00000000..5e803b97 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Month.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Date; + +defined('_JEXEC') or die; + +class Month extends DateBase +{ + /** + * Returns the assignment's value + * + * This returns the month in non-localized strings. + * + * @return string Name of the current month + */ + public function value() + { + return [ + $this->date->format('F', true, false), + $this->date->format('M', true, false), + $this->date->format('n', true, false), + $this->date->format('m', true, false), + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Scheduler.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Scheduler.php new file mode 100644 index 00000000..be123ff8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Scheduler.php @@ -0,0 +1,254 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Date; + +defined('_JEXEC') or die; + +/** + * DateTime Assignment Scheduling helper + */ +class Scheduler +{ + /** + * Starting date + * + * @var object \DateTime + */ + protected $start_date; + + /** + * Ending date + * + * @var object \DateTime + */ + protected $end_date; + + /** + * Date to test against + * + * @var object \DateTime + */ + protected $current_date; + + /** + * @var string + */ + protected $repetitionFrequency; + + /** + * @var int + */ + protected $repetitionStep; + + /** + * Used by 'weekly' repetition frequency + * + * @var array + */ + protected $weekdays; + + /** + * The interval between the starting and current date + * http://php.net/manual/en/class.dateinterval.php + * + * @var object \DateInterval + */ + protected $interval; + + + /** + * Scheduler constructor + * + * @param array $options: Scheduling options + * start_date: + * end_date: + * current_date: + * repetitionFrequency: one of 'daily', 'weekly', 'monthly', 'yearly' + * repetitionStep: integer (1,2,3,...) + * weekdays: Array, used with 'weekly' repetitionFrequency, any of 'Monday', 'Tuesday', etc. + * Defaults to start_date's day name if empty + */ + public function __construct($options) + { + $this->start_date = $options['start_date']; + $this->end_date = array_key_exists('end_date', $options) ? $options['end_date'] : null; + $this->current_date = $options['current_date']; + $this->repetitionFrequency = $options['repetitionFrequency']; + $this->repetitionStep = $options['repetitionStep']; + $this->weekdays = array_key_exists('weekdays', $options) ? + array_map('ucfirst', $options['weekdays']) : + null; + + //create a DateInterval object from current and start dates + $this->interval = $this->start_date->diff($this->current_date); + } + + /** + * @return bool + */ + public function repeat() + { + //check if we are within the start/end date range (inclusive) + if (!$this->checkDateRange()) + { + return false; + } + + $result = false; + + // + switch ($this->repetitionFrequency) + { + case 'daily': + $result = $this->repeatDaily(); + break; + case 'weekly': + $result = $this->repeatWeekly(); + break; + case 'monthly': + $result = $this->repeatMonthly(); + break; + case 'yearly': + $result = $this->repeatYearly(); + break; + } + + return $result; + } + + /** + * Daily repetition check + * + * @return bool + */ + protected function repeatDaily() + { + //get the number of days that have passed since start_date + $num_days = $this->interval->days; + + if ($num_days % $this->repetitionStep !== 0) { + return false; + } + + return true; + } + + /** + * Weekly repetition check + * + * @return bool + */ + protected function repeatWeekly() + { + //get current_date's day name + $today_name = $this->current_date->format('l'); + + // if $this->weekdays is empty use start_date's day name + if (empty($this->weekdays)) + { + $start_day_name = $this->start_date->format('l'); + if ($start_day_name !== $today_name) + { + return false; + } + } + else + { + if (!in_array($today_name, $this->weekdays)) + { + return false; + } + } + + //get the number of weeks that passed since start_date + $num_weeks = floor($this->interval->days / 7); + + if ($num_weeks % $this->repetitionStep !== 0) + { + return false; + } + + return true; + } + + /** + * Monthly repetition check + * + * @return bool + */ + protected function repeatMonthly() + { + //check if we are on the same day of the month + $start_day = $this->start_date->format('d'); + $current_day = $this->current_date->format('d'); + + if ($start_day !== $current_day) + { + return false; + } + + //get the number of months that have passed since start_date + $num_months = ($this->interval->y * 12) + $interval->m; + + if ($num_months % $this->repetitionStep !== 0) + { + return false; + } + + return true; + } + + /** + * Yearly repetition check + * + * @return bool + */ + protected function repeatYearly() + { + //check if we are on the same month and day + $start_day_month = $this->start_date->format('d-m'); + $current_day_month = $this->current_date->format('d-m'); + + if ($start_day_month !== $current_day_month) + { + return false; + } + + //get the number of years that have passed since start_date + $num_years = $this->interval->y; + + if ($num_years % $this->repetitionStep !== 0) + { + return false; + } + + return true; + } + + /** + * Checks if the current date is between the start/end range (inclusive) + * + * @return bool + */ + protected function checkDateRange() + { + if ($this->start_date > $this->current_date) + { + return false; + } + + if (!empty($this->end_date) && $this->end_date < $this->current_date) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Time.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Time.php new file mode 100644 index 00000000..b77f3b9b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Date/Time.php @@ -0,0 +1,60 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Date; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class Time extends DateBase +{ + /** + * If set to True, dates will be constructed with modified offset based on the passed timezone + * + * @var Boolean + */ + protected $modify_offset = false; + + /** + * Checks if current time passes the given time range + * + * @return bool + */ + public function pass() + { + $up = $this->date->format('Y-m-d', true) . ' ' . $this->params->get('publish_up'); + $down = $this->date->format('Y-m-d', true) . ' ' . $this->params->get('publish_down'); + + $up = $this->factory->getDate((string) $up, $this->tz); + $down = $this->factory->getDate((string) $down, $this->tz); + + return $this->checkRange($up, $down); + } + + /** + * Returns the assignment's value + * + * @return \Date Current date + */ + public function value() + { + return $this->date; + } + + /** + * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. + * + * @return string + */ + public function getValueHint() + { + return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_' . strtoupper($this->getName()), $this->date->format('H:i', true)); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Device.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Device.php new file mode 100644 index 00000000..cafe79a3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Device.php @@ -0,0 +1,38 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +use Joomla\CMS\Language\Text; + +class Device extends Condition +{ + /** + * Returns the assignment's value + * + * @return string Device type + */ + public function value() + { + return $this->factory->getDevice(); + } + + /** + * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. + * + * @return string + */ + public function getValueHint() + { + return parent::getValueHint() . ' ' . Text::_('NR_ASSIGN_DEVICES_NOTE'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/EngageBox.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/EngageBox.php new file mode 100644 index 00000000..e25bf460 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/EngageBox.php @@ -0,0 +1,60 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class EngageBox extends Condition +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['onotherbox']; + + /** + * Checks if the user viewed any of the given boxes + * + * @return bool + */ + public function pass() + { + // Skip if the visitorID is not set + $visitorID = \NRFramework\VisitorToken::getInstance()->get(); + if (empty($visitorID)) + { + return true; + } + + $box_ids = $this->selection; + if (!is_array($box_ids) || empty($box_ids)) + { + return true; + } + + $box_ids = implode(',', $box_ids); + + $query = $this->db->getQuery(true); + + $query + ->select('COUNT(id)') + ->from($this->db->quoteName('#__rstbox_logs')) + ->where($this->db->quoteName('event') . ' = 1') + ->where($this->db->quoteName('box') . " IN ( $box_ids )") + ->where($this->db->quoteName('visitorid') . ' = '. $this->db->quote($visitorID)); + + $this->db->setQuery($query); + + $pass = (int) $this->db->loadResult(); + + return (bool) $pass; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/City.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/City.php new file mode 100644 index 00000000..befcc5f1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/City.php @@ -0,0 +1,30 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Geo; + +defined('_JEXEC') or die; + +class City extends GeoBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['geo.city']; + + /** + * Returns the assignment's value + * + * @return string City name + */ + public function value() + { + return $this->geo->getCity(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Continent.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Continent.php new file mode 100644 index 00000000..c5519345 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Continent.php @@ -0,0 +1,54 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Geo; + +defined('_JEXEC') or die; + +use NRFramework\Functions; + +class Continent extends GeoBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['geo.continent']; + + /** + * Continent check + * + * @return bool + */ + public function prepareSelection() + { + $selection = Functions::makeArray($this->getSelection()); + + // Try to convert continent names to codes + return array_map(function($c) { + if (strlen($c) > 2) + { + $c = \NRFramework\Continents::getCode($c); + } + return $c; + }, $selection); + } + + /** + * Return the Continent's code and full name + * + * @return string Country code + */ + public function value() + { + return [ + $this->geo->getContinentName('en'), + $this->geo->getContinentCode() + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Country.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Country.php new file mode 100644 index 00000000..d8523f03 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Country.php @@ -0,0 +1,54 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Geo; + +defined('_JEXEC') or die; + +use NRFramework\Countries; +use NRFramework\Functions; + +class Country extends GeoBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['geo.country']; + + /** + * Country check + * + * @return bool + */ + public function prepareSelection() + { + $selection = Functions::makeArray($this->getSelection()); + + return array_map(function($c) { + if (strlen($c) > 2) + { + $c = Countries::getCode($c); + } + return $c; + }, $selection); + } + + /** + * Returns the assignment's value + * + * @return string Country code + */ + public function value() + { + return [ + $this->geo->getCountryName(), + $this->geo->getCountryCode() + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/GeoBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/GeoBase.php new file mode 100644 index 00000000..3e3afebc --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/GeoBase.php @@ -0,0 +1,107 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Geo; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +use NRFramework\User; +use Joomla\CMS\Language\Text; + +/** + * IP addresses sample + * + * Greece / Dodecanese: 94.67.238.3 + * Belgium / Flanders: 37.62.255.255 + * USA / New York: 72.229.28.185 + */ +class GeoBase extends Condition +{ + /** + * GeoIP Class + * + * @var class + */ + protected $geo; + + /** + * Indicates whether we detected successfully the user's geographical location + * + * @var bool + */ + protected $success; + + /** + * Class constructor + * + * @param object $options + * @param object $factory + */ + public function __construct($options = null, $factory = null) + { + parent::__construct($options, $factory); + + $ip = $this->params->get('ip', null); + + $this->loadGeo($ip); + } + + /** + * Load GeoIP Classes + * + * @return void + */ + private function loadGeo($ip) + { + if (!class_exists('TGeoIP')) + { + $path = JPATH_PLUGINS . '/system/tgeoip'; + + if (@file_exists($path . '/helper/tgeoip.php')) + { + if (@include_once($path . '/vendor/autoload.php')) + { + @include_once $path . '/helper/tgeoip.php'; + } + } + + // If for some reason the tgeoip plugin files do not exist, abort + if (!class_exists('TGeoIP')) + { + return; + } + } + + $this->geo = new \TGeoIP($ip); + + $record = $this->geo->getRecord(); + + $this->success = ($record !== false AND !is_null($record)); + } + + /** + * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. + * + * @return string + */ + public function getValueHint() + { + if (!$this->success) + { + return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_GEO_ERROR', User::getIP()); + } + + // If the rule returns an array, use the 1st one. + $value = $this->value(); + $value = is_array($value) ? $value[0] : $value; + + return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_GEO', User::getIP(), $this->getName(), ucfirst(strtolower($value))); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Region.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Region.php new file mode 100644 index 00000000..821d81de --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Geo/Region.php @@ -0,0 +1,63 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Geo; + +defined('_JEXEC') or die; + +class Region extends GeoBase +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['geo.region']; + + /** + * Returns the assignment's value + * + * @return string Region codes + */ + public function value() + { + return $this->getRegions(); + } + + /** + * Get list of all ISO 3611 Country Region Codes + * + * @return array + */ + private function getRegions() + { + $regionCodes = []; + $record = $this->geo->getRecord(); + + if ($record === false || is_null($record)) + { + return $regionCodes; + } + + // Skip if no regions found + if (!$regions = $record->subdivisions) + { + return $regionCodes; + } + + foreach ($regions as $key => $region) + { + // Get the Region's full name + $regionCodes[] = $region->names['en']; + + // Get the Region's code by preppending the country isocode to the region code + $regionCodes[] = $record->country->isoCode . '-' . $region->isoCode; + } + + return $regionCodes; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Homepage.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Homepage.php new file mode 100644 index 00000000..d6058244 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Homepage.php @@ -0,0 +1,28 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +use Joomla\CMS\Factory; + +class Homepage extends Condition +{ + public static $shortcode_aliases = ['ishomepage']; + + public function value() + { + $menu = Factory::getApplication()->getMenu(); + $lang = Factory::getLanguage()->getTag(); + + return ($menu->getActive() == $menu->getDefault($lang)); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/IP.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/IP.php new file mode 100644 index 00000000..72697c11 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/IP.php @@ -0,0 +1,132 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\User; +use NRFramework\Functions; +use NRFramework\Conditions\Condition; + +class IP extends Condition +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['ip_address', 'iprange']; + + public function prepareSelection() + { + return Functions::makeArray($this->getSelection()); + } + + /** + * Checks if the user's ip address is within the specified ranges + * + * @return bool + */ + public function pass() + { + // get the user's ip address + $user_ip = $this->value(); + + // get the supplied ip addresses/ranges as an array + foreach ($this->getSelection() as $ip_range) + { + if ($this->isInRange($user_ip, $ip_range)) + { + return true; + } + } + + return false; + } + + /** + * Returns the assignment's value + * + * @return string User IP + */ + public function value() + { + return User::getIP(); + } + + /** + * Checks if an IP address falls within an IP range + * Todo: factor out common logic... + * @param string $user_ip + * @param string $range + * @return boolean + */ + protected function isInRange($user_ip, $range) + { + if (empty($user_ip) || empty($range)) + { + return false; + } + + // break ip addresses/ranges into parts + $user_ip_parts = explode('.', $user_ip); + $ip_range_parts = explode('.', $range); + + for ($i = 0; $i < count($ip_range_parts); $i++) + { + $r = $ip_range_parts[$i]; + + // parse and check range + if (strpos($r, '-') !== FALSE) + { + list($range_start, $range_end) = explode('-', $r); + + // format checks... + if (!is_numeric($range_start) || !is_numeric($range_end)) + { + return false; + } + // cast strings to integers + $range_start = (int) $range_start; + $range_end = (int) $range_end; + + if ($range_start > $range_end || $range_start < 0 || $range_end > 255) + { + return false; + } + + if ((int)$user_ip_parts[$i] < $range_start || (int)$user_ip_parts[$i] > $range_end) + { + return false; + } + } + else + { + // format checks... + if (!is_numeric($r)) + { + return false; + } + + $r = (int)$r; + + if ($r < 0 || $r > 255) + { + return false; + } + + if ((int)$user_ip_parts[$i] !== $r) + { + return false; + } + } + } //for loop + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/AccessLevel.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/AccessLevel.php new file mode 100644 index 00000000..0dae899a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/AccessLevel.php @@ -0,0 +1,84 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Joomla; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +use NRFramework\Cache; +use Joomla\CMS\Language\Text; + +class AccessLevel extends Condition +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['user.access']; + + /** + * Get the user's authorized view levels + * + * @return array User groups + */ + public function value() + { + $viewLevels = $this->user->getAuthorisedViewLevels(); + + // Beyond the IDs return also the Titles of the User Access Levels but only when the User Value includes Titles (Performance-wise). This is mainly used in conditional shortcode to be able to do comparison with Titles. + if ($this->selection) + { + $userValueHasTitles = array_filter((array) $this->selection, function($item) + { + return !is_numeric($item); + }); + + if ($userValueHasTitles) + { + $viewLevels = array_merge($viewLevels, $this->getAuthorisedViewLevelTitles()); + } + } + + return $viewLevels; + } + + /** + * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. + * + * @return string + */ + public function getValueHint() + { + return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_' . strtoupper($this->getName()), implode(', ', $this->getAuthorisedViewLevelTitles())); + } + + /** + * Return a list with user access level titles + * + * @return array + */ + private function getAuthorisedViewLevelTitles() + { + $callback = function() + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->qn('title')) + ->from('#__viewlevels') + ->where($db->qn('id') . ' IN ' . '(' . implode(',', $this->user->getAuthorisedViewLevels()) . ')'); + + $db->setQuery($query); + + return $db->loadColumn(); + }; + + return Cache::memo('getAuthorisedViewLevelTitles', $callback); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Component.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Component.php new file mode 100644 index 00000000..aeabec5b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Component.php @@ -0,0 +1,27 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Joomla; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class Component extends Condition +{ + /** + * Returns the assignment's value + * + * @return string The component's name + */ + public function value() + { + return $this->app->input->get('option'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Language.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Language.php new file mode 100644 index 00000000..4e7ac3d9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Language.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Joomla; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class Language extends Condition +{ + /** + * Returns the assignment's value + * + * @return array Language strings + */ + public function value() + { + $lang = $this->factory->getLanguage(); + + $lang_strings = $lang->getLocale(); + $lang_strings[] = $lang->getTag(); + + return $lang_strings; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Menu.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Menu.php new file mode 100644 index 00000000..1407d2a1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/Menu.php @@ -0,0 +1,107 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Joomla; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class Menu extends Condition +{ + protected $itemID = null; + + public function __construct($options, $factory) + { + parent::__construct($options, $factory); + + $this->itemID = $this->app->input->getInt('Itemid', 0); + $this->selection = (array) $this->selection; + } + + /** + * Pass check for menu items + * + * @return bool + */ + public function pass() + { + $includeChildren = $this->params->get('inc_children', false); // includeChildren is more user-friendly for Restrict Content + $includeNoItemID = $this->params->get('noitem', false); + + // Pass if selection is empty or the itemid is missing + if (!$this->itemID || empty($this->selection)) + { + return $includeNoItemID; + } + + // return true if menu type is in selection + $menutype = 'type.' . $this->getMenuType(); + if (in_array($menutype, $this->selection)) + { + return true; + } + + // return true if menu is in selection and we are not including child items only + if (in_array($this->itemID, $this->selection)) + { + return ($includeChildren != 2); + } + + // Let's discover child items. + // Obviously if the option is disabled return false. + if (!$includeChildren) + { + return false; + } + + // Get menu item parents + $parent_ids = $this->getParentIds($this->itemID); + $parent_ids = array_diff($parent_ids, array('1')); + + foreach ($parent_ids as $id) + { + if (!in_array($id, $this->selection)) + { + continue; + } + + return true; + } + + return false; + } + + /** + * Returns the assignment's value + * + * @return integer Menu ID + */ + public function value() + { + return $this->itemID; + } + + /** + * Get active menu items's menu type + * + * @return bool False on failure, string on success + */ + private function getMenuType() + { + if (empty($this->itemID)) + { + return; + } + + $menu = $this->app->getMenu()->getItem((int) $this->itemID); + + return isset($menu->menutype) ? $menu->menutype : false; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/UserGroup.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/UserGroup.php new file mode 100644 index 00000000..2584de2a --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/UserGroup.php @@ -0,0 +1,74 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Joomla; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +use Joomla\CMS\Access\Access; +use Joomla\CMS\Language\Text; + +class UserGroup extends Condition +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['user.group']; + + /** + * Returns the ID and the Title of the user's authorized groups + * + * @return array User groups + */ + public function value() + { + $groups = $this->user->getAuthorisedGroups(); + + // Beyond the IDs return also the Titles of the User Groups but only when the User Value includes Titles (Performance-wise). This is mainly used in conditional shortcode to be able to do comparison with Titles. + if ($this->selection) + { + $userValueHasTitles = array_filter((array) $this->selection, function($item) + { + return !is_numeric($item); + }); + + if ($userValueHasTitles) + { + foreach ($groups as $id) + { + $groups[] = Access::getGroupTitle($id); + } + } + } + + return $groups; + } + + /** + * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. + * + * @return string + */ + public function getValueHint() + { + $db = $this->db; + + $query = $db->getQuery(true) + ->select($db->qn('title')) + ->from('#__usergroups') + ->where($db->qn('id') . ' IN ' . '(' . implode(',', $this->user->getAuthorisedGroups()) . ')'); + + $db->setQuery($query); + + $value = implode(', ', $db->loadColumn()); + + return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_' . strtoupper($this->getName()), $value); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/UserID.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/UserID.php new file mode 100644 index 00000000..cc505b7e --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Joomla/UserID.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions\Joomla; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class UserID extends Condition +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['user.id']; + + /** + * Returns the assignment's value + * + * @return int User ID + */ + public function value() + { + return $this->user->id; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/NewVisitor.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/NewVisitor.php new file mode 100644 index 00000000..ed745bb7 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/NewVisitor.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class NewVisitor extends Condition +{ + public static $shortcode_aliases = ['isnewvisitor']; + + /** + * Checks whether the visitor is new or returning + * + * @return boolean True when visitor is new + */ + public function value() + { + $visitor = new \NRFramework\Visitor(); + $visitor->createOrUpdateCookie(); + + return $visitor->isNew(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/OS.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/OS.php new file mode 100644 index 00000000..7707417c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/OS.php @@ -0,0 +1,50 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\WebClient; +use NRFramework\Functions; +use NRFramework\Conditions\Condition; + +class OS extends Condition +{ + /** + * Check the client's operating system + * + * @return bool + */ + public function prepareSelection() + { + $selection = Functions::makeArray($this->getSelection()); + + // backwards compatibility check + // replace 'iphone' and 'ipad' selection values with 'ios' + return array_map(function($os_selection) + { + if ($os_selection === 'iphone' || $os_selection === 'ipad') + { + return 'ios'; + } + return $os_selection; + }, $selection); + } + + /** + * Returns the assignment's value + * + * @return string OS name + */ + public function value() + { + return WebClient::getOS(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/PHP.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/PHP.php new file mode 100644 index 00000000..6db4ce66 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/PHP.php @@ -0,0 +1,37 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class PHP extends Condition +{ + /** + * Pass check Custom PHP + * + * @return bool + */ + public function pass() + { + return (bool) $this->value(); + } + + public function value() + { + // Enable buffer output + ob_start(); + $pass = $this->factory->getExecuter($this->selection)->run(); + ob_end_clean(); + + return $pass; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Pageviews.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Pageviews.php new file mode 100644 index 00000000..b27ae582 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Pageviews.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class Pageviews extends Condition +{ + /** + * Shortcode aliases for this Condition + */ + public static $shortcode_aliases = ['user.pageviews']; + + /** + * Returns the assignment's value + * + * @return int Number of page visits + */ + public function value() + { + return $this->factory->getSession()->get('session.counter', 0); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/Referrer.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Referrer.php new file mode 100644 index 00000000..ecbdb3c3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/Referrer.php @@ -0,0 +1,35 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +class Referrer extends URLBase +{ + /** + * Pass Referrer URL. + * + * @return bool Returns true if the Referrer URL contains any of the selection URLs + */ + public function pass() + { + return $this->passURL($this->value()); + } + + /** + * Returns the assignment's value + * + * @return string Referrer URL + */ + public function value() + { + return $this->app->input->server->get('HTTP_REFERER', '', 'RAW'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/ReturningNewVisitor.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/ReturningNewVisitor.php new file mode 100644 index 00000000..fecadec9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/ReturningNewVisitor.php @@ -0,0 +1,34 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; +/** + * @deprecated Use the NewVisitor condition instead. + */ +class ReturningNewVisitor extends Condition +{ + + public function pass() + { + // Get visitor instance + $visitor = new \NRFramework\Visitor(); + + // Create and update cookies as needed + $visitor->createOrUpdateCookie(); + + // Check if user is new + $isNew = $visitor->isNew(); + + return $this->operator === 'new' ? $isNew : !$isNew; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/TimeOnSite.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/TimeOnSite.php new file mode 100644 index 00000000..11a3c684 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/TimeOnSite.php @@ -0,0 +1,64 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Conditions\Condition; + +class TimeOnSite extends Condition +{ + /** + * Returns the assignment's value + * + * @return int Time on site in seconds + */ + public function value() + { + return $this->getTimeOnSite(); + } + + /** + * Returns the user's time on site in seconds + * + * @return int + */ + public function getTimeOnSite() + { + if (!$sessionStartTime = strtotime($this->getSessionStartTime())) + { + return; + } + + $dateTimeNow = strtotime(\NRFramework\Functions::dateTimeNow()); + return $dateTimeNow - $sessionStartTime; + } + + /** + * Returns the sessions start time + * + * @return string + */ + private function getSessionStartTime() + { + $session = $this->factory->getSession(); + + $var = 'starttime'; + $sessionStartTime = $session->get($var); + + if (!$sessionStartTime) + { + $date = \NRFramework\Functions::dateTimeNow(); + $session->set($var, $date); + } + + return $session->get($var); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/URL.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/URL.php new file mode 100644 index 00000000..ba3d7bd5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/URL.php @@ -0,0 +1,25 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +class URL extends URLBase +{ + /** + * Returns the assignment's value + * + * @return string Current URL + */ + public function value() + { + return $this->factory->getURL(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Conditions/URLBase.php b/plugins/system/nrframework/NRFramework/Conditions/Conditions/URLBase.php new file mode 100644 index 00000000..9da7c520 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Conditions/URLBase.php @@ -0,0 +1,102 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Conditions\Conditions; + +defined('_JEXEC') or die; + +use NRFramework\Functions; +use NRFramework\Conditions\Condition; + +class URLBase extends Condition +{ + public function prepareSelection() + { + return Functions::makeArray($this->getSelection()); + } + + /** + * Pass URL. + * + * @return bool Returns true if the current URL contains any of the selection URLs + */ + public function pass() + { + return $this->passURL(); + } + + /** + * Pass URL + * + * @param mixed $url If null, the current URL will be used. Otherwise we need a valid absolute URL. + * + * @return bool Returns true if the URL contains any of the selection URLs + */ + public function passURL($url = null) + { + // Get the current URL if none is passed + $url = is_null($url) ? $this->factory->getURL() : $url; + + // Create an array with all possible values of the URL + $urls = array( + html_entity_decode(urldecode($url), ENT_COMPAT, 'UTF-8'), + urldecode($url), + html_entity_decode($url, ENT_COMPAT, 'UTF-8'), + $url + ); + + // Remove duplicates and invalid URLs + $urls = array_filter(array_unique($urls)); + + $regex = $this->params->get('regex', false); + $pass = false; + + foreach ($urls as $url) + { + foreach ($this->getSelection() as $s) + { + // Skip empty selection URLs + $s = trim($s); + if (empty($s)) + { + continue; + } + + // Regular expression check + if ($regex) + { + $url_part = str_replace(array('#', '&'), array('\#', '(&|&)'), $s); + $s = '#' . $url_part . '#si'; + + if (@preg_match($s . 'u', $url) || @preg_match($s, $url)) + { + $pass = true; + break; + } + + continue; + } + + // String check + if (strpos($url, $s) !== false) + { + $pass = true; + break; + } + } + + if ($pass) + { + break; + } + } + + return $pass; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/ConditionsHelper.php b/plugins/system/nrframework/NRFramework/Conditions/ConditionsHelper.php new file mode 100644 index 00000000..40b4dce5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/ConditionsHelper.php @@ -0,0 +1,567 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Conditions; + +use NRFramework\Factory; +use Joomla\CMS\Language\Text; + +defined('_JEXEC') or die; + +/** + * Conditions Helper Class + * + * Singleton + */ +class ConditionsHelper +{ + /** + * Factory object + * + * @var \NRFramework\Factory + */ + protected $factory; + + /** + * Class constructor + */ + public function __construct($factory = null) + { + $this->factory = is_null($factory) ? new Factory() : $factory; + } + + /** + * Get only one instance of the class + * + * @return object + */ + static public function getInstance($factory = null) + { + static $instance = null; + + if ($instance === null) + { + $instance = new ConditionsHelper($factory); + } + + return $instance; + } + + /** + * Passes a set of groups which are connected with OR comparison operator. + * + * Expected object: + * + * $groups = [ + * [ + * mathing_method => string (all|any), + * rules => array + * ], + * [ + * mathing_method => string (all|any), + * rules => array + * ] + * ... + * ]; + * + * @param array $groups + * + * @return mixed On validation error return null, if validation runs return bool + */ + public function passSets($groups) + { + $pass = null; + + // Validations + if (!is_array($groups) OR (is_array($groups) AND empty($groups))) + { + return $pass; + } + + foreach ($groups as $group) + { + // Skip invalid groups + if (!isset($group['rules']) OR !is_array($group['rules']) OR (is_array($group['rules']) AND empty($group['rules']))) + { + continue; + } + + $matching_method = isset($group['matching_method']) ? $group['matching_method'] : 'all'; + + // If a group meets the condition, pass the check and abort so no further tests are executed. + if ($pass = $this->passSet($group['rules'], $matching_method)) + { + break; + } + } + + return $pass; + } + + /** + * Passes a set of rules. + * + * Expected object for rules: + * + * $rules = [ + * [ + * name => string, + * value => mixed, + * operator => string, + * params => array + * ], + * [ + * name => string, + * value => mixed, + * operator => string, + * params => array + * ] + * ... + * ]; + * + * @param array $rules + * @param string $matchingMethod + * + * @return bool + */ + public function passSet($rules, $matchingMethod) + { + $pass = null; + + // Validations + if (!is_array($rules) OR (is_array($rules) AND empty($rules))) + { + return $pass; + } + + foreach ($rules as $rule) + { + // Skip unknown rules + if (!isset($rule['name'])) + { + continue; + } + + // Validate rule + $params = isset($rule['params']) ? $rule['params'] : null; + $value = isset($rule['value']) ? $rule['value'] : ''; + $operator = isset($rule['operator']) ? $rule['operator'] : ''; + + // Run checks + $pass = $this->passOne($rule['name'], $value, $operator, $params); + + // Check no further the Ruleset when any of the following happens: + // 1. We expect ALL Rules to pass but one fails. + // 2. We expect ANY Rule to pass and one does so. + if ((!$pass AND $matchingMethod == 'all') OR ($pass AND $matchingMethod == 'any')) + { + break; + } + } + + return $pass; + } + + /** + * Execute given rnule + * + * @param string $name The name of the rule. Case-sensitive. + * @param mixed $selection The value to compare with the value returned by the rule. + * @param string $operator The operator to use to do the comparison + * @param array $params Optional rule parameters + + * @return mixed Null when the validation doesn't run properly, bool otherwize + */ + public function passOne($name, $selection, $operator, $params = []) + { + // Convert deprecated operators to new operators + $migrationMap = [ + 'is' => 'not_empty', + 'is_not' => 'empty', + ]; + + if (array_key_exists($operator, $migrationMap)) + { + $operator = $migrationMap[$operator]; + } + + if (!$rule = $this->getCondition($name, $selection, str_replace('not_', '', $operator ?? ''), $params)) + { + return; + } + + $pass = $rule->pass(); + + if (is_null($pass)) + { + return $pass; + } + + return strpos($operator ?? '', 'not_') !== false ? !$pass : $pass; + } + + /** + * Initialize the condition class object + * + * @param string $name The name of the rule. Case-sensitive. + * @param mixed $selection The value to compare with the value returned by the rule. + * @param string $operator The operator to use to do the comparison + * @param array $params Optional rule parameters + + * @return mixed Null on failure, object on success + */ + public function getCondition($name, $selection = null, $operator = '', $params = null) + { + if (!$name) + { + return; + } + + $class = __NAMESPACE__ . '\\Conditions\\' . $name; + + if (!class_exists($class)) + { + return; + } + + // Prepare rule options + $options = [ + 'selection' => $selection, + 'operator' => str_replace('not_', '', $operator), + 'params' => $params + ]; + + $rule = new $class($options, $this->factory); + + return $rule; + } + + /** + * Validate and manipulate rules before they are get stored into the database. + * + * @param array $rules + * + * @return void + */ + public function onBeforeSave(&$rules) + { + // If its a string, transform it into an array, otherwise, use the actual value (array) + $rules = is_string($rules) ? json_decode($rules, true) : $rules; + + if (!is_array($rules)) + { + return; + } + + foreach ($rules as &$group) + { + if (!isset($group['rules'])) + { + continue; + } + + foreach ($group['rules'] as &$rule) + { + if (!$condition = $this->getCondition($rule['name'])) + { + continue; + } + + if (!\method_exists($condition, 'onBeforeSave')) + { + continue; + } + + $condition->onBeforeSave($rule); + } + } + } + + public static function getConditionsList($product = '') + { + return [ + 'page_targeting' => [ + 'title' => Text::_('NR_PAGE_TARGETING'), + 'desc' => Text::sprintf('NR_PAGE_TARGETING_SECTION_DESC', $product), + 'conditions' => [ + 'Homepage' => [ + 'title' => Text::_('NR_HOMEPAGE'), + 'desc' => Text::sprintf('NR_HOMEPAGE_CONDITION_DESC', $product) + ], + 'Joomla\Menu' => [ + 'title' => Text::_('NR_MENU'), + 'desc' => Text::sprintf('NR_MENU_CONDITION_DESC', $product) + ], + 'URL' => [ + 'title' => Text::_('NR_URL_QUERY_STRING'), + 'desc' => Text::sprintf('NR_URL_CONDITION_DESC', $product) + ], + 'com_content#Component\ContentArticle' => [ + 'title' => Text::_('NR_CONTENT_ARTICLE'), + 'desc' => Text::sprintf('NR_CONTENT_ARTICLE_CONDITION_DESC', $product) + ], + 'com_content#Component\ContentCategory' => [ + 'title' => Text::_('NR_CONTENT_CATEGORY'), + 'desc' => Text::sprintf('NR_CONTENT_CATEGORY_CONDITION_DESC', $product) + ], + 'com_content#Component\ContentView' => [ + 'title' => Text::_('NR_CONTENT_VIEW'), + 'desc' => Text::sprintf('NR_CONTENT_VIEW_CONDITION_DESC', $product) + ] + ] + ], + 'user_targeting' => [ + 'title' => Text::_('NR_USER_TARGETING'), + 'desc' => Text::sprintf('NR_USER_TARGETING_SECTION_DESC', $product), + 'conditions' => [ + 'Joomla\UserID' => [ + 'title' => Text::_('NR_USER'), + 'desc' => Text::sprintf('NR_USER_CONDITION_DESC', $product) + ], + 'Joomla\UserGroup' => [ + 'title' => Text::_('NR_USERGROUP'), + 'desc' => Text::sprintf('NR_USER_GROUP_CONDITION_DESC', $product) + ], + 'Joomla\AccessLevel' => [ + 'title' => Text::_('NR_USERACCESSLEVEL'), + 'desc' => Text::sprintf('NR_USER_ACCESS_LEVEL_CONDITION_DESC', $product) + ], + 'ReturningNewVisitor' => [ + 'title' => Text::_('NR_NEW_VS_RETURNING'), + 'desc' => Text::sprintf('NR_NEW_VS_RETURNING_VISITOR_CONDITION_DESC', $product) + ], + 'IP' => [ + 'title' => Text::_('NR_IPADDRESS'), + 'desc' => Text::sprintf('NR_IP_ADDRESS_CONDITION_DESC', $product) + ], + 'Device' => [ + 'title' => Text::_('NR_DEVICE_TYPE'), + 'desc' => Text::sprintf('NR_DEVICE_CONDITION_DESC', $product) + ], + 'Geo\Country' => [ + 'title' => Text::_('NR_ASSIGN_COUNTRIES'), + 'desc' => Text::sprintf('NR_COUNTRY_CONDITION_DESC', $product) + ], + 'Geo\City' => [ + 'title' => Text::_('NR_CITY'), + 'desc' => Text::sprintf('NR_CITY_CONDITION_DESC', $product) + ], + 'Geo\Continent' => [ + 'title' => Text::_('NR_CONTINENT'), + 'desc' => Text::sprintf('NR_CONTINENT_CONDITION_DESC', $product) + ], + 'Geo\Region' => [ + 'title' => Text::_('NR_REGION'), + 'desc' => Text::sprintf('NR_REGION_CONDITION_DESC', $product) + ], + 'TimeOnSite' => [ + 'title' => Text::_('NR_ASSIGN_TIMEONSITE'), + 'desc' => Text::sprintf('NR_TIME_ON_SITE_CONDITION_DESC', $product) + ], + 'Pageviews' => [ + 'title' => Text::_('NR_PAGEVIEWS_COUNT'), + 'desc' => Text::sprintf('NR_PAGEVIEWS_COUNT_CONDITION_DESC', $product) + ], + 'OS' => [ + 'title' => Text::_('NR_OPERATING_SYSTEM'), + 'desc' => Text::sprintf('NR_OPERATING_SYSTEM_CONDITION_DESC', $product) + ], + 'Browser' => [ + 'title' => Text::_('NR_ASSIGN_BROWSERS'), + 'desc' => Text::sprintf('NR_BROWSER_CONDITION_DESC', $product) + ], + ] + ], + 'date_time' => [ + 'title' => Text::_('NR_DATE_AND_TIME'), + 'desc' => Text::sprintf('NR_DATE_AND_TIME_SECTION_DESC', $product), + 'conditions' => [ + 'Date\Date' => [ + 'title' => Text::_('NR_DATE'), + 'desc' => Text::sprintf('NR_DATE_CONDITION_DESC', $product) + ], + 'Date\Time' => [ + 'title' => Text::_('NR_TIME'), + 'desc' => Text::sprintf('NR_TIME_CONDITION_DESC', $product) + ], + 'Date\Day' => [ + 'title' => Text::_('NR_WEEKDAY'), + 'desc' => Text::sprintf('NR_DAY_CONDITION_DESC', $product) + ], + 'Date\Month' => [ + 'title' => Text::_('NR_MONTH'), + 'desc' => Text::sprintf('NR_MONTH_CONDITION_DESC', $product) + ], + ] + ], + 'eCommerce' => [ + 'title' => Text::_('NR_ECOMMERCE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_SECTION_DESC', $product), + 'conditions' => [ + 'com_virtuemart#Component\VirtueMartCartContainsProducts' => [ + 'title' => Text::_('NR_VM_CART_CONTAINS_PRODUCTS'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CART_CONTAINS_PRODUCTS_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartCartContainsXProducts' => [ + 'title' => Text::_('NR_VM_CART_CONTAINS_X_PRODUCTS'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CART_CONTAINS_X_PRODUCTS_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartCartValue' => [ + 'title' => Text::_('NR_VM_CART_VALUE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CART_VALUE_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartSingle' => [ + 'title' => Text::_('NR_VM_PRODUCT'), + 'desc' => Text::sprintf('NR_ECOMMERCE_PRODUCT_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartCategory' => [ + 'title' => Text::_('NR_VM_CURRENT_PRODUCT_CATEGORY'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_PRODUCT_CATEGORY_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartCurrentProductPrice' => [ + 'title' => Text::_('NR_VM_CURRENT_PRODUCT_PRICE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_PRODUCT_PRICE_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartCurrentProductStock' => [ + 'title' => Text::_('NR_VM_CURRENT_PRODUCT_STOCK'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_PRODUCT_STOCK_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartCategoryView' => [ + 'title' => Text::_('NR_VM_CURRENT_CATEGORY'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_CATEGORY_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartPurchasedProduct' => [ + 'title' => Text::_('NR_VM_PURCHASED_PRODUCT'), + 'desc' => Text::sprintf('NR_ECOMMERCE_PURCHASED_PRODUCT_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartLastPurchasedDate' => [ + 'title' => Text::_('NR_VM_LAST_PURCHASED_DATE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_LAST_PURCHASED_DATE_CONDITION_DESC', $product) + ], + 'com_virtuemart#Component\VirtueMartTotalSpend' => [ + 'title' => Text::_('NR_VM_TOTAL_SPEND'), + 'desc' => Text::sprintf('NR_ECOMMERCE_TOTAL_SPEND_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCartContainsProducts' => [ + 'title' => Text::_('NR_HIKA_CART_CONTAINS_PRODUCTS'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CART_CONTAINS_PRODUCTS_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCartContainsXProducts' => [ + 'title' => Text::_('NR_HIKA_CART_CONTAINS_X_PRODUCTS'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CART_CONTAINS_X_PRODUCTS_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCartValue' => [ + 'title' => Text::_('NR_HIKA_CART_VALUE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CART_VALUE_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopSingle' => [ + 'title' => Text::_('NR_HIKA_PRODUCT'), + 'desc' => Text::sprintf('NR_ECOMMERCE_PRODUCT_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCategory' => [ + 'title' => Text::_('NR_HIKA_CURRENT_PRODUCT_CATEGORY'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_PRODUCT_CATEGORY_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCurrentProductPrice' => [ + 'title' => Text::_('NR_HIKA_CURRENT_PRODUCT_PRICE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_PRODUCT_PRICE_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCurrentProductStock' => [ + 'title' => Text::_('NR_HIKA_CURRENT_PRODUCT_STOCK'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_PRODUCT_STOCK_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopCategoryView' => [ + 'title' => Text::_('NR_HIKA_CURRENT_CATEGORY'), + 'desc' => Text::sprintf('NR_ECOMMERCE_CURRENT_CATEGORY_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopPurchasedProduct' => [ + 'title' => Text::_('NR_HIKA_PURCHASED_PRODUCT'), + 'desc' => Text::sprintf('NR_ECOMMERCE_PURCHASED_PRODUCT_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopLastPurchasedDate' => [ + 'title' => Text::_('NR_HIKA_LAST_PURCHASED_DATE'), + 'desc' => Text::sprintf('NR_ECOMMERCE_LAST_PURCHASED_DATE_CONDITION_DESC', $product) + ], + 'com_hikashop#Component\HikashopTotalSpend' => [ + 'title' => Text::_('NR_HIKA_TOTAL_SPEND'), + 'desc' => Text::sprintf('NR_ECOMMERCE_TOTAL_SPEND_CONDITION_DESC', $product) + ] + ] + ], + 'integrations' => [ + 'title' => Text::_('NR_INTEGRATIONS'), + 'desc' => Text::sprintf('NR_INTEGRATIONS_SECTION_DESC', $product), + 'conditions' => [ + 'com_convertforms#ConvertForms'=> [ + 'title' => Text::_('NR_CONVERT_FORMS_CAMPAIGN'), + 'desc' => Text::sprintf('NR_CONVERT_FORMS_CAMPAIGN_CONDITION_DESC', $product) + ], + 'com_convertforms#ConvertFormsForm'=> [ + 'title' => Text::_('NR_CONVERT_FORMS_FORM'), + 'desc' => Text::sprintf('NR_CONVERT_FORMS_FORM_CONDITION_DESC', $product) + ], + 'com_rstbox#EngageBox'=> [ + 'title' => Text::_('NR_ENGAGEBOX'), + 'desc' => Text::sprintf('NR_ENGAGEBOX_CONDITION_DESC', $product) + ], + 'com_acymailing#AcyMailing|com_acym#AcyMailing' => [ + 'title' => Text::_('NR_ACYMAILING_LIST'), + 'desc' => Text::sprintf('NR_ACYMAILING_LIST_CONDITION_DESC', $product) + ], + 'com_k2#Component\K2Item' => [ + 'title' => Text::_('NR_K2_ITEM'), + 'desc' => Text::sprintf('NR_K2_ITEM_CONDITION_DESC', $product) + ], + 'com_k2#Component\K2Category' => [ + 'title' => Text::_('NR_K2_CATEGORY'), + 'desc' => Text::sprintf('NR_K2_CATEGORY_CONDITION_DESC', $product) + ], + 'com_k2#Component\K2Tag' => [ + 'title' => Text::_('NR_K2_TAG'), + 'desc' => Text::sprintf('NR_K2_TAG_CONDITION_DESC', $product) + ], + 'com_k2#Component\K2Pagetype' => [ + 'title' => Text::_('NR_K2_PAGE_TYPE'), + 'desc' => Text::sprintf('NR_K2_PAGE_TYPE_CONDITION_DESC', $product) + ], + 'com_akeebasubs#AkeebaSubs' => [ + 'title' => Text::_('NR_AKEEBASUBS_LEVELS'), + 'desc' => Text::sprintf('NR_AKEEBASUBS_LEVELS_CONDITION_DESC', $product) + ] + ] + ], + 'other' => [ + 'title' => Text::_('NR_OTHER'), + 'desc' => Text::sprintf('NR_OTHER_SECTION_DESC', $product), + 'conditions' => [ + 'Joomla\Component' => [ + 'title' => Text::_('NR_ASSIGN_COMPONENTS'), + 'desc' => Text::sprintf('NR_COMPONENT_CONDITION_DESC', $product) + ], + 'Joomla\Language' => [ + 'title' => Text::_('NR_ASSIGN_LANGS'), + 'desc' => Text::sprintf('NR_LANGUAGE_CONDITION_DESC', $product) + ], + 'Referrer' => [ + 'title' => Text::_('NR_ASSIGN_REFERRER'), + 'desc' => Text::sprintf('NR_REFERRER_URL_CONDITION_DESC', $product) + ], + 'Cookie' => [ + 'title' => Text::_('NR_COOKIE'), + 'desc' => Text::sprintf('NR_COOKIE_CONDITION_DESC', $product) + ], + 'PHP' => [ + 'title' => Text::_('NR_ASSIGN_PHP'), + 'desc' => Text::sprintf('NR_PHP_CONDITION_DESC', $product) + ] + ] + ] + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Conditions/Migrator.php b/plugins/system/nrframework/NRFramework/Conditions/Migrator.php new file mode 100644 index 00000000..c2be5e46 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Conditions/Migrator.php @@ -0,0 +1,220 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Conditions; + +use NRFramework\Assignments; + +defined('_JEXEC') or die; + +class Migrator +{ + /** + * Migrate old Assignments data to the new Condition Builder object. + * + * @since 5.0.1 + * + * @param object $box + * + * @return void + */ + public static function run(&$params) + { + if ($params->get('mirror') == '1') + { + $params->set('display_conditions_type', 'mirror'); + return; + } + + $assignmentsClass = new Assignments(); + + $matching_method_map = [ + 'and' => 'all', + 'or' => 'any' + ]; + + $rules = [ + 0 => [ + 'matching_method' => $matching_method_map[$params->get('assignmentMatchingMethod', 'and')], + 'enabled' => 1, + 'rules' => [] + ] + ]; + + foreach ($params as $paramKey => $paramValue) + { + if (strpos($paramKey, 'assign_') !== 0) + { + continue; + } + + $oldName = str_replace('assign_', '', $paramKey); + $newName = $assignmentsClass->aliasToClassname($oldName); + + // Skip unknown conditions + if (!$newName) + { + continue; + } + + // Skip disabled conditions + if ($paramValue == '0') + { + continue; + } + + // Date assignment doesn't use the value property + if ($newName == 'Date\Date') + { + $params->set($paramKey . '_list', true); + + $publish_up = $params->get('assign_'. $oldName .'_param_publish_up'); + $publish_down = $params->get('assign_'. $oldName .'_param_publish_down'); + + \NRFramework\Functions::fixDateOffset($publish_up); + \NRFramework\Functions::fixDateOffset($publish_down); + + $params->set('assign_'. $oldName .'_param_publish_up', $publish_up); + $params->set('assign_'. $oldName .'_param_publish_down', $publish_down); + } + + // Date assignment doesn't use the value property + if ($newName == 'Date\Time') + { + $params->set($paramKey . '_list', true); + } + + // Skip conditions with no value + if (!$value = $params->get($paramKey . '_list')) + { + continue; + } + + $operator = $paramValue == '1' ? 'includes' : 'not_includes'; + + // These Conditions have custom operators + if (in_array($newName, ['Date\Date', 'Date\Time'])) + { + $operator = $paramValue == '1' ? 'equal' : 'not_equal'; + } + + if ($newName == 'Cookie') + { + $operatorMap = [ + 'exists' => 'exists', + 'not_exists' => 'not_exists', + 'equal' => 'equal', + 'not_equal' => 'not_equal', + 'contains' => 'includes', + 'not_contains' => 'not_includes', + 'starts' => 'starts_with', + 'not_start' => 'not_starts_with', + 'ends' => 'ends_with', + 'not_end' => 'not_ends_with', + ]; + + if ($paramValue == '2') + { + switch ($value) + { + case 'exists': + $value = 'not_exists'; + break; + + case 'equal': + $value = 'not_equal'; + break; + + case 'contains': + $value = 'not_contains'; + break; + + case 'starts': + $value = 'not_start'; + break; + + case 'ends': + $value = 'not_end'; + break; + } + } + + $operator = $operatorMap[$value]; + + $params->set('assign_cookiename_param_operator', $operator); + + $value = $params->get('assign_cookiename_param_name'); + } + + if ($newName == 'Pageviews') + { + $operatorMap = [ + 'exactly' => 'equal', + 'not_equal' => 'not_equal', + 'fewer' => 'less_than', + 'greater' => 'greater_than', + ]; + + if ($paramValue == '2') + { + switch ($value) + { + case 'exactly': + $value = 'not_equal'; + break; + + case 'fewer': + $value = 'greater'; + break; + + case 'greater': + $value = 'fewer'; + break; + } + } + + $operator = $operatorMap[$value]; + $value = $params->get('assign_pageviews_param_views'); + } + + $data = [ + 'name' => $newName, + 'enabled' => 1, + 'operator' => $operator, + 'value' => $value + ]; + + // Find params + foreach ($params as $assignParamKey => $assignParamValue) + { + if (strpos($assignParamKey, $paramKey . '_param') !== 0) + { + continue; + } + + if ($assignParamValue == '') + { + continue; + } + + $realParamName = str_replace($paramKey . '_param_', '', $assignParamKey); + + $data['params'][$realParamName] = $assignParamValue; + } + + $rules[0]['rules'][] = $data; + } + + if (!empty($rules[0]['rules'])) + { + // Finally, set the rules + $params->set('display_conditions_type', 'custom'); + $params->set('rules', $rules); + } + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Continents.php b/plugins/system/nrframework/NRFramework/Continents.php new file mode 100644 index 00000000..01be803d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Continents.php @@ -0,0 +1,56 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework; + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +/** + * Helper class to work with continent names/codes + */ +class Continents +{ + /** + * Return a continent code from it's name + * + * @param string $cont + * @return string|void + */ + public static function getCode($cont) + { + $cont = \ucwords(strtolower($cont)); + foreach (self::getContinentsList() as $key => $value) + { + if (strpos($value, $cont) !== false) + { + return $key; + } + } + return null; + } + + /** + * Returns a list of continents + * + * @return array + */ + public static function getContinentsList() + { + return [ + 'AF' => Text::_('NR_CONTINENT_AF'), + 'AS' => Text::_('NR_CONTINENT_AS'), + 'EU' => Text::_('NR_CONTINENT_EU'), + 'NA' => Text::_('NR_CONTINENT_NA'), + 'SA' => Text::_('NR_CONTINENT_SA'), + 'OC' => Text::_('NR_CONTINENT_OC'), + 'AN' => Text::_('NR_CONTINENT_AN'), + ]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Controls/Border.php b/plugins/system/nrframework/NRFramework/Controls/Border.php new file mode 100644 index 00000000..f58e8aeb --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Controls/Border.php @@ -0,0 +1,36 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Controls; + +defined('_JEXEC') or die; + +class Border extends Control +{ + protected function generateCSSProperty($value, $unit) + { + // We require all border attributes + if (!isset($value['width']) || !isset($value['style']) || !isset($value['color'])) + { + return; + } + + // Ensure the width is > 0 + if (intval($value['width']) === 0) + { + return; + } + + return $this->property . ':' . implode(' ', [ + $value['width'] . $unit, + $value['style'], + $value['color'] + ]) . ';'; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Controls/Control.php b/plugins/system/nrframework/NRFramework/Controls/Control.php new file mode 100644 index 00000000..1a6dd9e9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Controls/Control.php @@ -0,0 +1,367 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Controls; + +defined('_JEXEC') or die; + +class Control +{ + /** + * The CSS selector related to this control. + * + * @var string|array + */ + protected $selector; + + /** + * The CSS property related to this control. + * + * @var mixed + */ + protected $property; + + /** + * The CSS property used when there are conditions and we fail to use the property, so we override it using this property. + * + * @var mixed + */ + protected $fallback_property; + + /** + * The CSS property used when there are no conditions and we fail to use the property, so we override it using this value. + * + * @var mixed + */ + protected $fallback_value; + + /** + * Some controls may render CSS conditionally, based on the given value. + * + * @var array + */ + protected $values; + + /** + * The control value. + * + * @var mixed + */ + protected $value; + + /** + * The raw control value. + * + * @var mixed + */ + protected $value_raw; + + /** + * The control value unit. + * + * @var string + */ + protected $unit; + + /** + * Exclude specific breakpoints from the control's CSS. + * + * @var array + */ + protected $exclude_breakpoints = []; + + /** + * The existing controls we have parsed so far. + * + * @var array + */ + protected $parsedControls = []; + + /** + * A control may require some conditions to be set. + * + * @var array + */ + protected $conditions = []; + + /** + * Whether to ignore "inherit" values. + * + * @var bool + */ + protected $skip_inherit_value = false; + + /** + * The current breakpoint we are checking against to set CSS. + * + * @var string + */ + protected $current_breakpoint = 'desktop'; + + public function __construct($payload = []) + { + $this->parsedControls = isset($payload['parsedControls']) ? $payload['parsedControls'] : null; + $this->selector = isset($payload['selector']) ? $payload['selector'] : null; + $this->conditions = isset($payload['conditions']) ? $payload['conditions'] : []; + $this->property = isset($payload['property']) ? $payload['property'] : null; + $this->skip_inherit_value = isset($payload['skip_inherit_value']) ? $payload['skip_inherit_value'] : $this->skip_inherit_value; + // The fallback_property is used when we have conditions and we don't have a value, so we override it using this property. + $this->fallback_property = isset($payload['fallback_property']) ? $payload['fallback_property'] : null; + // The fallback_value is used when we don't have conditions and we don't have a value, so we override it using this value. + $this->fallback_value = isset($payload['fallback_value']) ? $payload['fallback_value'] : null; + $this->values = isset($payload['values']) ? $payload['values'] : []; + $this->exclude_breakpoints = isset($payload['exclude_breakpoints']) ? $payload['exclude_breakpoints'] : null; + $this->value = isset($payload['value']['value']) ? $payload['value']['value'] : (isset($payload['value']) ? $payload['value'] : null); + $this->value_raw = isset($payload['value']['value']) ? $payload['value']['value'] : (isset($payload['value']) ? $payload['value'] : null); + $this->unit = isset($payload['value']['unit']) ? $payload['value']['unit'] : (isset($payload['unit']) ? $payload['unit'] : null); + + if (isset($this->value['unit'])) + { + unset($this->value['unit']); + } + } + + public function getCSS() + { + if (!$this->isResponsive()) + { + $this->value = $this->generateCSSProperty($this->value, $this->unit); + return $this->value; + } + + // Prepare value for arrays + foreach ($this->value as $breakpoint => &$value) + { + $this->current_breakpoint = $breakpoint; + + // If this breakpoint is excluded, skip + if (is_array($this->exclude_breakpoints) && count($this->exclude_breakpoints) && in_array($breakpoint, $this->exclude_breakpoints)) + { + unset($this->value[$breakpoint]); + continue; + } + + if (is_scalar($value)) + { + $value = $this->generateCSSProperty($value, $this->unit); + } + else + { + $unit = isset($value['unit']) ? $value['unit'] : $this->unit; + + // Remove "unit" property + if ($unit) + { + unset($value['unit']); + } + + // Remove "linked" property + if (isset($value['linked'])) + { + unset($value['linked']); + } + + $value = isset($value['value']) ? $value['value'] : $value; + + // Remove responsive value if no actual value is set + if (!$value && $value != '0' && $unit != 'auto') + { + unset($this->value[$breakpoint]); + continue; + } + + $value = $this->generateCSSProperty($value, $unit); + } + + if (is_null($value)) + { + continue; + } + } + + return $this->value; + } + + protected function generateCSSProperty($value, $unit) + { + if ($this->shouldSkipPropertyGeneration($value, $unit)) + { + return; + } + + $conditions_pass = $this->conditionsPass($value); + + $conditionsNotMet = (!$conditions_pass && !$this->fallback_property); + $emptyValueWithUnitNotAuto = ($value === '' || is_null($value)) && !$this->fallback_property && $unit !== 'auto'; + + if ($conditionsNotMet || $emptyValueWithUnitNotAuto) + { + return; + } + + if ($this->values) + { + return $this->generateValueCSS($value); + } + + $properties = $this->conditions && !$conditions_pass && $this->fallback_property ? $this->fallback_property : $this->property; + + // If we have no conditions and no value, but we have a fallback value, use it. + if (!$this->conditions && !$value && $this->fallback_value) + { + $properties = $this->fallback_value; + } + + + if (is_array($properties)) + { + return $this->generateArrayPropertyCSS($properties, $conditions_pass, $value, $unit); + } + + if ($value === 'inherit' && $this->skip_inherit_value) + { + return; + } + + return $this->generateSinglePropertyCSS($value, $unit); + } + + private function shouldSkipPropertyGeneration($value, $unit) + { + return !$value && $value != '0' && $unit != 'auto' && !$this->fallback_property; + } + + private function generateValueCSS($value) + { + $css = ''; + + $value = explode(' ', $value); + + foreach ($this->values as $key => $css_value) + { + if (!in_array($key, $value)) + { + continue; + } + + $css .= implode('', $css_value); + } + + return $css; + } + + private function generateArrayPropertyCSS($properties, $conditions_pass, $value, $unit) + { + $css = ''; + + // If the conditions did pass and we do not have a value, do not set any CSS. + if ($conditions_pass && !$value && $unit !== 'auto') + { + return $css; + } + + foreach ($properties as $prop_key => $prop_value) + { + $css_line = $prop_key . ':' . str_replace('%value%', $value . $unit, $prop_value) . ';'; + $css_line = str_replace('%value_raw%', $value , $css_line); + + $css .= $css_line; + } + + return $css; + } + + private function generateSinglePropertyCSS($value, $unit) + { + $value = \NRFramework\Helpers\Controls\Control::findUnitInValue($value); + $val = isset($value['value']) ? $value['value'] : $value; + $unit = isset($value['unit']) && $value['unit'] ? $value['unit'] : $unit; + + return $this->getProperty() . ':' . $val . $unit . ';'; + } + + private function conditionsPass(&$value) + { + if (!$this->conditions) + { + return true; + } + + foreach ($this->conditions as $conditionItem) + { + if (!$this->checkCondition($conditionItem, $value)) + { + return false; + } + } + + return true; + } + + private function checkCondition($conditionItem, &$value) + { + $foundCondition = array_search($conditionItem['property'], array_column($this->parsedControls, 'property')); + + if ($foundCondition === false) + { + return false; + } + + $foundConditionControl = isset($this->parsedControls[$foundCondition]) ? $this->parsedControls[$foundCondition] : false; + + if (!$foundConditionControl) + { + return false; + } + + $conditionValue = $conditionItem['property'] . ':' . $conditionItem['value'] . ';'; + $foundConditionControlValues = $foundConditionControl['control']->getValue(); + $breakpointValue = is_array($foundConditionControlValues) ? ($foundConditionControlValues[$this->current_breakpoint] ?? null) : $foundConditionControlValues; + + if (\NRFramework\Functions::endsWith($breakpointValue, ':inherit;')) + { + $prevBreakpoint = $this->current_breakpoint === 'tablet' ? 'desktop' : 'tablet'; + $breakpointValue = is_array($foundConditionControlValues) ? ($foundConditionControlValues[$prevBreakpoint] ?? null) : $foundConditionControlValues; + + // Also update the value + $value = isset($this->value_raw[$prevBreakpoint]['value']) || isset($this->value_raw[$prevBreakpoint]) ? '' : $value; + + if (\NRFramework\Functions::endsWith($breakpointValue, ':inherit;') && $this->current_breakpoint === 'mobile' && $prevBreakpoint === 'tablet') + { + $breakpointValue = is_array($foundConditionControlValues) ? ($foundConditionControlValues['desktop'] ?? null) : $foundConditionControlValues; + + // Also update the value + $value = isset($this->value_raw['desktop']['value']) || isset($this->value_raw['desktop']) ? '' : $value; + } + } + + return $conditionValue === $breakpointValue; + } + + public function getValue() + { + return $this->value; + } + + public function getSelector() + { + return $this->selector; + } + + public function getProperty() + { + return $this->property; + } + + public function isResponsive() + { + $keys = ['desktop', 'tablet', 'mobile']; + return is_array($this->value) && !empty(array_intersect_key(array_flip($keys), $this->value)); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Controls/ControlFactory.php b/plugins/system/nrframework/NRFramework/Controls/ControlFactory.php new file mode 100644 index 00000000..26b98b52 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Controls/ControlFactory.php @@ -0,0 +1,40 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Controls; + +defined('_JEXEC') or die; + +class ControlFactory +{ + public function createControl($value = []) + { + if (!isset($value['value'])) + { + return; + } + + $type = isset($value['type']) ? $value['type'] : 'Control'; + + switch ($type) + { + case 'Control': + return new \NRFramework\Controls\Control($value); + break; + case 'Border': + return new \NRFramework\Controls\Border($value); + break; + case 'Spacing': + return new \NRFramework\Controls\Spacing($value); + break; + } + + return; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Controls/Controls.php b/plugins/system/nrframework/NRFramework/Controls/Controls.php new file mode 100644 index 00000000..2fa59c63 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Controls/Controls.php @@ -0,0 +1,134 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Controls; + +defined('_JEXEC') or die; + +class Controls +{ + /** + * The Control Factory. + * + * @var ControlFactory + */ + protected $factory; + + /** + * The main selector that will be used for all controls generated CSS. + * + * Each control can override this by setting the "selector" property. + * + * @var string + */ + protected $selector; + + /** + * Define which breakpoints to exclude from the CSS generation. + * + * @var array + */ + protected $exclude_breakpoints = []; + + public function __construct($factory = null, $selector = null, $exclude_breakpoints = []) + { + if (!$factory) + { + $factory = new ControlFactory(); + } + $this->factory = $factory; + + $this->selector = $selector; + $this->exclude_breakpoints = $exclude_breakpoints; + } + + public function generateCSS($controls = []) + { + $cssArray = $this->getCSSArray($controls); + + // Get the final CSS + return \NRFramework\Helpers\Controls\CSS::generateCSS($cssArray); + } + + protected function getCSSArray($controls = []) + { + if (!$controls || !is_array($controls)) + { + return; + } + + $parsedControls = []; + + $cssArray = [ + 'desktop' => [], + 'tablet' => [], + 'mobile' => [] + ]; + + // Get the responsive CSS for each control + foreach ($controls as $control_payload) + { + // Set any breakpoints to exclude when generating CSS + $control_payload['exclude_breakpoints'] = $this->exclude_breakpoints; + + // Set the selector + if (!isset($control_payload['selector'])) + { + $control_payload['selector'] = $this->selector; + } + + $control_payload['parsedControls'] = $parsedControls; + + if (!$control = $this->factory->createControl($control_payload)) + { + continue; + } + + if (!$control_css = $control->getCSS()) + { + continue; + } + + $selector = $control->getSelector(); + + if (isset($control_payload['property'])) + { + $parsedControls[] = [ + 'property' => $control_payload['property'], + 'control' => $control + ]; + } + + if ($control->isResponsive()) + { + foreach ($control_css as $breakpoint => $control_payload) + { + if (is_null($control_payload)) + { + continue; + } + + $cssArray[$breakpoint][] = [ + 'selector' => $selector, + 'css' => $control_payload + ]; + } + } + else + { + $cssArray['desktop'][] = [ + 'selector' => $selector, + 'css' => $control_css + ]; + } + } + + return $cssArray; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Controls/Spacing.php b/plugins/system/nrframework/NRFramework/Controls/Spacing.php new file mode 100644 index 00000000..81c437f7 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Controls/Spacing.php @@ -0,0 +1,27 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Controls; + +defined('_JEXEC') or die; + +class Spacing extends Control +{ + protected function generateCSSProperty($value, $unit) + { + $value = \NRFramework\Helpers\Controls\Control::getCSSValue($value, $unit); + + if ((is_null($value) || $value === '') && $value != '0') + { + return; + } + + return $this->property . ':' . $value . ';'; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Countries.php b/plugins/system/nrframework/NRFramework/Countries.php new file mode 100644 index 00000000..d3a48a94 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Countries.php @@ -0,0 +1,712 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework; + +use Joomla\CMS\Language\LanguageHelper; +use Joomla\CMS\Language\Text; +use NRFramework\Cache; + +defined('_JEXEC') or die('Restricted access'); + +/** + * Helper class to work with country names/codes + */ +class Countries +{ + /** + * Countries List + * + * @deprecated: Use getCountriesData(); + * + * @const array + */ + public static $map = [ + 'AF' => "Afghanistan", + 'AX' => "Aland Islands", + 'AL' => "Albania", + 'DZ' => "Algeria", + 'AS' => "American Samoa", + 'AD' => "Andorra", + 'AO' => "Angola", + 'AI' => "Anguilla", + 'AQ' => "Antarctica", + 'AG' => "Antigua and Barbuda", + 'AR' => "Argentina", + 'AM' => "Armenia", + 'AW' => "Aruba", + 'AU' => "Australia", + 'AT' => "Austria", + 'AZ' => "Azerbaijan", + 'BS' => "Bahamas", + 'BH' => "Bahrain", + 'BD' => "Bangladesh", + 'BB' => "Barbados", + 'BY' => "Belarus", + 'BE' => "Belgium", + 'BZ' => "Belize", + 'BJ' => "Benin", + 'BM' => "Bermuda", + 'BQ-BO' => "Bonaire", + 'BQ-SA' => "Saba", + 'BQ-SE' => "Sint Eustatius", + 'BT' => "Bhutan", + 'BO' => "Bolivia", + 'BA' => "Bosnia and Herzegovina", + 'BW' => "Botswana", + 'BV' => "Bouvet Island", + 'BR' => "Brazil", + 'IO' => "British Indian Ocean Territory", + 'BN' => "Brunei Darussalam", + 'BG' => "Bulgaria", + 'BF' => "Burkina Faso", + 'BI' => "Burundi", + 'KH' => "Cambodia", + 'CM' => "Cameroon", + 'CA' => "Canada", + 'CV' => "Cape Verde", + 'KY' => "Cayman Islands", + 'CF' => "Central African Republic", + 'TD' => "Chad", + 'CL' => "Chile", + 'CN' => "China", + 'CX' => "Christmas Island", + 'CC' => "Cocos (Keeling) Islands", + 'CO' => "Colombia", + 'KM' => "Comoros", + 'CG' => "Congo", + 'CD' => "Congo, The Democratic Republic of the", + 'CK' => "Cook Islands", + 'CR' => "Costa Rica", + 'CI' => "Cote d'Ivoire", + 'HR' => "Croatia", + 'CU' => "Cuba", + 'CW' => "Curaçao", + 'CY' => "Cyprus", + 'CZ' => "Czech Republic", + 'DK' => "Denmark", + 'DJ' => "Djibouti", + 'DM' => "Dominica", + 'DO' => "Dominican Republic", + 'EC' => "Ecuador", + 'EG' => "Egypt", + 'SV' => "El Salvador", + 'GQ' => "Equatorial Guinea", + 'ER' => "Eritrea", + 'EE' => "Estonia", + 'ET' => "Ethiopia", + 'FK' => "Falkland Islands (Malvinas)", + 'FO' => "Faroe Islands", + 'FJ' => "Fiji", + 'FI' => "Finland", + 'FR' => "France", + 'GF' => "French Guiana", + 'PF' => "French Polynesia", + 'TF' => "French Southern Territories", + 'GA' => "Gabon", + 'GM' => "Gambia", + 'GE' => "Georgia", + 'DE' => "Germany", + 'GH' => "Ghana", + 'GI' => "Gibraltar", + 'GR' => "Greece", + 'GL' => "Greenland", + 'GD' => "Grenada", + 'GP' => "Guadeloupe", + 'GU' => "Guam", + 'GT' => "Guatemala", + 'GG' => "Guernsey", + 'GN' => "Guinea", + 'GW' => "Guinea-Bissau", + 'GY' => "Guyana", + 'HT' => "Haiti", + 'HM' => "Heard Island and McDonald Islands", + 'VA' => "Holy See (Vatican City State)", + 'HN' => "Honduras", + 'HK' => "Hong Kong", + 'HU' => "Hungary", + 'IS' => "Iceland", + 'IN' => "India", + 'ID' => "Indonesia", + 'IR' => "Iran, Islamic Republic of", + 'IQ' => "Iraq", + 'IE' => "Ireland", + 'IM' => "Isle of Man", + 'IL' => "Israel", + 'IT' => "Italy", + 'JM' => "Jamaica", + 'JP' => "Japan", + 'JE' => "Jersey", + 'JO' => "Jordan", + 'KZ' => "Kazakhstan", + 'KE' => "Kenya", + 'KI' => "Kiribati", + 'KP' => "Korea, Democratic People's Republic of", + 'KR' => "Korea, Republic of", + 'KW' => "Kuwait", + 'KG' => "Kyrgyzstan", + 'LA' => "Lao People's Democratic Republic", + 'LV' => "Latvia", + 'LB' => "Lebanon", + 'LS' => "Lesotho", + 'LR' => "Liberia", + 'LY' => "Libyan Arab Jamahiriya", + 'LI' => "Liechtenstein", + 'LT' => "Lithuania", + 'LU' => "Luxembourg", + 'MO' => "Macao", + 'MK' => "Macedonia", + 'MG' => "Madagascar", + 'MW' => "Malawi", + 'MY' => "Malaysia", + 'MV' => "Maldives", + 'ML' => "Mali", + 'MT' => "Malta", + 'MH' => "Marshall Islands", + 'MQ' => "Martinique", + 'MR' => "Mauritania", + 'MU' => "Mauritius", + 'YT' => "Mayotte", + 'MX' => "Mexico", + 'FM' => "Micronesia, Federated States of", + 'MD' => "Moldova, Republic of", + 'MC' => "Monaco", + 'MN' => "Mongolia", + 'ME' => "Montenegro", + 'MS' => "Montserrat", + 'MA' => "Morocco", + 'MZ' => "Mozambique", + 'MM' => "Myanmar", + 'NA' => "Namibia", + 'NR' => "Nauru", + 'NP' => "Nepal", + 'NL' => "Netherlands", + 'AN' => "Netherlands Antilles", + 'NC' => "New Caledonia", + 'NZ' => "New Zealand", + 'NI' => "Nicaragua", + 'NE' => "Niger", + 'NG' => "Nigeria", + 'NU' => "Niue", + 'NF' => "Norfolk Island", + 'NM' => "North Macedonia", + 'MP' => "Northern Mariana Islands", + 'NO' => "Norway", + 'OM' => "Oman", + 'PK' => "Pakistan", + 'PW' => "Palau", + 'PS' => "Palestinian Territory", + 'PA' => "Panama", + 'PG' => "Papua New Guinea", + 'PY' => "Paraguay", + 'PE' => "Peru", + 'PH' => "Philippines", + 'PN' => "Pitcairn", + 'PL' => "Poland", + 'PT' => "Portugal", + 'PR' => "Puerto Rico", + 'QA' => "Qatar", + 'RE' => "Reunion", + 'RO' => "Romania", + 'RU' => "Russian Federation", + 'RW' => "Rwanda", + 'SH' => "Saint Helena", + 'KN' => "Saint Kitts and Nevis", + 'LC' => "Saint Lucia", + 'PM' => "Saint Pierre and Miquelon", + 'VC' => "Saint Vincent and the Grenadines", + 'WS' => "Samoa", + 'SM' => "San Marino", + 'ST' => "Sao Tome and Principe", + 'SA' => "Saudi Arabia", + 'SN' => "Senegal", + 'RS' => "Serbia", + 'SC' => "Seychelles", + 'SL' => "Sierra Leone", + 'SG' => "Singapore", + 'SK' => "Slovakia", + 'SI' => "Slovenia", + 'SB' => "Solomon Islands", + 'SO' => "Somalia", + 'ZA' => "South Africa", + 'GS' => "South Georgia and the South Sandwich Islands", + 'ES' => "Spain", + 'LK' => "Sri Lanka", + 'SD' => "Sudan", + 'SS' => "South Sudan", + 'SR' => "Suriname", + 'SJ' => "Svalbard and Jan Mayen", + 'SZ' => "Swaziland", + 'SE' => "Sweden", + 'CH' => "Switzerland", + 'SY' => "Syrian Arab Republic", + 'TW' => "Taiwan", + 'TJ' => "Tajikistan", + 'TZ' => "Tanzania, United Republic of", + 'TH' => "Thailand", + 'TL' => "Timor-Leste", + 'TG' => "Togo", + 'TK' => "Tokelau", + 'TO' => "Tonga", + 'TT' => "Trinidad and Tobago", + 'TN' => "Tunisia", + 'TR' => "Turkey", + 'TM' => "Turkmenistan", + 'TC' => "Turks and Caicos Islands", + 'TV' => "Tuvalu", + 'UG' => "Uganda", + 'UA' => "Ukraine", + 'AE' => "United Arab Emirates", + 'GB' => "United Kingdom", + 'US' => "United States", + 'UM' => "United States Minor Outlying Islands", + 'UY' => "Uruguay", + 'UZ' => "Uzbekistan", + 'VU' => "Vanuatu", + 'VE' => "Venezuela", + 'VN' => "Vietnam", + 'VG' => "Virgin Islands, British", + 'VI' => "Virgin Islands, U.S.", + 'WF' => "Wallis and Futuna", + 'EH' => "Western Sahara", + 'YE' => "Yemen", + 'ZM' => "Zambia", + 'ZW' => "Zimbabwe", + ]; + + /** + * Get information for given country + * + * @param string $countryCode + * + * @return array An assosiative array with country information + */ + public static function getCountry($countryCode) + { + $countries = self::getCountriesData(); + $countryCode = \strtoupper($countryCode); + + if (!isset($countries[$countryCode])) + { + return; + } + + return array_merge($countries[$countryCode], [ + 'code' => $countryCode + ]); + } + + /** + * Attemp to convert a Country Code to a Country Name + * + * @param string $country_code The country code + * + * @return mixed String on success, Null on failure + */ + public static function toCountryName($country_code) + { + $countries = self::getCountriesList(); + + if (isset($countries[$country_code])) + { + return $countries[$country_code]; + } + } + + /** + * Attemp to convert a Country name to a Country code + * + * @param string $subject The country name + * + * @return mixed String on success, Null on failure + */ + public static function toCountryCode($subject) + { + $subject = strtolower($subject); + + $cacheHash = md5('toCountryCode' . $subject); + if (Cache::has($cacheHash)) + { + return Cache::get($cacheHash); + } + + $countries = array_change_key_case(self::getCountriesList()); + + // Sanity check. Check first if we have a country code already. + if (array_key_exists($subject, $countries)) + { + return strtoupper($subject); + } + + // Let's find the country code in the list. + foreach ($countries as $country_code => $country_name) + { + if (strtolower($country_name) == $subject) + { + return strtoupper($country_code); + } + } + + // Country code still not found. Probably we have a non-english country name. + // Let's load one by one all the language files and try to find it there. + $langFiles = \Joomla\Filesystem\Folder::files(JPATH_PLUGINS . '/system/nrframework/language', '.ini', 1, true); + + foreach ($langFiles as $langFile) + { + $strings = LanguageHelper::parseIniFile($langFile); + + foreach ($strings as $key => $label) + { + if (strpos($key, 'NR_COUNTRY_') === false) + { + continue; + } + + if (strtolower($label) !== $subject) + { + continue; + } + + // Found! + return Cache::set($cacheHash, str_replace('NR_COUNTRY_', '', $key)); + } + } + } + + /** + * Convert a Country Name to Country Code + * + * @param string $country The country name + * + * @return string|void + */ + public static function getCode($country) + { + $country = strtolower($country); + + foreach (self::getCountriesList() as $key => $value) + { + if (strtolower($value) == $country) + { + return $key; + } + } + } + + /** + * Returns translatable countries list + * + * @return array + */ + public static function getCountriesList() + { + $countries = []; + + foreach (self::getCountriesData() as $key => $country) + { + $countries[$key] = $country['name']; + } + + return $countries; + } + + /** + * Returns a country's calling code. + * + * @param string $country_code + * + * @return string + */ + public static function getCallingCodeByCountryCode($country_code = '') + { + if (!$country_code) + { + return; + } + + $countries = self::getCountriesData(); + + if (!isset($countries[$country_code])) + { + return; + } + + return $countries[$country_code]['calling_code']; + } + + /** + * Holds the following data for each country: + * - Name + * - Code + * - Calling Code + * - Currency Code + * - Curency Name + * - Currency Symbol + * + * @return array + */ + public static function getCountriesData() + { + $list = [ + 'AF' => [ 'name' => Text::_('NR_COUNTRY_AF'), 'calling_code' => '93', 'currency_code' => 'AFN', 'currency_name' => 'Afghan Afghani', 'currency_symbol' => '؋' ], + 'AX' => [ 'name' => Text::_('NR_COUNTRY_AX'), 'calling_code' => '358', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'AL' => [ 'name' => Text::_('NR_COUNTRY_AL'), 'calling_code' => '355', 'currency_code' => 'ALL', 'currency_name' => 'Lek', 'currency_symbol' => 'Lek' ], + 'DZ' => [ 'name' => Text::_('NR_COUNTRY_DZ'), 'calling_code' => '213', 'currency_code' => 'DZD', 'currency_name' => 'Dinar', 'currency_symbol' => 'دج' ], + 'AS' => [ 'name' => Text::_('NR_COUNTRY_AS'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'AD' => [ 'name' => Text::_('NR_COUNTRY_AD'), 'calling_code' => '376', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'AO' => [ 'name' => Text::_('NR_COUNTRY_AO'), 'calling_code' => '244', 'currency_code' => 'AOA', 'currency_name' => 'Kwanza', 'currency_symbol' => 'Kz' ], + 'AI' => [ 'name' => Text::_('NR_COUNTRY_AI'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'AQ' => [ 'name' => Text::_('NR_COUNTRY_AQ'), 'calling_code' => '672', 'currency_code' => '', 'currency_name' => '', 'currency_symbol' => '' ], + 'AG' => [ 'name' => Text::_('NR_COUNTRY_AG'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'AR' => [ 'name' => Text::_('NR_COUNTRY_AR'), 'calling_code' => '54', 'currency_code' => 'ARS', 'currency_name' => 'Peso', 'currency_symbol' => '$' ], + 'AM' => [ 'name' => Text::_('NR_COUNTRY_AM'), 'calling_code' => '374', 'currency_code' => 'AMD', 'currency_name' => 'Dram', 'currency_symbol' => '֏' ], + 'AW' => [ 'name' => Text::_('NR_COUNTRY_AW'), 'calling_code' => '297', 'currency_code' => 'AWG', 'currency_name' => 'Guilder', 'currency_symbol' => 'ƒ' ], + 'AU' => [ 'name' => Text::_('NR_COUNTRY_AU'), 'calling_code' => '61', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'AT' => [ 'name' => Text::_('NR_COUNTRY_AT'), 'calling_code' => '43', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'AZ' => [ 'name' => Text::_('NR_COUNTRY_AZ'), 'calling_code' => '994', 'currency_code' => 'AZN', 'currency_name' => 'Manat', 'currency_symbol' => 'ман' ], + 'BS' => [ 'name' => Text::_('NR_COUNTRY_BS'), 'calling_code' => '1', 'currency_code' => 'BSD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BH' => [ 'name' => Text::_('NR_COUNTRY_BH'), 'calling_code' => '973', 'currency_code' => 'BHD', 'currency_name' => 'Dinar', 'currency_symbol' => 'د.ب' ], + 'BD' => [ 'name' => Text::_('NR_COUNTRY_BD'), 'calling_code' => '880', 'currency_code' => 'BDT', 'currency_name' => 'Taka', 'currency_symbol' => '৳' ], + 'BB' => [ 'name' => Text::_('NR_COUNTRY_BB'), 'calling_code' => '1', 'currency_code' => 'BBD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BY' => [ 'name' => Text::_('NR_COUNTRY_BY'), 'calling_code' => '375', 'currency_code' => 'BYR', 'currency_name' => 'Ruble', 'currency_symbol' => 'p.' ], + 'BE' => [ 'name' => Text::_('NR_COUNTRY_BE'), 'calling_code' => '32', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'BZ' => [ 'name' => Text::_('NR_COUNTRY_BZ'), 'calling_code' => '501', 'currency_code' => 'BZD', 'currency_name' => 'Dollar', 'currency_symbol' => 'BZ$' ], + 'BJ' => [ 'name' => Text::_('NR_COUNTRY_BJ'), 'calling_code' => '229', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'BM' => [ 'name' => Text::_('NR_COUNTRY_BM'), 'calling_code' => '1', 'currency_code' => 'BMD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BQ-BO' => [ 'name' => Text::_('NR_COUNTRY_BQ_BO'), 'calling_code' => '599', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BQ-SA' => [ 'name' => Text::_('NR_COUNTRY_BQ_SA'), 'calling_code' => '599', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BQ-SE' => [ 'name' => Text::_('NR_COUNTRY_BQ_SE'), 'calling_code' => '599', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BT' => [ 'name' => Text::_('NR_COUNTRY_BT'), 'calling_code' => '975', 'currency_code' => 'BTN', 'currency_name' => 'Ngultrum', 'currency_symbol' => 'Nu.' ], + 'BO' => [ 'name' => Text::_('NR_COUNTRY_BO'), 'calling_code' => '591', 'currency_code' => 'BOB', 'currency_name' => 'Boliviano', 'currency_symbol' => '$b' ], + 'BA' => [ 'name' => Text::_('NR_COUNTRY_BA'), 'calling_code' => '387', 'currency_code' => 'BAM', 'currency_name' => 'Marka', 'currency_symbol' => 'KM' ], + 'BW' => [ 'name' => Text::_('NR_COUNTRY_BW'), 'calling_code' => '267', 'currency_code' => 'BWP', 'currency_name' => 'Pula', 'currency_symbol' => 'P' ], + 'BV' => [ 'name' => Text::_('NR_COUNTRY_BV'), 'calling_code' => '47', 'currency_code' => 'NOK', 'currency_name' => 'Krone', 'currency_symbol' => 'kr' ], + 'BR' => [ 'name' => Text::_('NR_COUNTRY_BR'), 'calling_code' => '55', 'currency_code' => 'BRL', 'currency_name' => 'Real', 'currency_symbol' => 'R$' ], + 'IO' => [ 'name' => Text::_('NR_COUNTRY_IO'), 'calling_code' => '246', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'VG' => [ 'name' => Text::_('NR_COUNTRY_VG'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BN' => [ 'name' => Text::_('NR_COUNTRY_BN'), 'calling_code' => '673', 'currency_code' => 'BND', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'BG' => [ 'name' => Text::_('NR_COUNTRY_BG'), 'calling_code' => '359', 'currency_code' => 'BGN', 'currency_name' => 'Lev', 'currency_symbol' => 'лв' ], + 'BF' => [ 'name' => Text::_('NR_COUNTRY_BF'), 'calling_code' => '226', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'BI' => [ 'name' => Text::_('NR_COUNTRY_BI'), 'calling_code' => '257', 'currency_code' => 'BIF', 'currency_name' => 'Franc', 'currency_symbol' => 'FBu' ], + 'KH' => [ 'name' => Text::_('NR_COUNTRY_KH'), 'calling_code' => '855', 'currency_code' => 'KHR', 'currency_name' => 'Riels', 'currency_symbol' => '៛' ], + 'CM' => [ 'name' => Text::_('NR_COUNTRY_CM'), 'calling_code' => '237', 'currency_code' => 'XAF', 'currency_name' => 'Franc', 'currency_symbol' => 'FCF' ], + 'CA' => [ 'name' => Text::_('NR_COUNTRY_CA'), 'calling_code' => '1', 'currency_code' => 'CAD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'CV' => [ 'name' => Text::_('NR_COUNTRY_CV'), 'calling_code' => '238', 'currency_code' => 'CVE', 'currency_name' => 'Escudo', 'currency_symbol' => '$' ], + 'KY' => [ 'name' => Text::_('NR_COUNTRY_KY'), 'calling_code' => '1', 'currency_code' => 'KYD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'CF' => [ 'name' => Text::_('NR_COUNTRY_CF'), 'calling_code' => '236', 'currency_code' => 'XAF', 'currency_name' => 'Franc', 'currency_symbol' => 'FCF' ], + 'TD' => [ 'name' => Text::_('NR_COUNTRY_TD'), 'calling_code' => '235', 'currency_code' => 'XAF', 'currency_name' => 'Franc', 'currency_symbol' => 'FCFA' ], + 'CL' => [ 'name' => Text::_('NR_COUNTRY_CL'), 'calling_code' => '56', 'currency_code' => 'CLP', 'currency_name' => 'Peso', 'currency_symbol' => '$' ], + 'CN' => [ 'name' => Text::_('NR_COUNTRY_CN'), 'calling_code' => '86', 'currency_code' => 'CNY', 'currency_name' => 'YuanRenminbi', 'currency_symbol' => '¥' ], + 'CX' => [ 'name' => Text::_('NR_COUNTRY_CX'), 'calling_code' => '61', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'CC' => [ 'name' => Text::_('NR_COUNTRY_CC'), 'calling_code' => '61', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'CO' => [ 'name' => Text::_('NR_COUNTRY_CO'), 'calling_code' => '57', 'currency_code' => 'COP', 'currency_name' => 'Peso', 'currency_symbol' => '$' ], + 'KM' => [ 'name' => Text::_('NR_COUNTRY_KM'), 'calling_code' => '269', 'currency_code' => 'KMF', 'currency_name' => 'Franc', 'currency_symbol' => 'CF' ], + 'CK' => [ 'name' => Text::_('NR_COUNTRY_CK'), 'calling_code' => '682', 'currency_code' => 'NZD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'CR' => [ 'name' => Text::_('NR_COUNTRY_CR'), 'calling_code' => '506', 'currency_code' => 'CRC', 'currency_name' => 'Colon', 'currency_symbol' => '₡' ], + 'HR' => [ 'name' => Text::_('NR_COUNTRY_HR'), 'calling_code' => '385', 'currency_code' => 'HRK', 'currency_name' => 'Kuna', 'currency_symbol' => 'kn' ], + 'CU' => [ 'name' => Text::_('NR_COUNTRY_CU'), 'calling_code' => '53', 'currency_code' => 'CUP', 'currency_name' => 'Peso', 'currency_symbol' => '₱' ], + 'CW' => [ 'name' => Text::_('NR_COUNTRY_CW'), 'calling_code' => '599', 'currency_code' => 'ANG', 'currency_name' => 'Guilder', 'currency_symbol' => 'ƒ' ], + 'CY' => [ 'name' => Text::_('NR_COUNTRY_CY'), 'calling_code' => '357', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'CZ' => [ 'name' => Text::_('NR_COUNTRY_CZ'), 'calling_code' => '420', 'currency_code' => 'CZK', 'currency_name' => 'Koruna', 'currency_symbol' => 'Kč' ], + 'CD' => [ 'name' => Text::_('NR_COUNTRY_CD'), 'calling_code' => '243', 'currency_code' => 'CDF', 'currency_name' => 'Franc', 'currency_symbol' => 'FC' ], + 'DK' => [ 'name' => Text::_('NR_COUNTRY_DK'), 'calling_code' => '45', 'currency_code' => 'DKK', 'currency_name' => 'Krone', 'currency_symbol' => 'kr' ], + 'DJ' => [ 'name' => Text::_('NR_COUNTRY_DJ'), 'calling_code' => '253', 'currency_code' => 'DJF', 'currency_name' => 'Franc', 'currency_symbol' => 'Fdj' ], + 'DM' => [ 'name' => Text::_('NR_COUNTRY_DM'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'DO' => [ 'name' => Text::_('NR_COUNTRY_DO'), 'calling_code' => '1', 'currency_code' => 'DOP', 'currency_name' => 'Peso', 'currency_symbol' => 'RD$' ], + 'TL' => [ 'name' => Text::_('NR_COUNTRY_TL'), 'calling_code' => '670', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'EC' => [ 'name' => Text::_('NR_COUNTRY_EC'), 'calling_code' => '593', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'EG' => [ 'name' => Text::_('NR_COUNTRY_EG'), 'calling_code' => '20', 'currency_code' => 'EGP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'SV' => [ 'name' => Text::_('NR_COUNTRY_SV'), 'calling_code' => '503', 'currency_code' => 'SVC', 'currency_name' => 'Colone', 'currency_symbol' => '$' ], + 'GQ' => [ 'name' => Text::_('NR_COUNTRY_GQ'), 'calling_code' => '240', 'currency_code' => 'XAF', 'currency_name' => 'Franc', 'currency_symbol' => 'FCF' ], + 'ER' => [ 'name' => Text::_('NR_COUNTRY_ER'), 'calling_code' => '291', 'currency_code' => 'ERN', 'currency_name' => 'Nakfa', 'currency_symbol' => 'Nfk' ], + 'EE' => [ 'name' => Text::_('NR_COUNTRY_EE'), 'calling_code' => '372', 'currency_code' => 'EEK', 'currency_name' => 'Kroon', 'currency_symbol' => 'kr' ], + 'ET' => [ 'name' => Text::_('NR_COUNTRY_ET'), 'calling_code' => '251', 'currency_code' => 'ETB', 'currency_name' => 'Birr', 'currency_symbol' => 'Br' ], + 'FK' => [ 'name' => Text::_('NR_COUNTRY_FK'), 'calling_code' => '500', 'currency_code' => 'FKP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'FO' => [ 'name' => Text::_('NR_COUNTRY_FO'), 'calling_code' => '298', 'currency_code' => 'DKK', 'currency_name' => 'Krone', 'currency_symbol' => 'kr' ], + 'FJ' => [ 'name' => Text::_('NR_COUNTRY_FJ'), 'calling_code' => '679', 'currency_code' => 'FJD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'FI' => [ 'name' => Text::_('NR_COUNTRY_FI'), 'calling_code' => '358', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'FR' => [ 'name' => Text::_('NR_COUNTRY_FR'), 'calling_code' => '33', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'GF' => [ 'name' => Text::_('NR_COUNTRY_GF'), 'calling_code' => '594', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'PF' => [ 'name' => Text::_('NR_COUNTRY_PF'), 'calling_code' => '689', 'currency_code' => 'XPF', 'currency_name' => 'Franc', 'currency_symbol' => 'F' ], + 'TF' => [ 'name' => Text::_('NR_COUNTRY_TF'), 'calling_code' => '262', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'GA' => [ 'name' => Text::_('NR_COUNTRY_GA'), 'calling_code' => '241', 'currency_code' => 'XAF', 'currency_name' => 'Franc', 'currency_symbol' => 'FCF' ], + 'GM' => [ 'name' => Text::_('NR_COUNTRY_GM'), 'calling_code' => '220', 'currency_code' => 'GMD', 'currency_name' => 'Dalasi', 'currency_symbol' => 'D' ], + 'GE' => [ 'name' => Text::_('NR_COUNTRY_GE'), 'calling_code' => '995', 'currency_code' => 'GEL', 'currency_name' => 'Lari', 'currency_symbol' => '₾' ], + 'DE' => [ 'name' => Text::_('NR_COUNTRY_DE'), 'calling_code' => '49', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'GH' => [ 'name' => Text::_('NR_COUNTRY_GH'), 'calling_code' => '233', 'currency_code' => 'GHC', 'currency_name' => 'Cedi', 'currency_symbol' => '¢' ], + 'GI' => [ 'name' => Text::_('NR_COUNTRY_GI'), 'calling_code' => '350', 'currency_code' => 'GIP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'GR' => [ 'name' => Text::_('NR_COUNTRY_GR'), 'calling_code' => '30', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'GL' => [ 'name' => Text::_('NR_COUNTRY_GL'), 'calling_code' => '299', 'currency_code' => 'DKK', 'currency_name' => 'Krone', 'currency_symbol' => 'kr' ], + 'GD' => [ 'name' => Text::_('NR_COUNTRY_GD'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'GP' => [ 'name' => Text::_('NR_COUNTRY_GP'), 'calling_code' => '590', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'GU' => [ 'name' => Text::_('NR_COUNTRY_GU'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'GT' => [ 'name' => Text::_('NR_COUNTRY_GT'), 'calling_code' => '502', 'currency_code' => 'GTQ', 'currency_name' => 'Quetzal', 'currency_symbol' => 'Q' ], + 'GN' => [ 'name' => Text::_('NR_COUNTRY_GN'), 'calling_code' => '224', 'currency_code' => 'GNF', 'currency_name' => 'Franc', 'currency_symbol' => 'FG' ], + 'GW' => [ 'name' => Text::_('NR_COUNTRY_GW'), 'calling_code' => '245', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'GY' => [ 'name' => Text::_('NR_COUNTRY_GY'), 'calling_code' => '592', 'currency_code' => 'GYD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'HT' => [ 'name' => Text::_('NR_COUNTRY_HT'), 'calling_code' => '509', 'currency_code' => 'HTG', 'currency_name' => 'Gourde', 'currency_symbol' => 'G' ], + 'HM' => [ 'name' => Text::_('NR_COUNTRY_HM'), 'calling_code' => '0', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'HN' => [ 'name' => Text::_('NR_COUNTRY_HN'), 'calling_code' => '504', 'currency_code' => 'HNL', 'currency_name' => 'Lempira', 'currency_symbol' => 'L' ], + 'HK' => [ 'name' => Text::_('NR_COUNTRY_HK'), 'calling_code' => '852', 'currency_code' => 'HKD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'HU' => [ 'name' => Text::_('NR_COUNTRY_HU'), 'calling_code' => '36', 'currency_code' => 'HUF', 'currency_name' => 'Forint', 'currency_symbol' => 'Ft' ], + 'IS' => [ 'name' => Text::_('NR_COUNTRY_IS'), 'calling_code' => '354', 'currency_code' => 'ISK', 'currency_name' => 'Krona', 'currency_symbol' => 'kr' ], + 'IN' => [ 'name' => Text::_('NR_COUNTRY_IN'), 'calling_code' => '91', 'currency_code' => 'INR', 'currency_name' => 'Rupee', 'currency_symbol' => '₹' ], + 'ID' => [ 'name' => Text::_('NR_COUNTRY_ID'), 'calling_code' => '62', 'currency_code' => 'IDR', 'currency_name' => 'Rupiah', 'currency_symbol' => 'Rp' ], + 'IR' => [ 'name' => Text::_('NR_COUNTRY_IR'), 'calling_code' => '98', 'currency_code' => 'IRR', 'currency_name' => 'Rial', 'currency_symbol' => '﷼' ], + 'IQ' => [ 'name' => Text::_('NR_COUNTRY_IQ'), 'calling_code' => '964', 'currency_code' => 'IQD', 'currency_name' => 'Dinar', 'currency_symbol' => 'د.ع' ], + 'IE' => [ 'name' => Text::_('NR_COUNTRY_IE'), 'calling_code' => '353', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'IM' => [ 'name' => Text::_('NR_COUNTRY_IM'), 'calling_code' => '44', 'currency_code' => 'GBP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'IL' => [ 'name' => Text::_('NR_COUNTRY_IL'), 'calling_code' => '972', 'currency_code' => 'ILS', 'currency_name' => 'Shekel', 'currency_symbol' => '₪' ], + 'IT' => [ 'name' => Text::_('NR_COUNTRY_IT'), 'calling_code' => '39', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'CI' => [ 'name' => Text::_('NR_COUNTRY_CI'), 'calling_code' => '225', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'JM' => [ 'name' => Text::_('NR_COUNTRY_JM'), 'calling_code' => '1', 'currency_code' => 'JMD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'JP' => [ 'name' => Text::_('NR_COUNTRY_JP'), 'calling_code' => '81', 'currency_code' => 'JPY', 'currency_name' => 'Yen', 'currency_symbol' => '¥' ], + 'JO' => [ 'name' => Text::_('NR_COUNTRY_JO'), 'calling_code' => '962', 'currency_code' => 'JOD', 'currency_name' => 'Dinar', 'currency_symbol' => 'د.أ' ], + 'KZ' => [ 'name' => Text::_('NR_COUNTRY_KZ'), 'calling_code' => '7', 'currency_code' => 'KZT', 'currency_name' => 'Tenge', 'currency_symbol' => 'лв' ], + 'KE' => [ 'name' => Text::_('NR_COUNTRY_KE'), 'calling_code' => '254', 'currency_code' => 'KES', 'currency_name' => 'Shilling', 'currency_symbol' => 'KSh' ], + 'KI' => [ 'name' => Text::_('NR_COUNTRY_KI'), 'calling_code' => '686', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'KW' => [ 'name' => Text::_('NR_COUNTRY_KW'), 'calling_code' => '965', 'currency_code' => 'KWD', 'currency_name' => 'Dinar', 'currency_symbol' => 'د.ك' ], + 'KG' => [ 'name' => Text::_('NR_COUNTRY_KG'), 'calling_code' => '996', 'currency_code' => 'KGS', 'currency_name' => 'Som', 'currency_symbol' => 'лв' ], + 'LA' => [ 'name' => Text::_('NR_COUNTRY_LA'), 'calling_code' => '856', 'currency_code' => 'LAK', 'currency_name' => 'Kip', 'currency_symbol' => '₭' ], + 'LV' => [ 'name' => Text::_('NR_COUNTRY_LV'), 'calling_code' => '371', 'currency_code' => 'LVL', 'currency_name' => 'Lat', 'currency_symbol' => 'Ls' ], + 'LB' => [ 'name' => Text::_('NR_COUNTRY_LB'), 'calling_code' => '961', 'currency_code' => 'LBP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'LS' => [ 'name' => Text::_('NR_COUNTRY_LS'), 'calling_code' => '266', 'currency_code' => 'LSL', 'currency_name' => 'Loti', 'currency_symbol' => 'L' ], + 'LR' => [ 'name' => Text::_('NR_COUNTRY_LR'), 'calling_code' => '231', 'currency_code' => 'LRD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'LY' => [ 'name' => Text::_('NR_COUNTRY_LY'), 'calling_code' => '218', 'currency_code' => 'LYD', 'currency_name' => 'Dinar', 'currency_symbol' => 'ل.د' ], + 'LI' => [ 'name' => Text::_('NR_COUNTRY_LI'), 'calling_code' => '423', 'currency_code' => 'CHF', 'currency_name' => 'Franc', 'currency_symbol' => 'CHF' ], + 'LT' => [ 'name' => Text::_('NR_COUNTRY_LT'), 'calling_code' => '370', 'currency_code' => 'LTL', 'currency_name' => 'Litas', 'currency_symbol' => 'Lt' ], + 'LU' => [ 'name' => Text::_('NR_COUNTRY_LU'), 'calling_code' => '352', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'MO' => [ 'name' => Text::_('NR_COUNTRY_MO'), 'calling_code' => '853', 'currency_code' => 'MOP', 'currency_name' => 'Pataca', 'currency_symbol' => 'MOP' ], + 'MK' => [ 'name' => Text::_('NR_COUNTRY_MK'), 'calling_code' => '389', 'currency_code' => 'MKD', 'currency_name' => 'Denar', 'currency_symbol' => 'ден' ], + 'MG' => [ 'name' => Text::_('NR_COUNTRY_MG'), 'calling_code' => '261', 'currency_code' => 'MGA', 'currency_name' => 'Ariary', 'currency_symbol' => 'Ar' ], + 'MW' => [ 'name' => Text::_('NR_COUNTRY_MW'), 'calling_code' => '265', 'currency_code' => 'MWK', 'currency_name' => 'Kwacha', 'currency_symbol' => 'MK' ], + 'MY' => [ 'name' => Text::_('NR_COUNTRY_MY'), 'calling_code' => '60', 'currency_code' => 'MYR', 'currency_name' => 'Ringgit', 'currency_symbol' => 'RM' ], + 'MV' => [ 'name' => Text::_('NR_COUNTRY_MV'), 'calling_code' => '960', 'currency_code' => 'MVR', 'currency_name' => 'Rufiyaa', 'currency_symbol' => 'Rf' ], + 'ML' => [ 'name' => Text::_('NR_COUNTRY_ML'), 'calling_code' => '223', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'MT' => [ 'name' => Text::_('NR_COUNTRY_MT'), 'calling_code' => '356', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'MH' => [ 'name' => Text::_('NR_COUNTRY_MH'), 'calling_code' => '692', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'MQ' => [ 'name' => Text::_('NR_COUNTRY_MQ'), 'calling_code' => '596', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'MR' => [ 'name' => Text::_('NR_COUNTRY_MR'), 'calling_code' => '222', 'currency_code' => 'MRO', 'currency_name' => 'Ouguiya', 'currency_symbol' => 'UM' ], + 'MU' => [ 'name' => Text::_('NR_COUNTRY_MU'), 'calling_code' => '230', 'currency_code' => 'MUR', 'currency_name' => 'Rupee', 'currency_symbol' => '₨' ], + 'YT' => [ 'name' => Text::_('NR_COUNTRY_YT'), 'calling_code' => '262', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'MX' => [ 'name' => Text::_('NR_COUNTRY_MX'), 'calling_code' => '52', 'currency_code' => 'MXN', 'currency_name' => 'Peso', 'currency_symbol' => '$' ], + 'FM' => [ 'name' => Text::_('NR_COUNTRY_FM'), 'calling_code' => '691', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'MD' => [ 'name' => Text::_('NR_COUNTRY_MD'), 'calling_code' => '373', 'currency_code' => 'MDL', 'currency_name' => 'Leu', 'currency_symbol' => 'L' ], + 'MC' => [ 'name' => Text::_('NR_COUNTRY_MC'), 'calling_code' => '377', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'MN' => [ 'name' => Text::_('NR_COUNTRY_MN'), 'calling_code' => '976', 'currency_code' => 'MNT', 'currency_name' => 'Tugrik', 'currency_symbol' => '₮' ], + 'ME' => [ 'name' => Text::_('NR_COUNTRY_ME'), 'calling_code' => '382', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'MS' => [ 'name' => Text::_('NR_COUNTRY_MS'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'MA' => [ 'name' => Text::_('NR_COUNTRY_MA'), 'calling_code' => '212', 'currency_code' => 'MAD', 'currency_name' => 'Dirham', 'currency_symbol' => 'DH' ], + 'MZ' => [ 'name' => Text::_('NR_COUNTRY_MZ'), 'calling_code' => '258', 'currency_code' => 'MZN', 'currency_name' => 'Meticail', 'currency_symbol' => 'MT' ], + 'MM' => [ 'name' => Text::_('NR_COUNTRY_MM'), 'calling_code' => '95', 'currency_code' => 'MMK', 'currency_name' => 'Kyat', 'currency_symbol' => 'K' ], + 'NA' => [ 'name' => Text::_('NR_COUNTRY_NA'), 'calling_code' => '264', 'currency_code' => 'NAD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'NR' => [ 'name' => Text::_('NR_COUNTRY_NR'), 'calling_code' => '674', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'NP' => [ 'name' => Text::_('NR_COUNTRY_NP'), 'calling_code' => '977', 'currency_code' => 'NPR', 'currency_name' => 'Rupee', 'currency_symbol' => '₨' ], + 'NL' => [ 'name' => Text::_('NR_COUNTRY_NL'), 'calling_code' => '31', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'NC' => [ 'name' => Text::_('NR_COUNTRY_NC'), 'calling_code' => '687', 'currency_code' => 'XPF', 'currency_name' => 'Franc', 'currency_symbol' => 'F' ], + 'NZ' => [ 'name' => Text::_('NR_COUNTRY_NZ'), 'calling_code' => '64', 'currency_code' => 'NZD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'NI' => [ 'name' => Text::_('NR_COUNTRY_NI'), 'calling_code' => '505', 'currency_code' => 'NIO', 'currency_name' => 'Cordoba', 'currency_symbol' => 'C$' ], + 'NE' => [ 'name' => Text::_('NR_COUNTRY_NE'), 'calling_code' => '227', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'NG' => [ 'name' => Text::_('NR_COUNTRY_NG'), 'calling_code' => '234', 'currency_code' => 'NGN', 'currency_name' => 'Naira', 'currency_symbol' => '₦' ], + 'NU' => [ 'name' => Text::_('NR_COUNTRY_NU'), 'calling_code' => '683', 'currency_code' => 'NZD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'NF' => [ 'name' => Text::_('NR_COUNTRY_NF'), 'calling_code' => '672', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'KP' => [ 'name' => Text::_('NR_COUNTRY_KP'), 'calling_code' => '850', 'currency_code' => 'KPW', 'currency_name' => 'Won', 'currency_symbol' => '₩' ], + 'MP' => [ 'name' => Text::_('NR_COUNTRY_MP'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'NO' => [ 'name' => Text::_('NR_COUNTRY_NO'), 'calling_code' => '47', 'currency_code' => 'NOK', 'currency_name' => 'Krone', 'currency_symbol' => 'kr' ], + 'OM' => [ 'name' => Text::_('NR_COUNTRY_OM'), 'calling_code' => '968', 'currency_code' => 'OMR', 'currency_name' => 'Rial', 'currency_symbol' => '﷼' ], + 'PK' => [ 'name' => Text::_('NR_COUNTRY_PK'), 'calling_code' => '92', 'currency_code' => 'PKR', 'currency_name' => 'Rupee', 'currency_symbol' => '₨' ], + 'PW' => [ 'name' => Text::_('NR_COUNTRY_PW'), 'calling_code' => '680', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'PS' => [ 'name' => Text::_('NR_COUNTRY_PS'), 'calling_code' => '970', 'currency_code' => 'ILS', 'currency_name' => 'Shekel', 'currency_symbol' => '₪' ], + 'PA' => [ 'name' => Text::_('NR_COUNTRY_PA'), 'calling_code' => '507', 'currency_code' => 'PAB', 'currency_name' => 'Balboa', 'currency_symbol' => 'B/.' ], + 'PG' => [ 'name' => Text::_('NR_COUNTRY_PG'), 'calling_code' => '675', 'currency_code' => 'PGK', 'currency_name' => 'Kina', 'currency_symbol' => 'K' ], + 'PY' => [ 'name' => Text::_('NR_COUNTRY_PY'), 'calling_code' => '595', 'currency_code' => 'PYG', 'currency_name' => 'Guarani', 'currency_symbol' => 'Gs' ], + 'PE' => [ 'name' => Text::_('NR_COUNTRY_PE'), 'calling_code' => '51', 'currency_code' => 'PEN', 'currency_name' => 'Sol', 'currency_symbol' => 'S/.' ], + 'PH' => [ 'name' => Text::_('NR_COUNTRY_PH'), 'calling_code' => '63', 'currency_code' => 'PHP', 'currency_name' => 'Peso', 'currency_symbol' => 'Php' ], + 'PN' => [ 'name' => Text::_('NR_COUNTRY_PN'), 'calling_code' => '870', 'currency_code' => 'NZD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'PL' => [ 'name' => Text::_('NR_COUNTRY_PL'), 'calling_code' => '48', 'currency_code' => 'PLN', 'currency_name' => 'Zloty', 'currency_symbol' => 'zł' ], + 'PT' => [ 'name' => Text::_('NR_COUNTRY_PT'), 'calling_code' => '351', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'PR' => [ 'name' => Text::_('NR_COUNTRY_PR'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'QA' => [ 'name' => Text::_('NR_COUNTRY_QA'), 'calling_code' => '974', 'currency_code' => 'QAR', 'currency_name' => 'Rial', 'currency_symbol' => '﷼' ], + 'CG' => [ 'name' => Text::_('NR_COUNTRY_CG'), 'calling_code' => '242', 'currency_code' => 'XAF', 'currency_name' => 'Franc', 'currency_symbol' => 'FCF' ], + 'RE' => [ 'name' => Text::_('NR_COUNTRY_RE'), 'calling_code' => '262', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'RO' => [ 'name' => Text::_('NR_COUNTRY_RO'), 'calling_code' => '40', 'currency_code' => 'RON', 'currency_name' => 'Leu', 'currency_symbol' => 'lei' ], + 'RU' => [ 'name' => Text::_('NR_COUNTRY_RU'), 'calling_code' => '7', 'currency_code' => 'RUB', 'currency_name' => 'Ruble', 'currency_symbol' => 'руб' ], + 'RW' => [ 'name' => Text::_('NR_COUNTRY_RW'), 'calling_code' => '250', 'currency_code' => 'RWF', 'currency_name' => 'Franc', 'currency_symbol' => 'FRw' ], + 'SH' => [ 'name' => Text::_('NR_COUNTRY_SH'), 'calling_code' => '290', 'currency_code' => 'SHP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'KN' => [ 'name' => Text::_('NR_COUNTRY_KN'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'LC' => [ 'name' => Text::_('NR_COUNTRY_LC'), 'calling_code' => '1758', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'PM' => [ 'name' => Text::_('NR_COUNTRY_PM'), 'calling_code' => '508', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'VC' => [ 'name' => Text::_('NR_COUNTRY_VC'), 'calling_code' => '1', 'currency_code' => 'XCD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'WS' => [ 'name' => Text::_('NR_COUNTRY_WS'), 'calling_code' => '685', 'currency_code' => 'WST', 'currency_name' => 'Tala', 'currency_symbol' => 'WS$' ], + 'SM' => [ 'name' => Text::_('NR_COUNTRY_SM'), 'calling_code' => '378', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'ST' => [ 'name' => Text::_('NR_COUNTRY_ST'), 'calling_code' => '239', 'currency_code' => 'STD', 'currency_name' => 'Dobra', 'currency_symbol' => 'Db' ], + 'SA' => [ 'name' => Text::_('NR_COUNTRY_SA'), 'calling_code' => '966', 'currency_code' => 'SAR', 'currency_name' => 'Rial', 'currency_symbol' => '﷼' ], + 'SN' => [ 'name' => Text::_('NR_COUNTRY_SN'), 'calling_code' => '221', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'RS' => [ 'name' => Text::_('NR_COUNTRY_RS'), 'calling_code' => '381', 'currency_code' => 'RSD', 'currency_name' => 'Dinar', 'currency_symbol' => 'Дин' ], + 'SC' => [ 'name' => Text::_('NR_COUNTRY_SC'), 'calling_code' => '248', 'currency_code' => 'SCR', 'currency_name' => 'Rupee', 'currency_symbol' => '₨' ], + 'SL' => [ 'name' => Text::_('NR_COUNTRY_SL'), 'calling_code' => '232', 'currency_code' => 'SLL', 'currency_name' => 'Leone', 'currency_symbol' => 'Le' ], + 'SG' => [ 'name' => Text::_('NR_COUNTRY_SG'), 'calling_code' => '65', 'currency_code' => 'SGD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'SK' => [ 'name' => Text::_('NR_COUNTRY_SK'), 'calling_code' => '421', 'currency_code' => 'SKK', 'currency_name' => 'Koruna', 'currency_symbol' => 'Sk' ], + 'SI' => [ 'name' => Text::_('NR_COUNTRY_SI'), 'calling_code' => '386', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'SB' => [ 'name' => Text::_('NR_COUNTRY_SB'), 'calling_code' => '677', 'currency_code' => 'SBD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'SO' => [ 'name' => Text::_('NR_COUNTRY_SO'), 'calling_code' => '252', 'currency_code' => 'SOS', 'currency_name' => 'Shilling', 'currency_symbol' => 'S' ], + 'ZA' => [ 'name' => Text::_('NR_COUNTRY_ZA'), 'calling_code' => '27', 'currency_code' => 'ZAR', 'currency_name' => 'Rand', 'currency_symbol' => 'R' ], + 'GS' => [ 'name' => Text::_('NR_COUNTRY_GS'), 'calling_code' => '500', 'currency_code' => 'GBP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'KR' => [ 'name' => Text::_('NR_COUNTRY_KR'), 'calling_code' => '82', 'currency_code' => 'KRW', 'currency_name' => 'Won', 'currency_symbol' => '₩' ], + 'ES' => [ 'name' => Text::_('NR_COUNTRY_ES'), 'calling_code' => '34', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'LK' => [ 'name' => Text::_('NR_COUNTRY_LK'), 'calling_code' => '94', 'currency_code' => 'LKR', 'currency_name' => 'Rupee', 'currency_symbol' => '₨' ], + 'SD' => [ 'name' => Text::_('NR_COUNTRY_SD'), 'calling_code' => '249', 'currency_code' => 'SDD', 'currency_name' => 'Dinar', 'currency_symbol' => 'ج.س' ], + 'SS' => [ 'name' => Text::_('NR_COUNTRY_SS'), 'calling_code' => '211', 'currency_code' => 'SSP', 'currency_name' => 'Pound', 'currency_symbol' => 'SS£' ], + 'SR' => [ 'name' => Text::_('NR_COUNTRY_SR'), 'calling_code' => '597', 'currency_code' => 'SRD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'SJ' => [ 'name' => Text::_('NR_COUNTRY_SJ'), 'calling_code' => '47', 'currency_code' => 'NOK', 'currency_name' => 'Krone', 'currency_symbol' => 'kr' ], + 'SZ' => [ 'name' => Text::_('NR_COUNTRY_SZ'), 'calling_code' => '268', 'currency_code' => 'SZL', 'currency_name' => 'Lilangeni', 'currency_symbol' => 'L' ], + 'SE' => [ 'name' => Text::_('NR_COUNTRY_SE'), 'calling_code' => '46', 'currency_code' => 'SEK', 'currency_name' => 'Krona', 'currency_symbol' => 'kr' ], + 'CH' => [ 'name' => Text::_('NR_COUNTRY_CH'), 'calling_code' => '41', 'currency_code' => 'CHF', 'currency_name' => 'Franc', 'currency_symbol' => 'CHF' ], + 'SY' => [ 'name' => Text::_('NR_COUNTRY_SY'), 'calling_code' => '963', 'currency_code' => 'SYP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'TW' => [ 'name' => Text::_('NR_COUNTRY_TW'), 'calling_code' => '886', 'currency_code' => 'TWD', 'currency_name' => 'Dollar', 'currency_symbol' => 'NT$' ], + 'TJ' => [ 'name' => Text::_('NR_COUNTRY_TJ'), 'calling_code' => '992', 'currency_code' => 'TJS', 'currency_name' => 'Somoni', 'currency_symbol' => 'SM' ], + 'TZ' => [ 'name' => Text::_('NR_COUNTRY_TZ'), 'calling_code' => '255', 'currency_code' => 'TZS', 'currency_name' => 'Shilling', 'currency_symbol' => 'TSh' ], + 'TH' => [ 'name' => Text::_('NR_COUNTRY_TH'), 'calling_code' => '66', 'currency_code' => 'THB', 'currency_name' => 'Baht', 'currency_symbol' => '฿' ], + 'TG' => [ 'name' => Text::_('NR_COUNTRY_TG'), 'calling_code' => '228', 'currency_code' => 'XOF', 'currency_name' => 'Franc', 'currency_symbol' => 'CFA' ], + 'TK' => [ 'name' => Text::_('NR_COUNTRY_TK'), 'calling_code' => '690', 'currency_code' => 'NZD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'TO' => [ 'name' => Text::_('NR_COUNTRY_TO'), 'calling_code' => '676', 'currency_code' => 'TOP', 'currency_name' => 'Paanga', 'currency_symbol' => 'T$' ], + 'TT' => [ 'name' => Text::_('NR_COUNTRY_TT'), 'calling_code' => '1', 'currency_code' => 'TTD', 'currency_name' => 'Dollar', 'currency_symbol' => 'TT$' ], + 'TN' => [ 'name' => Text::_('NR_COUNTRY_TN'), 'calling_code' => '216', 'currency_code' => 'TND', 'currency_name' => 'Dinar', 'currency_symbol' => 'د.ت' ], + 'TR' => [ 'name' => Text::_('NR_COUNTRY_TR'), 'calling_code' => '90', 'currency_code' => 'TRY', 'currency_name' => 'Lira', 'currency_symbol' => 'YTL' ], + 'TM' => [ 'name' => Text::_('NR_COUNTRY_TM'), 'calling_code' => '993', 'currency_code' => 'TMM', 'currency_name' => 'Manat', 'currency_symbol' => 'm' ], + 'TC' => [ 'name' => Text::_('NR_COUNTRY_TC'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'TV' => [ 'name' => Text::_('NR_COUNTRY_TV'), 'calling_code' => '688', 'currency_code' => 'AUD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'VI' => [ 'name' => Text::_('NR_COUNTRY_VI'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'UG' => [ 'name' => Text::_('NR_COUNTRY_UG'), 'calling_code' => '256', 'currency_code' => 'UGX', 'currency_name' => 'Shilling', 'currency_symbol' => 'USh' ], + 'UA' => [ 'name' => Text::_('NR_COUNTRY_UA'), 'calling_code' => '380', 'currency_code' => 'UAH', 'currency_name' => 'Hryvnia', 'currency_symbol' => '₴' ], + 'AE' => [ 'name' => Text::_('NR_COUNTRY_AE'), 'calling_code' => '971', 'currency_code' => 'AED', 'currency_name' => 'Dirham', 'currency_symbol' => 'د.إ' ], + 'GB' => [ 'name' => Text::_('NR_COUNTRY_GB'), 'calling_code' => '44', 'currency_code' => 'GBP', 'currency_name' => 'Pound', 'currency_symbol' => '£' ], + 'US' => [ 'name' => Text::_('NR_COUNTRY_US'), 'calling_code' => '1', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'UM' => [ 'name' => Text::_('NR_COUNTRY_UM'), 'calling_code' => '246', 'currency_code' => 'USD', 'currency_name' => 'Dollar', 'currency_symbol' => '$' ], + 'UY' => [ 'name' => Text::_('NR_COUNTRY_UY'), 'calling_code' => '598', 'currency_code' => 'UYU', 'currency_name' => 'Peso', 'currency_symbol' => '$U' ], + 'UZ' => [ 'name' => Text::_('NR_COUNTRY_UZ'), 'calling_code' => '998', 'currency_code' => 'UZS', 'currency_name' => 'Som', 'currency_symbol' => 'лв' ], + 'VU' => [ 'name' => Text::_('NR_COUNTRY_VU'), 'calling_code' => '678', 'currency_code' => 'VUV', 'currency_name' => 'Vatu', 'currency_symbol' => 'Vt' ], + 'VA' => [ 'name' => Text::_('NR_COUNTRY_VA'), 'calling_code' => '39', 'currency_code' => 'EUR', 'currency_name' => 'Euro', 'currency_symbol' => '€' ], + 'VE' => [ 'name' => Text::_('NR_COUNTRY_VE'), 'calling_code' => '58', 'currency_code' => 'VEF', 'currency_name' => 'Bolivar', 'currency_symbol' => 'Bs' ], + 'VN' => [ 'name' => Text::_('NR_COUNTRY_VN'), 'calling_code' => '84', 'currency_code' => 'VND', 'currency_name' => 'Dong', 'currency_symbol' => '₫' ], + 'WF' => [ 'name' => Text::_('NR_COUNTRY_WF'), 'calling_code' => '681', 'currency_code' => 'XPF', 'currency_name' => 'Franc', 'currency_symbol' => 'F' ], + 'EH' => [ 'name' => Text::_('NR_COUNTRY_EH'), 'calling_code' => '212', 'currency_code' => 'MAD', 'currency_name' => 'Dirham', 'currency_symbol' => 'DH' ], + 'YE' => [ 'name' => Text::_('NR_COUNTRY_YE'), 'calling_code' => '967', 'currency_code' => 'YER', 'currency_name' => 'Rial', 'currency_symbol' => '﷼' ], + 'ZM' => [ 'name' => Text::_('NR_COUNTRY_ZM'), 'calling_code' => '260', 'currency_code' => 'ZMK', 'currency_name' => 'Kwacha', 'currency_symbol' => 'ZK' ], + 'ZW' => [ 'name' => Text::_('NR_COUNTRY_ZW'), 'calling_code' => '263', 'currency_code' => 'ZWD', 'currency_name' => 'Dollar', 'currency_symbol' => 'Z$' ] + ]; + + // Sort by name + uasort($list, function ($item1, $item2) + { + return $item1['name'] <=> $item2['name']; + }); + + return $list; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/DOMCrawler.php b/plugins/system/nrframework/NRFramework/DOMCrawler.php new file mode 100644 index 00000000..432e3e30 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/DOMCrawler.php @@ -0,0 +1,368 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework; + +use Joomla\String\StringHelper; +use NRFramework\Cache; + +defined('_JEXEC') or die; + +class DOMCrawler +{ + /** + * The content to craw + * + * @var string + */ + protected $content; + + /** + * The nodes discovered by crawling + * + * @var object + */ + public $nodes; + + /** + * Class constructor + * + * @param mixed $content The content to crawl. Defaults + */ + public function __construct($content = null) + { + if (is_null($content)) + { + $content = \NRFramework\Functions::getBuffer(); + } + + $this->setContent($content); + } + + /** + * Set content to crawl + * + * @param string $content The content to crawl. Defaults + + * @return void + */ + public function setContent($content) + { + $this->content = $this->stringToUTF8($content); + } + + /** + * Filter dom elements with a CSS Selector or XPath expression + * + * @param string $expression A CSS Selector or XPath expression + * + * @return void + */ + public function filter($expression) + { + // If empty content, return + if (empty($this->content)) + { + return $this; + } + + // If empty selector, return + if (empty($expression)) + { + return $this; + } + + if (!class_exists('DOMDocument') || !class_exists('DOMXPath')) + { + return $this; + } + + // Cache check + $hash = md5($expression); + + if (Cache::has($hash)) + { + $this->nodes = Cache::get($hash, false); + return $this; + } + + libxml_use_internal_errors(true); + $dom = new \DOMDocument; + $dom->loadHTML($this->content); + $finder = new \DOMXPath($dom); + + // Check if we are writing our own XPath query + // example: =//h1[contains(@class, "faq-question")] + if (substr($expression, 0, 1) == '=') + { + $xpath = StringHelper::substr($expression, 1); + } + else + { + // Create the XPath via the provided selector + $xpath = $this->cssSelectorToXPath($expression); + } + + $this->nodes = $finder->query($xpath); + + // Speed up filtering by caching results + Cache::set($hash, $this->nodes); + + return $this; + } + + /** + * Returns the HTML of the first discovered node + * + * @param string $fallback The fallback text to return if no node is found + * @param boolean $inner If set to true, only the node's inner HTML will be returned. + * @param boolean $firstOnly If set to true, only the first node will be returned. + * + * @return string + */ + public function html($fallback = '', $inner = false, $firstOnly = true) + { + if (!$this->nodes || !$this->nodes->length) + { + return $fallback; + } + + if ($firstOnly) + { + return $this->cleanText($this->getNodeHTML($this->nodes[0], $inner)); + } + + $result = []; + + foreach ($this->nodes as $node) + { + $result[] = $this->cleanText($this->getNodeHTML($node, $inner)); + } + + return $result; + } + + /** + * Returns the text of the 1st discovered node. + * + * @param string $fallback The fallback text to return if no node is found + * @param boolean $firstOnly If set to true, only the first node will be returned. + * + * @return string + */ + public function text($fallback = '', $firstOnly = true) + { + if (!$this->nodes || !$this->nodes->length) + { + return $fallback; + } + + if ($firstOnly) + { + return $this->cleanText($this->nodes[0]->textContent); + } + + $result = []; + + foreach ($this->nodes as $node) + { + $result[] = $this->cleanText($node->textContent); + } + + return $result; + } + + /** + * Returns the attribute value of the 1st discovered node + * + * @param string $attribute_name The name of the attribute + * @param string $fallback The fallback text to return if no nodes found + * @param boolean $firstOnly If set to true, only the first node will be returned. + * + * @return string + */ + public function attr($attribute_name, $fallback = '', $firstOnly = true) + { + if (!$this->nodes || !$this->nodes->length) + { + return $fallback; + } + + if ($firstOnly) + { + return $this->cleanText($this->nodes[0]->getAttribute($attribute_name)); + } + + $result = []; + + foreach ($this->nodes as $node) + { + $result[] = $this->cleanText($node->getAttribute($attribute_name)); + } + + return $result; + } + + /** + * Returns the total number of nodes found + * + * @param integer $fallback The fallback value number to return if no nodes found + * + * @return integer + */ + public function count($fallback = 0) + { + return $this->nodes && $this->nodes->length ? $this->nodes->length : $fallback; + } + + /** + * Helper method to crawl page based on the value of a CSS Selector field. + * + * @param array $props Expected properties: selector, task, attr + * + * @return string + */ + public function readCSSSelectorField($props, $firstOnly = true) + { + $props = (array) $props; + $fallback = $firstOnly ? '' : []; + + if (empty($props['selector'])) + { + return $fallback; + } + + $this->filter($props['selector']); + + switch ($props['task']) + { + case 'html': + return $this->html($fallback, false, $firstOnly); + + case 'innerhtml': + return $this->html($fallback, true, $firstOnly); + + case 'attr': + return $this->attr($props['attr'], $fallback, $firstOnly); + + case 'count': + return $this->count(); + + default: + return $this->text($fallback, $firstOnly); + } + } + + /** + * Helper method to clean the text + * + * @param string $text The text to clean + * + * @return string + */ + private function cleanText($text) + { + return StringHelper::trim($text); + } + + /** + * Transforms the CSS Selector to a valid XPath expression + * + * @param string $selector The CSS selector to transform + * + * @return string XPath expression + */ + private function cssSelectorToXPath($selector) + { + // explode() the given selectors and create a XPath syntax + $selectors = explode(' ', $selector); + + $xpath = ''; + + foreach ($selectors as $selector) + { + // Check if the selector contains a class or ID + $explode_class = explode('.', $selector); + $explode_id = explode('#', $selector); + + // Selector contains a class + if (count($explode_class) > 1) + { + $prefix = (isset($explode_class[0]) && !empty($explode_class[0])) ? $explode_class[0] : '*'; + $xpath .= '//' . $prefix . '['; + + // When we use a selector such as div.class1.class2 or .class1.class2 + // we need to use all classes in the xpath and no the first one only + unset($explode_class[0]); + $total = count($explode_class); + $counter = 1; + $xpath_and_prefix = 'and'; + + foreach ($explode_class as $class) + { + $xpath .= ($counter != 1) ? $xpath_and_prefix : ''; + $xpath .= ' contains(concat(" ", normalize-space(@class), " "), " ' . $class . ' ") '; + $counter++; + } + + $xpath .= ']'; + } + else if (count($explode_id) > 1) // Selector contains an ID + { + $prefix = (isset($explode_id[0]) && !empty($explode_id[0])) ? $explode_id[0] : '*'; + $xpath .= './/' . $prefix . '[@id="' . $explode_id[1] . '"]'; + } + else // No class or ID given + { + $xpath .= '//' . $selector; + } + } + + return $xpath; + } + + /** + * Convert a string to UTF8 encoding for non-latin languages + * + * @param string + * + * @return string + */ + private function stringToUTF8($string) + { + $string = iconv('UTF-8', 'UTF-8', $string); + $string = mb_encode_numericentity($string, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'); + return $string; + } + + /** + * Helper method to return the outer or inner HTML of a node + * + * @param Node $node The node object + * @param boolean $inner Whether to return the outer or inner HTML + * + * @return string The HTML of the node + */ + private function getNodeHTML($node, $inner = true) + { + if ($inner) + { + $html = ''; + + foreach ($node->childNodes as $child) + { + $html .= $node->ownerDocument->saveHTML($child); + } + + return $html; + } + + return $node->ownerDocument->saveHTML($node); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Document.php b/plugins/system/nrframework/NRFramework/Document.php new file mode 100644 index 00000000..079e962c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Document.php @@ -0,0 +1,39 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +class Document +{ + /** + * A cross Joomla compatible method to inject inline script as module + * + * @param string $script The inline script to load as module (defer) + * + * @return void + */ + static public function addInlineScriptDefer($script) + { + $doc = Factory::getApplication()->getDocument(); + + if (defined('nrJ4')) + { + // Joomla => 4 + $doc->getWebAssetManager()->addInlineScript($script, [], ['type' => 'module']); + } else + { + // Joomla <= 3 + $doc->addCustomTag(''); + } + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Email.php b/plugins/system/nrframework/NRFramework/Email.php new file mode 100644 index 00000000..16a3db52 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Email.php @@ -0,0 +1,304 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +defined('_JEXEC') or die('Restricted access'); + +use NRFramework\Functions; +use NRFramework\URLHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; +use Joomla\Filesystem\Path; +use Joomla\CMS\Language\Text; + +/** + * Framework Emailer + */ +class Email +{ + /** + * Indicates the last error + * + * @var string + */ + public $error; + + /** + * Email Object + * + * @var email data to be sent + */ + private $email; + + /** + * Required elements for a valid email object + * + * @var array + */ + private $requiredKeys = [ + 'from_email', + 'from_name', + 'recipient', + 'subject', + 'body' + ]; + + /** + * Class constructor + */ + public function __construct($email) + { + $this->email = $email; + } + + /** + * Validates Email Object + * + * @param array $email The email object + * + * @return boolean Returns true if the email object is valid + */ + public function validate() + { + // Validate email object + if (!$this->email || !is_array($this->email) || !count($this->email)) + { + $this->setError('Invalid email object.'); + return; + } + + // Check for missing properties + foreach ($this->requiredKeys as $key) + { + if (!isset($this->email[$key]) || empty($this->email[$key])) + { + $this->setError("The $key field is either missing or invalid."); + return; + } + } + + // Validate recipient email addresses. + $this->email['recipient'] = Functions::makeArray($this->email['recipient']); + + foreach ($this->email['recipient'] as $recipient) + { + if (!$this->validateEmailAddress($recipient)) + { + $this->setError("Invalid recipient email address: $recipient"); + return; + } + } + + // Validate sender email address + if (!$this->validateEmailAddress($this->email['from_email'])) + { + $this->setError('Invalid sender email address: ' . $this->email['from_email']); + return; + } + + $this->email['bcc'] = isset($this->email['bcc']) ? Functions::makeArray($this->email['bcc']) : []; + $this->email['cc'] = isset($this->email['cc']) ? Functions::makeArray($this->email['cc']) : []; + + // Convert special HTML entities back to characters on non text-only properties. + // For instance, the subject line of an email is not parsed as HTML, it's just pure text. + // Because of this an HTML entity like & it will be displayed as encoded. + // To prevent this from happening we need decode the values. + $this->email['subject'] = htmlspecialchars_decode($this->email['subject']); + $this->email['from_name'] = htmlspecialchars_decode($this->email['from_name']); + $this->email['reply_to_name'] = htmlspecialchars_decode($this->email['reply_to_name']); + + return true; + } + + /** + * Sending emails + * + * @param array $email The mail objecta + * + * @return mixed Returns true on success. Throws exeption on fail. + */ + public function send() + { + // Proceed only if Mail Sending is enabled. + if (!Factory::getConfig()->get('mailonline')) + { + $this->error = Text::_('NR_ERROR_EMAIL_IS_DISABLED'); + return; + } + + // Validate first the email object + if (!$this->validate($this->email)) + { + return; + } + + $email = $this->email; + $mailer = Factory::getMailer(); + $mailer->CharSet = 'UTF-8'; + $mailer->Encoding = 'quoted-printable'; + + // Email Sender + $mailer->setSender([ + $email['from_email'], + $email['from_name'] + ]); + + // Reply-to + if (isset($email['reply_to']) && !empty($email['reply_to'])) + { + $name = (isset($email['reply_to_name']) && !empty($email['reply_to_name'])) ? $email['reply_to_name'] : ''; + + $reply_to_addresses = array_filter(array_map('trim', explode(',', $email['reply_to']))); + + foreach($reply_to_addresses as $reply_to_address) + { + $mailer->addReplyTo($reply_to_address, $name); + } + } + + // Convert all relative paths found in and elements to absolute URLs + $email['body'] = URLHelper::relativePathsToAbsoluteURLs($email['body']); + + // Fix space characters displayed as ???? in old email clients like SquirrelMail. + // Ticket reference: https://smilemotive.teamwork.com/desk/tickets/96313487/messages + $specialSpace = [ + "\xC2\xA0", + "\xE1\xA0\x8E", + "\xE2\x80\x80", + "\xE2\x80\x81", + "\xE2\x80\x82", + "\xE2\x80\x83", + "\xE2\x80\x84", + "\xE2\x80\x85", + "\xE2\x80\x86", + "\xE2\x80\x87", + "\xE2\x80\x88", + "\xE2\x80\x89", + "\xE2\x80\x8A", + "\xE2\x80\x8B", + "\xE2\x80\xAF", + "\xE2\x81\x9F", + "\xEF\xBB\xBF", + ]; + + $email['body'] = str_replace($specialSpace, " ", $email['body']); + + $mailer + ->addRecipient($email['recipient']) + ->isHTML(true) + ->setSubject($email['subject']) + ->setBody($email['body']); + + $mailer->AltBody = strip_tags(str_ireplace(['
', '
', '
'], "\r\n", $email['body'])); + + // Add CC + if (!empty($email['cc'])) + { + $mailer->addCc($email['cc']); + } + + // Add BCC + if (!empty($email['bcc'])) + { + $mailer->addBcc($email['bcc']); + } + + // Attachments + $attachments = $email['attachments']; + + if (!empty($attachments)) + { + if (!is_array($attachments)) + { + $attachments = explode(',', $attachments); + } + + // Validate Attachments + $attachments = array_filter(array_map('trim', $attachments)); + + foreach ($attachments as $attachment) + { + $file_path = $this->toRelativePath($attachment); + + if (!is_file($file_path)) + { + continue; + } + + $mailer->addAttachment($file_path); + } + } + + // Send mail + $send = $mailer->Send(); + + if ($send !== true) + { + $this->setError($send->__toString()); + return; + } + + return true; + } + + /** + * Set Class Error + * + * @param string $error The error message + */ + private function setError($error) + { + $this->error = 'Error sending email: ' . $error; + } + + /** + * Removes all illegal characters and validates an email address + * + * @param string $email Email address string + * + * @return bool + */ + private function validateEmailAddress($email) + { + // If the email address contains an ampersand, throw an error + if (strpos($email, '&') !== false) + { + return false; + } + + $email = filter_var($email, FILTER_SANITIZE_EMAIL); + return filter_var($email, FILTER_VALIDATE_EMAIL); + } + + /** + * Attempts to transform an absolute URL to path relative to the site's root. + * + * @param string $url + * + * @return string + */ + private function toRelativePath($url) + { + $needles = [ + Uri::root(), + JPATH_SITE, + JPATH_ROOT + ]; + + $path = str_replace($needles, '', $url); + + $path = Path::clean($path); + + // Relative paths should not start with a slash. + $path = ltrim($path, '/'); + + return $path; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Executer.php b/plugins/system/nrframework/NRFramework/Executer.php new file mode 100644 index 00000000..446ecbf9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Executer.php @@ -0,0 +1,283 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ +namespace NRFramework; + +defined('_JEXEC') or die; + +use Joomla\Registry\Registry; +use Joomla\Filesystem\File; +use Joomla\CMS\Factory; + +/** + * Cleverly evaluate php code using a temporary file and without using the evil eval() PHP method + */ +class Executer +{ + /** + * The php code is going to be executed + * + * @var string + */ + private $php_code; + + /** + * The data object passed as argument to function + * + * @var mixed + */ + private $payload; + + /** + * Executer configuration + * + * @var object + */ + private $options; + + /** + * Class constructor + * + * @param string $php_code The php code is going to be executed + */ + public function __construct($php_code = null, &$payload = null, $options = array()) + { + $this->setPhpCode($php_code); + $this->setPayload($payload); + + // Default options + $defaults = [ + 'forbidden_php_functions' => [ + 'fopen', + 'popen', + 'unlink', + 'rmdir', + 'dl', + 'escapeshellarg', + 'escapeshellcmd', + 'exec', + 'passthru', + 'proc_close', + 'proc_open', + 'shell_exec', + 'symlink', + 'system', + 'pcntl_exec', + 'eval', + 'create_function' + ] + ]; + + $options = array_merge($defaults, $options); + $this->options = new Registry($options); + } + + /** + * Payload contains the variables passed as argumentse into the PHP code + * + * @param mixed $data + * + * @return void + */ + public function setPayload(&$data) + { + $this->payload = &$data; + return $this; + } + + /** + * Set forbidden PHP functions. If any found, the whole PHP block won't run. + * + * @param array $functions + * + * @return void + */ + public function setForbiddenPHPFunctions($functions) + { + if (empty($functions)) + { + return $this; + } + + if (is_string($functions)) + { + $functions = explode(',', $functions); + } + + $this->options->set('forbidden_php_functions', $functions); + return $this; + } + + /** + * Helper method to set the php code is about to be executed + * + * @param string $php_code + * + * @return void + */ + public function setPhpCode($php_code) + { + $this->php_code = $php_code; + return $this; + } + + /** + * Checks if given PHP code is valid and it's allowed to run. + * + * @return bool + */ + private function allowedToRun() + { + // Get the forbidden PHP functions, but we want to make sure we whitelist 'curl_exec' + $forbidden_functions = array_diff($this->options->get('forbidden_php_functions'), ['curl_exec']); + + // Build the regex, making sure 'exec' does not match within 'curl_exec' + $re = '/\b(' . implode('|', $forbidden_functions) . ')\b(\s*\(|\s+[\'"])/mi'; + preg_match_all($re, $this->php_code ?? '', $matches); + if (!empty($matches[0])) + { + return false; + } + + // Check for backticks `` + if ($has_back_ticks = preg_match('/`(.*?)`/s', $this->php_code ?? '')) + { + return false; + } + + return true; + } + + /** + * Run function + * + * @return function + */ + public function run() + { + if (!$this->allowedToRun()) + { + return; + } + + $function_name = $this->getFunctionName(); + + // Function doesn't exist. Let's create it. + if (!function_exists($function_name)) + { + if (!$this->createFunction()) + { + return; + } + } + + return $function_name($this->payload); + } + + /** + * Creates a temporary function in memory + * + * @return void + */ + private function createFunction() + { + $function_name = $this->getFunctionName(); + $function_content = $this->getFunctionContent(); + $temp_file = $this->getTempPath() . '/' . $function_name; + + // Write function's content to a temporary file + File::write($temp_file, $function_content); + + // Include file + include_once $temp_file; + + // Delete file + if (!defined('JDEBUG') || !JDEBUG) + { + @chmod($temp_file, 0777); + @unlink($temp_file); + } + + return function_exists($function_name); + } + + /** + * Get temporary file content + * + * @return string + */ + private function getFunctionContent() + { + $function_name = $this->getFunctionName(); + $variables = $this->getFunctionVariables(); + + $contents = [ + 'php_code, + ';return true;}' + ]; + + $contents = implode("\n", $contents); + + // Remove Zero Width spaces / (non-)joiners + $contents = str_replace( + [ + "\xE2\x80\x8B", + "\xE2\x80\x8C", + "\xE2\x80\x8D", + ], + '', + $contents + ); + + return $contents; + } + + /** + * Make user's life easier by initializing some Joomla helpful variables + * + * @return array + */ + protected function getFunctionVariables() + { + return [ + '$app = $mainframe = \Joomla\CMS\Factory::getApplication();', + '$document = $doc = \Joomla\CMS\Factory::getDocument();', + '$database = $db = \Joomla\CMS\Factory::getDbo();', + '$user = \Joomla\CMS\Factory::getUser();', + '$Itemid = $app->input->getInt(\'Itemid\');' + ]; + } + + /** + * Construct a temporary function name + * + * @return string + */ + private function getFunctionName() + { + return 'tassos_php_' . md5($this->php_code ?? ''); + } + + /** + * Return Joomla temporary path + * + * @return void + */ + private function getTempPath() + { + return Factory::getConfig()->get('tmp_path', JPATH_ROOT . '/tmp'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Extension.php b/plugins/system/nrframework/NRFramework/Extension.php new file mode 100644 index 00000000..ae109f6b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Extension.php @@ -0,0 +1,925 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +use NRFramework\Cache; +use Joomla\Registry\Registry; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Http\HttpFactory; + + +defined( '_JEXEC' ) or die( 'Restricted access' ); + +class Extension +{ + /** + * Indicates the base url of Tassos.gr Joomla Extensions + * + * @var string + */ + public static $product_base_url = 'https://www.tassos.gr/joomla-extensions'; + + /** + * Array including already loaded extensions + * + * @var array + */ + public static $cache = []; + + /** + * Get extension ID + * + * @param string $element The extension element name + * @param string $type The extension type: component, plugin, library e.t.c + * @param mixed $folder The plugin folder: system, content e.t.c + * + * @return mixed False on failure, Integer on success + */ + public static function getID($element, $type = 'component', $folder = null) + { + if (!$extension = self::get($element, $type, $folder)) + { + return false; + } + + return (int) $extension['extension_id']; + } + + public static function getModuleByID($module_id = null) + { + if (!$module_id) + { + return; + } + + $hash = 'get_module_by_id_' . $module_id; + + // Render modal once + if ($module_data = Cache::get($hash)) + { + return $module_data; + } + + // Let's call the database + $db = Factory::getDBO(); + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__modules')) + ->where($db->quoteName('id') . ' = ' . $module_id); + + $db->setQuery($query); + + $module_data = $db->loadAssoc(); + + Cache::set($hash, $module_data); + + return $module_data; + } + + /** + * Get extension data by ID + * + * @param string $extension_id The extension primary key + * + * @return void + */ + public static function getByID($extension_id) + { + // Check if element is already cached + if (isset(self::$cache[$extension_id])) + { + return self::$cache[$extension_id]; + } + + // Let's call the database + $db = Factory::getDBO(); + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('extension_id') . ' = ' . $extension_id); + + $db->setQuery($query); + + return self::$cache[$extension_id] = $db->loadAssoc(); + } + + /** + * Get extension information from database + * + * @param string $element The extension element name + * @param string $type The extension type: component, plugin, library e.t.c + * @param mixed $folder The plugin folder: system, content e.t.c + * + * @return array + */ + public static function get($element, $type = 'component', $folder = null) + { + // Check if element is already cached + $hash = md5($element . '_' . $type . '_' . $folder); + if (isset(self::$cache[$hash])) + { + return self::$cache[$hash]; + } + + // Let's call the database + $db = Factory::getDBO(); + + switch ($type) + { + case 'component': + $element = 'com_' . str_replace('com_', '', $element); + break; + case 'module': + $element = 'mod_' . str_replace('mod_', '', $element); + break; + } + + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($element)) + ->where($db->quoteName('type') . ' = ' . $db->quote($type)); + + if (!is_null($folder)) + { + $query->where($db->quoteName('folder') . ' = ' . $db->quote($folder)); + } + + $db->setQuery($query); + + return self::$cache[$hash] = $db->loadAssoc(); + } + + /** + * Get framework plugin data + * + * @return array + */ + public static function getFramework() + { + return self::get('nrframework', 'plugin', 'system'); + } + + /** + * Helper method to check if a plugin is enabled + * + * @param string $element The extension element name + * @param string $type The extension type: component, plugin, library e.t.c + * + * @return boolean + */ + public static function pluginIsEnabled($element, $folder = 'system') + { + return self::isEnabled($element, 'plugin', $folder); + } + + /** + * Update an extension's params. + * + * @param string $name The extension name + * @param string $type The extension type + * @param string $element The extension element + * @param array $params The new params + * + * @return bool + */ + public static function updateExtensionParams($name = '', $type = '', $element = '', $params = []) + { + if (empty($name) || empty($type) || empty($element) || empty($params)) + { + return false; + } + + // Update params + $db = Factory::getDBO(); + + // Update params + $query = $db->getQuery(true) + ->update('#__extensions') + ->set($db->quoteName('params') . ' = ' . $db->quote(json_encode($params))) + ->where($db->quoteName('name') . ' = ' . $db->quote($name)) + ->where($db->quoteName('type') . ' = ' . $db->quote($type)) + ->where($db->quoteName('element') . ' = ' . $db->quote($element)); + $db->setQuery($query); + $db->execute(); + + return true; + } + + /** + * Helper method to check if a component is enabled + * + * @param string $element The component element name + * + * @return boolean + */ + public static function componentIsEnabled($element) + { + return self::isEnabled($element); + } + + /** + * Checks if an extension is enabled + * + * @param string $element The extension element name + * @param string $type The extension type: component, plugin, library e.t.c + * @param mixed $folder The plugin folder: system, content e.t.c + * + * @return boolean + */ + public static function isEnabled($element, $type = 'component', $folder = 'system') + { + switch ($type) + { + case 'component': + if (!$extension = self::get($element)) + { + return false; + } + + return (bool) $extension['enabled']; + break; + + case 'plugin': + if (!$extension = self::get($element, $type = 'plugin', $folder)) + { + return false; + } + + return (bool) $extension['enabled']; + break; + } + } + + /** + * Checks if an extension is installed + * + * @param string $extension The extension element name + * @param string $type The extension's type + * @param string $folder Plugin folder + * + * @return boolean Returns true if extension is installed + */ + public static function isInstalled($extension, $type = 'component', $folder = 'system') + { + $db = Factory::getDbo(); + + switch ($type) + { + case 'component': + $extension_data = self::get('com_' . str_replace('com_', '', $extension)); + return isset($extension_data['extension_id']); + break; + + case 'plugin': + return file_exists(JPATH_PLUGINS . '/' . $folder . '/' . $extension . '/' . $extension . '.php'); + + case 'module': + return (file_exists(JPATH_ADMINISTRATOR . '/modules/mod_' . $extension . '/' . $extension . '.php') + || file_exists(JPATH_ADMINISTRATOR . '/modules/mod_' . $extension . '/mod_' . $extension . '.php') + || file_exists(JPATH_SITE . '/modules/mod_' . $extension . '/' . $extension . '.php') + || file_exists(JPATH_SITE . '/modules/mod_' . $extension . '/mod_' . $extension . '.php') + ); + + case 'library': + return is_dir(JPATH_LIBRARIES . '/' . $extension); + } + + return false; + } + + /** + * Discover extension's name based on the query string + * + * @param boolean $translate If set to yes, the name will be returned translated + * + * @return string + */ + public static function getExtensionNameByRequest($translate = false) + { + $input = Factory::getApplication()->input; + $option = $input->get('option'); + + $name = ''; + + switch ($option) + { + case 'com_modules': + $name = 'com_smilepack'; + break; + case 'com_fields': + $name = 'plg_system_acf'; + break; + case 'com_plugins': + $plugin = self::getByID($input->get('extension_id')); + + if (is_array($plugin)) + { + $name = $plugin['name']; + } + break; + case 'com_config': + $component = $input->get('component'); + + $extension = self::get($component); + + if (is_array($extension)) + { + $name = $extension['name']; + } + + break; + default: + $name = $option; + break; + } + + if ($translate) + { + $name = explode(' - ', Text::_($name)); + return end($name); + } + + return $name; + } + + /** + * Returns Tassos.gr extension checkout URL + * + * @param string $name The extension's element name + * @param bool $append_utm Set whether to append the UTM parameters + * + * @return string + */ + public static function getTassosExtensionUpgradeURL($name = null, $append_utm = true) + { + $name = is_null($name) ? strtolower(self::getExtensionNameByRequest()) : $name; + + $suffix = $append_utm ? '?utm_source=Joomla&utm_medium=upgradebutton&utm_campaign=freeversion' : ''; + + return self::$product_base_url . '/' . self::getProductAlias($name) . '/upgrade-to-pro' . $suffix; + } + + public static function getProductAlias($extension) + { + $extension = is_null($extension) ? self::getExtensionNameByRequest() : $extension; + + switch ($extension) + { + case 'com_gsd': case 'plg_system_gsd': return 'google-structured-data'; + case 'com_rstbox': return 'engagebox'; + case 'com_convertforms': return 'convert-forms'; + case 'com_smilepack': return 'smile-pack'; + case 'plg_system_tweetme': return 'tweetme'; + case 'plg_system_acf': return 'advanced-custom-fields'; + } + } + + public static function getExtensionName($extension_element = null) + { + if (!$extension_element) + { + return; + } + + // Load extension's language file + Functions::loadLanguage($extension_element); + + // Remove plugin folder prefix from plugins + return str_replace('System -', '', Text::_($extension_element)); + } + + public static function getProductURL($extension) + { + return self::$product_base_url . '/' . self::getProductAlias($extension); + } + + public static function getPath($element) + { + $parts = explode('_', $element); + + switch ($parts[0]) + { + case 'com': + return JPATH_ADMINISTRATOR . '/components/' . $element; + case 'plg': + return JPATH_SITE . '/plugins/' . $parts[1] . '/' . $parts[2]; + } + } + + public static function getVersion($extension, $include_type = false) + { + $xml = self::getXML($extension); + + if (!$xml || !isset($xml->version)) + { + return; + } + + $version = (string) $xml->version; + + // If enabled, it returns EngageBox Pro + if ($include_type) + { + $isPro = self::isPro($extension); + $version_type = $isPro ? 'Pro' : 'Free'; + $version .= ' ' . $version_type; + } + + return $version; + } + + public static function elementToAlias($element) + { + $parts = explode('_', $element); + return end($parts); + } + + public static function getXML($element) + { + if (!$path = self::getPath($element)) + { + return; + } + + $extension_alias = self::elementToAlias($element); + $xml = $path . '/' . $extension_alias . '.xml'; + + return simplexml_load_file($xml); + } + + /** + * Returns a URL where we can check for extension updates. + * + * @param strong $extension + * + * @return mixed Null of fail, String on success + */ + public static function getUpdateServer($extension) + { + $xml = self::getXML($extension); + + if (!$xml || !isset($xml->updateservers)) + { + return; + } + + $updateserver = trim($xml->updateservers->server); + + // Remove unwanted string added by Free / Pro versions + $pp = strpos($updateserver, '@'); + if ($pp !== false) + { + $updateserver = substr($updateserver, 0, $pp); + } + + return $updateserver; + } + + /** + * Get the latest extension version from the remote update server + * + * @param string $extension + * + * @return mixed Null on failure, String on success + */ + public static function getLatestVersion($extension) + { + // Get the extension's update server URL + if (!$updateserver = self::getUpdateServer($extension)) + { + return; + } + + // Call the Update Server and make sure the response is valid + $response = HttpFactory::getHttp()->get($updateserver); + + if ($response->code != 200 || strpos($response->body, '') === false) + { + return; + } + + $body = new \SimpleXMLElement($response->body); + + $version = (string) $body->update[0]->version; + + return $version; + } + + /** + * Check if we have the Pro version of the extension + * + * @param string $element + * + * @return bool + */ + public static function isPro($element) + { + if (!$path = self::getPath($element)) + { + return false; + } + + $versionFile = $path . '/version.php'; + + // If version file does not exist we assume a PRO version + if (!file_exists($versionFile)) + { + return true; + } + + require $versionFile; + + // If the NR_PRO variable is not set we're probably under development mode. Assume a Pro version. + if (!isset($NR_PRO)) + { + return true; + } + + return (bool) $NR_PRO; + } + + /** + * Checks whether an extension is outdated. + * + * @param string $extension + * @param int $days_old + * + * @return bool + */ + public static function isOutdated($extension, $days_old = 120) + { + $versionFile = Functions::getExtensionPath($extension) . "/version.php"; + + if (!file_exists($versionFile)) + { + return false; + } + + require $versionFile; + + if (!isset($RELEASE_DATE)) + { + return false; + } + + if (!$then = strtotime($RELEASE_DATE)) + { + return false; + } + + $days_old = (int) $days_old; + $now = time(); + $diff = $now - $then; + $days_diff = round($diff / (60 * 60 * 24)); + + if ($days_diff <= $days_old) + { + return false; + } + + return true; + } + + /** + * Checks whether the geolocation plugin needs an update. + * + * @return bool + */ + public static function geoPluginNeedsUpdate() + { + // Check if TGeoIP plugin is enabled + if (!self::pluginIsEnabled('tgeoip')) + { + return false; + } + + $plugin_path = JPATH_PLUGINS . '/system/tgeoip/'; + + // Load plugin language (Needed by Joomla 4) + Factory::getLanguage()->load('plg_system_tgeoip', $plugin_path); + + // Load TGeoIP classes + @include_once $plugin_path . 'vendor/autoload.php'; + @include_once $plugin_path . 'helper/tgeoip.php'; + + if (!class_exists('TGeoIP')) + { + return false; + } + + // Check if database needs update. + $geo = new \TGeoIP(); + if (!$geo->needsUpdate()) + { + return false; + } + + // Database is too old and needs an update! Let's inform user. + return true; + } + + /** + * Returns the extension's JED URL. + * + * @param string $xml_folder + * + * @return string + */ + public static function getExtensionJEDURL($xml_folder = null) + { + if (empty($xml_folder)) + { + return; + } + + $url = 'https://extensions.joomla.org/extensions/extension/'; + + switch ($xml_folder) { + case 'com_smilepack': + $url .= 'smile-pack'; + break; + case 'com_rstbox': + $url .= 'style-a-design/popups-a-iframes/engage-box'; + break; + case 'com_convertforms': + $url .= 'contacts-and-feedback/forms/convert-forms'; + break; + case 'plg_system_acf': + $url .= 'authoring-a-content/content-construction/advanced-custom-fields'; + break; + case 'plg_system_gsd': + $url .= 'search-a-indexing/web-search/google-structured-data'; + break; + case 'tm_mailchimpuserautoadd': + $url .= 'marketing/mailing-a-newsletter-bridges/user-auto-add-to-mailchimp-for-joomla'; + break; + case 'tweetme': + $url .= 'social-web/social-share/tweetme'; + break; + case 'mod_webhotelier': + $url .= 'vertical-markets/booking-a-reservations/webhotelier-booking-form'; + break; + } + + return $url; + } + + /** + * Returns the installation date of an extension given its extension element. + * + * @param string $element + * + * @return string + */ + public static function getInstallationDate($element = null) + { + $alias = self::getExtensionDataFileAlias($element); + + $path = self::getExtensionsDataFilePath(); + + // If file does not exist, abort + if (!file_exists($path)) + { + return; + } + + // If file exists, retrieve its contents + $content = file_get_contents($path); + + // Decode it + if (!$content = json_decode($content, true)) + { + return; + } + + // If no installation date exists, abort + if (!isset($content[$alias])) + { + return; + } + + // Ensure install date exists + if (!isset($content[$alias]['install_date'])) + { + return; + } + + return $content[$alias]['install_date']; + } + + /** + * Sets the installation date of an extension. + * + * @param string $element + * @param string $install_date + * + * @return bool + */ + public static function setInstallationDate($element = null, $install_date = null) + { + $alias = self::getExtensionDataFileAlias($element); + + $path = self::getExtensionsDataFilePath(); + + // If file does not exist, abort + if (!file_exists($path)) + { + \NRFramework\File::createDirs(dirname($path)); + + file_put_contents($path, json_encode([ + $alias => [ + 'install_date' => $install_date + ] + ])); + return; + } + + // If file exists, retrieve its contents + $content = file_get_contents($path); + + // Decode it + $content = json_decode($content, true); + + if (!isset($content[$alias])) + { + $content[$alias]['install_date'] = $install_date; + } + else + { + foreach ($content as $key => &$value) + { + if ($key !== $alias) + { + continue; + } + + if (isset($value['install_date'])) + { + return false; + } + + $value['install_date'] = $install_date; + } + } + + file_put_contents($path, json_encode($content)); + } + + /** + * Returns all extensions file details. + * + * @return array + */ + public static function getExtensionsFileDetails() + { + $file = self::getExtensionsDataFilePath(); + + if (!file_exists($file)) + { + return []; + } + + if (!$data = file_get_contents($file)) + { + return []; + } + + if (!$data = json_decode($data, true)) + { + return []; + } + + return $data; + } + + /** + * Returns an extension's alias used to find an extensions data within the extensions.json data file. + * + * @param string $element + * + * @return string + */ + public static function getExtensionDataFileAlias($element) + { + $element = str_replace('com_', '', $element); + + switch ($element) { + case 'rstbox': + $element = 'engagebox'; + break; + case 'smilepack': + $element = 'smile-pack'; + break; + } + + return $element; + } + + /** + * The file path that stores all extensions data. + * + * @return string + */ + public static function getExtensionsDataFilePath() + { + return JPATH_SITE . '/media/plg_system_nrframework/data/extensions.json'; + } + + /** + * Returns all extensions details. + * + * @return array + */ + public static function getExtensionsDetails() + { + return [ + 'smilepack' => [ + 'extension' => 'com_smilepack', + 'type' => 'component' + ], + 'engagebox' => [ + 'extension' => 'com_rstbox', + 'type' => 'component' + ], + 'gsd' => [ + 'extension' => 'com_gsd', + 'type' => 'component' + ], + 'acf' => [ + 'extension' => 'acf', + 'type' => 'plugin' + ], + 'convertforms' => [ + 'extension' => 'com_convertforms', + 'type' => 'component' + ] + ]; + } + + /** + * Returns the number of tassos.gr installed extensions. + * + * @return int + */ + public static function getTotalInstalledExtensions() + { + $installed = 0; + + foreach (self::getExtensionsDetails() as $key => $value) + { + if (!self::isInstalled($value['extension'], $value['type'])) + { + continue; + } + + $installed++; + } + + return $installed; + } + + /** + * Returns the number of users active subscription plans. + * + * @param array $license_data + * + * @return int + */ + public static function getUserTotalPaidPlans($license_data = []) + { + if (!$license_data) + { + return 0; + } + + $count = 0; + + foreach ($license_data as $key => $value) + { + if (!isset($value['active'])) + { + continue; + } + + if (!$value['active']) + { + continue; + } + + $count++; + } + + return $count; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Factory.php b/plugins/system/nrframework/NRFramework/Factory.php new file mode 100644 index 00000000..6e25231f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Factory.php @@ -0,0 +1,125 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework; + +use \NRFramework\WebClient; +use \NRFramework\CacheManager; +use Joomla\CMS\Factory as JoomlaFactory; +use Joomla\CMS\Uri\Uri; + +defined('_JEXEC') or die; + +/** +* Framework Factory Class +* +* Used to decouple the framework from it's dependencies and make unit testing easier. +* +* @todo Rename class to Container and make all methods static. +*/ +class Factory +{ + public function isFrontend() + { + return $this->getApplication()->isClient('site'); + } + + public static function getCondition($name) + { + return \NRFramework\Conditions\ConditionsHelper::getInstance()->getCondition($name); + } + + public function getDbo() + { + return JoomlaFactory::getDbo(); + } + + public function getApplication() + { + $app = JoomlaFactory::getApplication(); + + // The 'forward_context' parameter is used to forward data from one page to another. This is rather useful in XHR requests. + // This is mainly used in Convert Forms to make the {article} Smart Tag work after form submission. + if ($context = $app->input->get('forward_context', '', 'raw')) + { + if (is_string($context)) + { + try + { + $context = json_decode($context, true); + $app->input->set('forward_context', $context); + } catch (\Throwable $th) + { + } + } + } + + return $app; + } + + public function getCookie($cookie_name) + { + return JoomlaFactory::getApplication()->input->cookie->get($cookie_name, null, 'string'); + } + + public function getDocument() + { + return JoomlaFactory::getDocument(); + } + + public function getUser($id = null) + { + return \NRFramework\User::get($id); + } + + public function getCache() + { + return CacheManager::getInstance(JoomlaFactory::getCache('tassos', '')); + } + + public function getDate($date = 'now', $tz = null) + { + return JoomlaFactory::getDate($date, $tz); + } + + public function getURI() + { + return Uri::getInstance(); + } + + public function getURL() + { + return Uri::getInstance()->toString(); + } + + public function getLanguage() + { + return JoomlaFactory::getLanguage(); + } + + public function getSession() + { + return JoomlaFactory::getSession(); + } + + public function getDevice() + { + return WebClient::getDeviceType(); + } + + public function getBrowser() + { + return WebClient::getBrowser(); + } + + public function getExecuter($php_code) + { + return new \NRFramework\Executer($php_code); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/File.php b/plugins/system/nrframework/NRFramework/File.php new file mode 100644 index 00000000..4c90b948 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/File.php @@ -0,0 +1,398 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +defined( '_JEXEC' ) or die( 'Restricted access' ); + +use NRFramework\Mimes; +use Joomla\Filesystem\File as JoomlaFile; +use Joomla\Filesystem\Path; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Factory; + +class File +{ + /** + * Upload file + * + * @param array $file The request file as posted by form + * @param string $upload_folder The upload folder where the file must be uploaded + * @param string $allowed_file_types A comma separated list of allowed file types like: .jpg, .gif, .png + * @param bool $allow_unsafe Allow the upload of unsafe files. See JFilterInput::isSafeFile() method. + * @param bool $random_prefix If is set to true, the filename will get a random unique prefix + * @param bool $random_suffix If is set to true, the filename will get a random unique suffix + * + * @return mixed String on success, Null on failure + */ + public static function upload($file, $upload_folder = null, $allowed_file_types = [], $allow_unsafe = false, $random_prefix = null, $random_suffix = false) + { + // Make sure we have a valid file array + if (!isset($file['name']) || !isset($file['tmp_name'])) + { + self::error(Text::sprintf('NR_UPLOAD_ERROR_CANNOT_UPLOAD_FILE', $file['name'])); + } + + // Check file type + self::checkMimeOrDie($allowed_file_types, $file); + + /** + * Try transiterating the file name using the native php function + * + * If the given filename is non-latin, then all characters will be removed from the filename via makeSafe and thus + * we wont be able to upload the file. + * + * @see https://github.com/joomla/joomla-cms/pull/27974 + */ + if (!defined('t_isJ5') && function_exists('transliterator_transliterate') && function_exists('iconv')) + { + // Using iconv to ignore characters that can't be transliterated + $file['name'] = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", transliterator_transliterate('Any-Latin; Latin-ASCII;', $file['name'])); + } + + // Sanitize filename + $filename = JoomlaFile::makeSafe($file['name']); + + if (!is_null($random_prefix)) + { + $filename = uniqid($random_prefix) . '_' . $filename; + } + + if (is_bool($random_suffix) && $random_suffix === true) + { + $file_data = File::pathinfo($filename); + $filename = $file_data['filename'] . '_' . uniqid($random_suffix) . '.' . $file_data['extension']; + } + + $filename = str_replace(' ', '_', $filename); + + // Setup the full file name + $upload_folder = is_null($upload_folder) ? self::getTempFolder() : $upload_folder; + $destination_file = implode(DIRECTORY_SEPARATOR, [$upload_folder, $filename]); + + // If file exists, rename to copy_X + self::uniquefy($destination_file); + + $destination_file = Path::clean($destination_file); + + if (!JoomlaFile::upload($file['tmp_name'], $destination_file, false, $allow_unsafe)) + { + self::error(Text::sprintf('NR_UPLOAD_ERROR_CANNOT_UPLOAD_FILE', $file['name'])); + } + + return $destination_file; + } + + /** + * Moves a file from one directory to another. Destination directories will be created if they are not exist. + * + * @param string $source_file The source file path + * @param string $destination_file The destination file path + * @param bool $replace_existing Replace same files names, otherwise create a copy in the format copy_X + * + * @return mixed String on success + */ + public static function move($source_file, $destination_file, $replace_existing = false, $hash = false) + { + $destination_folder = dirname($destination_file); + + // Create destination folders recursively + if (!self::createDirs($destination_folder)) + { + self::error(Text::sprintf('NR_CANNOT_CREATE_FOLDER', $destination_folder)); + } + + // Don't replace files with the same name. Instead, append copy_x to this one. + if (!$replace_existing) + { + self::uniquefy($destination_file, $hash); + } + + // Move file to the destination folder + if (!JoomlaFile::move($source_file, $destination_file)) + { + self::error(Text::sprintf('NR_CANNOT_MOVE_FILE', $destination_file)); + } + + return Path::clean($destination_file); + } + + /** + * Copies a file from one directory to another. + * + * @param string $source_file The source file path + * @param string $destination_file The destination file path + * @param bool $replace_existing Replace same files names, otherwise create a copy in the format copy_X + * @param bool $hash Whether to md5 hash the filename + * + * @return mixed String on success + */ + public static function copy($source_file, $destination_file, $replace_existing = false, $hash = false) + { + $destination_folder = dirname($destination_file); + + // Create destination folders recursively + if (!self::createDirs($destination_folder)) + { + self::error(Text::sprintf('NR_CANNOT_CREATE_FOLDER', $destination_folder)); + } + + // Don't replace files with the same name. Instead, append copy_x to this one. + if (!$replace_existing) + { + self::uniquefy($destination_file, $hash); + } + + // Copy file to the destination folder + if (!JoomlaFile::copy($source_file, $destination_file)) + { + self::error(Text::sprintf('NR_CANNOT_MOVE_FILE', $destination_file)); + } + + return Path::clean($destination_file); + } + + /** + * Reads (and checks) the temp Joomla folder + * + * @return string + */ + public static function getTempFolder() + { + $ds = DIRECTORY_SEPARATOR; + + $tmpdir = Factory::getConfig()->get('tmp_path'); + + if (realpath($tmpdir) == $ds . 'tmp') + { + $tmpdir = JPATH_SITE . $ds . 'tmp'; + } + + elseif (!is_dir($tmpdir)) + { + $tmpdir = JPATH_SITE . $ds . 'tmp'; + } + + return Path::clean(trim($tmpdir) . $ds); + } + + /** + * Checks if the path exists. If not creates the folders as well as subfolders. + * + * @param string $path The folder path + * @param string $protect If set to true, each folder will be protected by disabling PHP engine and preventing folder browsing + * + * @return bool + */ + public static function createDirs($path, $protect = true) + { + if (!is_dir($path)) + { + mkdir($path, 0755, true); + + // New folder created. Let's protect it. + if ($protect) + { + self::writeHtaccessFile($path); + self::writeIndexHtmlFile($path); + } + } + + // Make sure the folder is writable + return @is_writable($path); + } + + /** + * Checks whether a file type is in an allowed list + * + * @param mixed $allowed_types Array or a comma separated list of allowed file extensions or mime types. Eg: .jpg, .png, applicaton/pdf + * @param string $file_object The uploaded file as appears in the $_FILES array + * + * @return bool + */ + public static function checkMimeOrDie($allowed_types, $file_object) + { + $file_path = $file_object['tmp_name']; + $file_name = isset($file_object['name']) ? $file_object['name'] : basename($file_path); + $safeFilename = strip_tags($file_name); + $fileExtension = pathinfo($file_name, PATHINFO_EXTENSION); + + // First, validate file by its extension + if (!Mimes::validateFileExtension($fileExtension, $allowed_types)) + { + self::error(Text::sprintf('NR_UPLOAD_INVALID_FILE_EXT', $safeFilename, $fileExtension, $allowed_types)); + } + + // Do we have a mime type detected? + if (!$mime_type = Mimes::detectFileType($file_path)) + { + self::error(Text::sprintf('NR_UPLOAD_NO_MIME_TYPE', $safeFilename)); + } + + if (!Mimes::check($allowed_types, $mime_type)) + { + self::error(Text::sprintf('NR_UPLOAD_INVALID_FILE_TYPE', $safeFilename, $mime_type, $allowed_types)); + } + } + + /** + * Add an .htaccess file to the folder in order to disable PHP engine entirely + * + * @param string $path The path where to write the file + * + * @return void + */ + public static function writeHtaccessFile($path) + { + $content = ' + # Block direct PHP access + + + Deny from all + + + Require all denied + + + '; + + JoomlaFile::write($path . '/.htaccess', $content); + } + + /** + * Creates an empty index.html file to prevent directory listing + * + * @param string $path The path where to write the file + * + * @return void + */ + public static function writeIndexHtmlFile($path) + { + $content = ''; + + JoomlaFile::write($path . '/index.html', $content); + } + + /** + * Generates a unique filename in case the give name already exists by appending copy_X suffix to filename. + * + * @param string $path The path to the file. + * @param bool $hash MD5 hashes the file name. + * + * @return void + */ + public static function uniquefy(&$path, $hash = false) + { + $path_parts = self::pathinfo($path); + + $dir = $path_parts['dirname']; + $ext = $path_parts['extension']; + $actual_name = $path_parts['filename']; + + $original_name = $actual_name; + + // md5 hash the file name + if ($hash) + { + $actual_name = md5($actual_name); + + // Initialize again the path due to md5 hash + $path = $dir . '/' . $actual_name . '.' . $ext; + } + + $i = 1; + + while(file_exists($dir . '/' . $actual_name . '.' . $ext)) + { + $actual_name = (string) $original_name . '_copy_' . $i; + + // md5 hash the file name + if ($hash) + { + $actual_name = md5($actual_name); + } + + $path = $dir . '/' . $actual_name . '.' . $ext; + $i++; + } + } + + /** + * Returns information about a file path with multi-byte support + * + * @param string $path The path to be parsed. + * + * @return array + */ + public static function pathinfo($path) + { + // Store temporary the currenty locale + $currentLocale = setlocale(LC_ALL, 0); + + setlocale(LC_ALL, 'C.UTF-8'); + $pathinfo = pathinfo($path); + + // Set back to previus value + setlocale(LC_ALL, $currentLocale); + + return $pathinfo; + } + + /** + * Force download of the exported file + * + * @return void + */ + public static function download($filename, $path = null) + { + $path = is_null($path) ? self::getTempFolder() : $path; + $filename = $path . '/' . $filename; + + if (!is_file($filename)) + { + self::error('Invalid filename ' . $filename); + } + + error_reporting(0); + + // Send the appropriate headers to force the download in the browser + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . basename($filename) . '"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Cache-Control: public', false); + header('Pragma: public'); + header('Content-Length: ' . @filesize($filename)); + + // Clear the output buffer and disable output buffering + ob_clean(); + flush(); + + // Read exported file to buffer + readfile($filename); + + // Don't leave any clues on the server. Delete the file. + JoomlaFile::delete($filename); + + jexit(); + } + + /** + * Throw a sanitized exception + * + * @param string $error + * + * @return void + */ + private static function error($error) + { + throw new \Exception(htmlspecialchars($error, ENT_QUOTES, 'UTF-8')); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Fonts.php b/plugins/system/nrframework/NRFramework/Fonts.php new file mode 100644 index 00000000..a1ccad70 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Fonts.php @@ -0,0 +1,133 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +defined( '_JEXEC' ) or die( 'Restricted access' ); + +use Joomla\CMS\Factory; + +/** + * Fonts Class + */ +class Fonts +{ + /** + * Classic Fonts + * + * @var array + */ + private static $classic = array( + 'Arial', + 'Arial Black', + 'Georgia', + 'Tahoma', + 'Franklin Gothic Medium', + 'Calibri', + 'Cambria', + 'Century Gothic', + 'Consolas', + 'Corbel', + 'Courier New', + 'Times New Roman', + 'Impact', + 'Lucida Console', + 'Palatino Linotype', + 'Trebuchet MS', + 'Verdana' + ); + + /** + * Google Fonts List + * + * @var array + */ + private static $google = array( + 'Roboto', + 'Staatliches', + 'Thasadith', + 'Open Sans', + 'Sarabun', + 'Slabo 27px', + 'Lato', + 'Oswald', + 'Charm', + 'Roboto Condensed', + 'Source Sans Pro', + 'Montserrat', + 'Raleway', + 'PT Sans', + 'Poppins', + 'Roboto Slab', + 'Lora', + 'Droid Sans', + 'Merriweather', + 'Ubuntu', + 'Droid Serif', + 'Arimo', + 'Noto Sans', + 'PT Sans Narro' + ); + + /** + * Returns all font groups alphabetically sorted + * + * @return array + */ + public static function getFontGroups() + { + return array( + 'Google Fonts' => self::getFontGroup('google'), + 'Classic' => self::getFontGroup('classic') + ); + } + + /** + * Returns a font group alphabetically sorted + * + * @param string $name The Font Group + * + * @return array + */ + public static function getFontGroup($name) + { + $fonts = self::$$name; + sort($fonts); + return $fonts; + } + + /** + * Loads Google font to the document + * + * @param mixed $name The Google font name + * + * @return void + */ + public static function loadFont($names) + { + if (!$names) + { + return; + } + + if (!is_array($names)) + { + $names = array($names); + } + + foreach ($names as $key => $value) + { + // If font is a Google Font then load it into the document + if (in_array($value, self::$google)) + { + Factory::getDocument()->addStylesheet('//fonts.googleapis.com/css?family=' . urlencode($value)); + } + } + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Functions.php b/plugins/system/nrframework/NRFramework/Functions.php new file mode 100644 index 00000000..87bb462d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Functions.php @@ -0,0 +1,771 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +defined('_JEXEC') or die; + +use Joomla\Registry\Registry; +use \NRFramework\Cache; +use Joomla\CMS\Factory; +use Joomla\CMS\Date\Date; +use Joomla\CMS\Uri\Uri; +use Joomla\Filesystem\File; +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Helper\ModuleHelper; + +class Functions +{ + /** + * Add HTML before the closing tag. + * + * @param string $html The HTML to prepend to + * @param boolean $once If true, the HTML will be added only once. + * + * @return void + */ + public static function appendToBody($html, $once = false) + { + if ($once) + { + $hash = md5($html); + + if (Cache::has($hash)) + { + return; + } + + Cache::set($hash, true); + } + + $app = Factory::getApplication(); + + $app->registerEvent('onAfterRender', function() use ($app, $html) + { + $buffer = $app->getBody(); + + $closingTag = ''; + + if (strpos($buffer, $closingTag)) + { + // If exists prepend the given HTML + $buffer = str_replace($closingTag, $html . $closingTag, $buffer); + } else + { + // If does not exist append to document's end + $buffer .= $html; + } + + $app->setBody($buffer); + }); + } + + /** + * Fix arrays, remove duplicate items, null items and whitespace around item values. + * + * @param array $subject + * + * @return array The new cleaned array + */ + public static function cleanArray($subject) + { + if (!is_array($subject)) + { + return $subject; + } + + $subject = array_map(function($str) + { + return is_null($str) ? '' : trim($str); + }, $subject); + + $subject = array_unique($subject); + + // Remove empty items. We use a custom callback here because the default behavior of array_filter removes 0 values as well. + $subject = array_filter($subject, function($value) + { + return ($value !== null && $value !== false && $value !== ''); + }); + + return $subject; + } + + /** + * Attempt to convert a subject to array + * + * @param mixed $subject + * + * @return array + */ + public static function makeArray($subject) + { + if (empty($subject)) + { + return []; + } + + if (is_object($subject)) + { + return (array) $subject; + } + + if (!is_array($subject)) + { + // replace newlines with commas + $subject = str_replace(PHP_EOL, ',', $subject); + + // split keywords on commas + $subject = explode(',', $subject); + } + + // Now that we have an array, run some housekeeping. + $arr = $subject; + + $arr = self::cleanArray($arr); + + // Reset keys + $arr = array_values($arr); + + return $arr; + } + + /** + * Return the real site base URL by ignoring the live_site configuration option. + * + * @param bool $ignore_admin If enabled and we are browsing administrator, we will get the front-end site root URL. + * + * @return string + */ + public static function getRootURL($ignore_admin = true) + { + $factory = Factory::getConfig(); + + // Store the original live_site value + $live_site_original = $factory->get('live_site', ''); + + // If we live_site is not set, do not proceed further. Return the default website base URL. + if (empty($live_site_original)) + { + return $ignore_admin ? Uri::root() : Uri::base(); + } + + // Remove the live site + $factory->set('live_site', ''); + + // Remove all cached Uri instances + Uri::reset(); + + // Get a new URL. The live_site option should be ignored. + $base_url = $ignore_admin ? Uri::root() : Uri::base(); + + // Set back the original live_site + $factory->set('live_site', $live_site_original); + Uri::reset(); + + return $base_url; + } + + /** + * Insert an associative array into a specific position in an array + * + * @param $original array The original array to add to + * @param $new array The new array of values to insert into the original + * @param $offset int The position in the array ( 0 index ) where the new array should go + * + * @return array The new combined array + */ + public static function array_splice_assoc($original,$new,$offset) + { + return array_slice($original, 0, $offset, true) + $new + array_slice($original, $offset, NULL, true); + } + + public static function renderField($fieldname) + { + $fieldname = strtolower($fieldname); + + require_once JPATH_PLUGINS . '/system/nrframework/fields/' . $fieldname . '.php'; + + $classname = '\JFormField' . $fieldname; + + $field = new $classname(); + + $element = new \SimpleXMLElement(' + '); + + $field->setup($element, null); + + return $field->__get('input'); + } + + /** + * Checks if an array of values (needle) exists in a text (haystack) + * + * @param array $needle The searched array of values. + * @param string $haystack The text + * @param bool $case_insensitive Indicates whether the letter case plays any role + * + * @return bool + */ + public static function strpos_arr($needles, $haystack, $case_insensitive = false) + { + $needles = !is_array($needles) ? (array) $needles : $needles; + $haystack = $case_insensitive ? strtolower($haystack) : $haystack; + + foreach ($needles as $needle) + { + $needle = $case_insensitive ? strtolower($needle) : $needle; + + if (strpos($haystack, $needle) !== false) + { + // stop on first true result + return true; + } + } + + return false; + } + + /** + * Log message to framework's log file + * + * @param mixed $data Log message + * + * @return void + * + * @deprecated Stop using method + */ + public static function log($data) + { + } + + /** + * Return's a URL with the Google Analytics Campaign Parameters appended to the end + * + * @param string $url The URL + * @param string $medium Campaign Medium + * @param string $campaign Campaign Name + * + * @return string + */ + public static function getUTMURL($url, $medium = 'upgradebutton', $campaign = 'freeversion') + { + if (!$url) + { + return; + } + + $utm = 'utm_source=CustomerBackend&utm_medium=' . $medium . '&utm_campaign=' . $campaign; + $char = strpos($url, '?') === false ? '?' : '&'; + + return $url . $char . $utm; + } + + /** + * Returns user's Download Key + * + * @return string + */ + public static function getDownloadKey() + { + $class = new Updatesites(); + return $class->getDownloadKey(); + } + + /** + * Adds a script or a stylesheet to the document + * + * @param Mixed $files The files to be to added to the document + * @param boolean $appendVersion Adds file versioning based on extension's version + * + * @return void + */ + public static function addMedia($files, $extension = "plg_system_nrframework", $appendVersion = true) + { + $doc = Factory::getDocument(); + $version = self::getExtensionVersion($extension); + $mediaPath = Uri::root(true) . "/media/" . $extension; + + if (!is_array($files)) + { + $files = array($files); + } + + foreach ($files as $key => $file) + { + $fileExt = File::getExt($file); + $filename = $mediaPath . "/" . $fileExt . "/" . $file; + $filename = ($appendVersion) ? $filename . "?v=" . $version : $filename; + + if ($fileExt == "js") + { + $doc->addScript($filename); + } + + if ($fileExt == "css") + { + $doc->addStylesheet($filename); + } + } + } + + /** + * Get the Framework version + * + * @return string The framework version + */ + public static function getVersion() + { + return self::getExtensionVersion("plg_system_nrframework"); + } + + /** + * Checks if document is a feed document (xml, rss, atom) + * + * @return boolean + */ + public static function isFeed() + { + return ( + Factory::getDocument()->getType() == 'feed' + || Factory::getDocument()->getType() == 'xml' + || Factory::getApplication()->input->getWord('format') == 'feed' + || Factory::getApplication()->input->getWord('type') == 'rss' + || Factory::getApplication()->input->getWord('type') == 'atom' + ); + } + + public static function loadLanguage($extension = 'plg_system_nrframework', $basePath = '') + { + if ($basePath && Factory::getLanguage()->load($extension, $basePath)) + { + return true; + } + + $basePath = self::getExtensionPath($extension, $basePath, 'language'); + + return Factory::getLanguage()->load($extension, $basePath); + } + + /** + * Returns extension ID + * + * @param string $extension Extension name + * + * @return integer + * + * @deprecated Use \NRFramework\Extension::getID instead + */ + public static function getExtensionID($extension, $folder = null) + { + $type = is_null($folder) ? 'component' : 'plugin'; + return \NRFramework\Extension::getID($extension, $type, $folder); + } + + /** + * Checks if extension is installed + * + * @param string $extension The extension element name + * @param string $type The extension's type + * @param string $folder Plugin folder * + * + * @return boolean Returns true if extension is installed + * + * @deprecated Use \NRFramework\Extension::isInstalled instead + */ + public static function extensionInstalled($extension, $type = 'component', $folder = 'system') + { + return \NRFramework\Extension::isInstalled($extension, $type, $folder); + } + + /** + * Returns the version number from the extension's xml file + * + * @param string $extension The extension element name + * + * @return string Extension's version number + */ + public static function getExtensionVersion($extension, $type = false) + { + $hash = MD5($extension . "_" . ($type ? "1" : "0")); + $cache = Cache::read($hash); + + if ($cache) + { + return $cache; + } + + $xml = self::getExtensionXMLFile($extension); + + if (!$xml) + { + return false; + } + + $xml = Installer::parseXMLInstallFile($xml); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + $version = $xml['version']; + + if ($type) + { + $extType = Extension::isPro($extension) ? 'Pro' : 'Free'; + $version = $xml["version"] . " " . $extType; + } + + return Cache::set($hash, $version); + } + + public static function getExtensionXMLFile($extension, $basePath = JPATH_ADMINISTRATOR) + { + $alias = explode("_", $extension); + $alias = end($alias); + + $filename = (strpos($extension, 'mod_') === 0) ? "mod_" . $alias : $alias; + $file = self::getExtensionPath($extension, $basePath) . "/" . $filename . ".xml"; + + if (file_exists($file)) + { + return $file; + } + + return false; + } + + /** + * @deprecated // Use Extension::isPro(); + */ + public static function extensionHasProInstalled($extension) + { + return Extension::isPro($extension); + } + + public static function getExtensionPath($extension = 'plg_system_nrframework', $basePath = JPATH_ADMINISTRATOR, $check_folder = '') + { + $path = ''; + + switch (true) + { + case (strpos($extension, 'com_') === 0): + $path = 'components/' . $extension; + break; + + case (strpos($extension, 'mod_') === 0): + $path = 'modules/' . $extension; + break; + + case (strpos($extension, 'plg_system_') === 0): + $path = 'plugins/system/' . substr($extension, strlen('plg_system_')); + break; + + case (strpos($extension, 'plg_editors-xtd_') === 0): + $path = 'plugins/editors-xtd/' . substr($extension, strlen('plg_editors-xtd_')); + break; + } + + if (empty($path)) + { + return; + } + + $check_folder = $check_folder ? '/' . $check_folder : ''; + $basePath = empty($basePath) ? JPATH_ADMINISTRATOR : $basePath; + + if (is_dir($basePath . '/' . $path . $check_folder)) + { + return $basePath . '/' . $path; + } + + if (is_dir(JPATH_ADMINISTRATOR . '/' . $path . $check_folder)) + { + return JPATH_ADMINISTRATOR . '/' . $path; + } + + if (is_dir(JPATH_SITE . '/' . $path . $check_folder)) + { + return JPATH_SITE . '/' . $path; + } + + return $basePath; + } + + public static function renderModulePosition($position, $style = 'custom') + { + $modules = ModuleHelper::getModules($position); + + $attribs['style'] = $style; + foreach ($modules as $module) + { + echo ModuleHelper::renderModule($module, $attribs); + } + } + + public static function loadModule($id, $moduleStyle = null) + { + // Return if no module id passed + if (!$id) + { + return; + } + + // Fetch module from db + $db = Factory::getDBO(); + $query = $db->getQuery(true) + ->select('*') + ->from('#__modules') + ->where('id='.$db->q($id)); + + $db->setQuery($query); + + // Return if no modules found + if (!$module = $db->loadObject()) + { + return; + } + + // Success! Return module's html + return ModuleHelper::renderModule($module, $moduleStyle); + } + + public static function fixDate(&$date) + { + if (!$date) + { + $date = null; + + return; + } + + $date = trim($date); + + // Check if date has correct syntax: 00-00-00 00:00:00 + if (preg_match('#^[0-9]+-[0-9]+-[0-9]+( [0-9][0-9]:[0-9][0-9]:[0-9][0-9])$#', $date)) + { + return; + } + + // Check if date has syntax: 00-00-00 00:00 + // If so, add :00 (seconds) + if (preg_match('#^[0-9]+-[0-9]+-[0-9]+ [0-9][0-9]:[0-9][0-9]$#', $date)) + { + $date .= ':00'; + + return; + } + + // Check if date has a prepending date syntax: 00-00-00 ... + // If so, add 00:00:00 (hours:mins;secs) + if (preg_match('#^([0-9]+-[0-9]+-[0-9]+)#', $date, $match)) + { + $date = $match[1] . ' 00:00:00'; + + return; + } + + // Date format is not correct, so return null + // $date = null; + } + + /** + * Change date's timezone to UTC by modyfing the offset + * + * @param string $date The date in timezone other than UTC + * + * @return string The date in UTC + */ + public static function dateToUTC($date) + { + $date = is_string($date) ? trim($date) : $date; + + if (empty($date) || is_null($date) || $date == '0000-00-00 00:00:00') + { + return $date; + } + + $timezone = Factory::getUser()->getParam('timezone', Factory::getConfig()->get('offset')); + + $date = new Date($date, $timezone); + $date->setTimezone(new \DateTimeZone('UTC')); + + $dateUTC = $date->format('Y-m-d H:i:s', true, false); + + return $dateUTC; + } + + /** + * Applies the site's or the user's timezone to a given date. + * + * @param string $date + * @param string $format + * + * @return string + */ + public static function applySiteTimezoneToDate($date, $format = 'Y-m-d H:i:s') + { + $timezone = new \DateTimeZone(Factory::getUser()->getParam('timezone', Factory::getConfig()->get('offset'))); + return Factory::getDate($date)->setTimezone($timezone)->format($format, true); + } + + /** + * Change date's timezone to UTC by modyfing the offset + * + * @param string $date The date in timezone other than UTC + * + * @return string The date in UTC + * + * @deprecated Use dateToUTC() + */ + public static function fixDateOffset(&$date) + { + $date = self::dateToUTC($date); + } + + // Text + public static function clean($string) + { + $string = str_replace(' ', '-', $string); // Replaces all spaces with hyphens. + return preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars. + } + + public static function dateTimeNow() + { + return Factory::getDate()->format("Y-m-d H:i:s"); + } + + /** + * Get framework plugin's parameters + * + * @return Registry The plugin parameters + */ + public static function params() + { + $hash = md5('frameworkParams'); + + if (Cache::has($hash)) + { + return Cache::read($hash); + } + + $db = Factory::getDBO(); + + $result = $db->setQuery( + $db->getQuery(true) + ->select('params') + ->from('#__extensions') + ->where('element = ' . $db->quote('nrframework')) + )->loadResult(); + + return Cache::set($hash, new Registry($result)); + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks whether string end with substring. + * + * @param string $string + * @param string $substring + * + * @return bool + */ + public static function endsWith($string, $substring) + { + $length = strlen($substring); + + return substr((string) $string, -$length) === $substring; + } + + /** + * Updates the Download Key in the framework plugin. + * + * @param string $key + * + * @return bool + */ + public static function updateDownloadKey($key) + { + if (empty($key)) + { + return false; + } + + // Update params + $db = Factory::getDBO(); + + // Get params + $query = $db->getQuery(true) + ->select($db->quoteName('params')) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('element') . ' = ' . $db->quote('nrframework')); + + $db->setQuery($query); + $params = $db->loadResult(); + + $params = json_decode($params, true); + + // Set Download Key + $params['key'] = $key; + + // Update params + $query->clear() + ->update('#__extensions') + ->set($db->quoteName('params') . ' = ' . $db->quote(json_encode($params))) + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('element') . ' = ' . $db->quote('nrframework')); + $db->setQuery($query); + $db->execute(); + + return true; + } + + /** + * Return the current page's body regardless the system event fires the method. + * + * @return string + */ + public static function getBuffer() + { + $app = Factory::getApplication(); + + // Try to get the whole body first. This will only work if the method is executed in the onAfterRender event. + if ($body = $app->getBody()) + { + return $body; + } + + // We got an empty body. Probably, the method runs before the onAfterRender event. Let's give it another try. + $buffer = $app->getDocument()->getBuffer(); + + if (!is_array($buffer)) + { + return; + } + + $flatted = \Joomla\Utilities\ArrayHelper::flatten($buffer); + + return implode(' ', $flatted); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/HTML.php b/plugins/system/nrframework/NRFramework/HTML.php new file mode 100644 index 00000000..59314515 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/HTML.php @@ -0,0 +1,508 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +// No direct access +defined('_JEXEC') or die; + +use NRFramework\Cache; +use NRFramework\Functions; +use NRFramework\Extension; +use Joomla\CMS\Language\Text; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Layout\LayoutHelper; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Session\Session; + +class HTML +{ + /** + * Display field help text as tooltip in Joomla 4 + * + * @return void + */ + public static function fixFieldTooltips() + { + // Run once + static $run; + + if ($run) + { + return; + } + + $run = true; + + HTMLHelper::_('bootstrap.popover'); + HTMLHelper::_('jquery.framework'); + + $doc = Factory::getDocument(); + + $doc->addStyleDeclaration(' + .form-text, .form-control-feedback { + display:none; + } + .tooltip .arrow:before { + border-top-color:#444; + border-bottom-color:#444; + } + .tooltip-inner { + text-align: left; + background-color: #444; + padding: 7px 9px; + max-width:300px; + } + '); + + $doc->addScriptDeclaration(' + document.addEventListener("DOMContentLoaded", function() { + initPopover(); + + document.addEventListener("joomla:updated", initPopover); + + function initPopover(event) { + var target = event && event.target ? event.target : document; + var fields = target.querySelectorAll(".control-group"); + + fields.forEach(function(field) { + var desc = field.querySelector(".form-text"); + + if (desc) { + var label = field.querySelector("label"); + + if (label) { + label.classList.add("tTooltip"); + label.setAttribute("title", desc.innerHTML); + } + } + }); + + jQuery(target).find(".tTooltip").tooltip({ + placement: "top", + html: true, + delay: { + show: 200 + } + }); + } + }); + '); + } + + /** + * Renders the HTML layout + * + * @param string $layout The HTML class of the layout. + * @param array $options A list of attributes passed to the layout + * + * @return string + */ + public static function render($layout, $options = []) + { + if (!$layout) + { + return; + } + + $class = '\NRFramework\HTML\\' . $layout; + + // ensure class exists + if (!class_exists($class)) + { + return; + } + + return (new $class($options))->render(); + } + + /** + * Renders Pro Button + * + * @param string $feature_label The text that will be used as the modal popup feature + * @param string $buttonClass The class of the button + * + * @return void + */ + public static function renderProButton($feature_label = null, $buttonClass = 'btn-sm') + { + include_once JPATH_PLUGINS . '/system/nrframework/fields/pro.php'; + + $field = new \JFormFieldNR_PRO; + $element = new \SimpleXMLElement(' + '); + + $field->setup($element, null); + + echo $field->__get('input'); + } + + /** + * Renders a modal that will be shown on Pro only features + * + * @param string $extension_name + * + * @return void + */ + public static function renderProOnlyModal($extension = null) + { + $hash = 'proOnlyModal'; + + // Render modal once + if (Cache::get($hash)) + { + return; + } + + $options = [ + 'extension_name' => is_null($extension) ? Extension::getExtensionNameByRequest(true) : Extension::getExtensionName($extension), + 'upgrade_url' => Extension::getTassosExtensionUpgradeURL($extension) + ]; + + $html = LayoutHelper::render('proonlymodal', $options, dirname(__DIR__) . '/layouts'); + + echo HTMLHelper::_('bootstrap.renderModal', 'proOnlyModal', ['backdrop' => 'static'], $html); + + Cache::set($hash, true); + } + + public static function smartTagsBox($options = array()) + { + HTMLHelper::_('jquery.framework'); + + include_once JPATH_PLUGINS . '/system/nrframework/fields/smarttagsbox.php'; + + $field = new \JFormFieldSmartTagsBox; + $element = new \SimpleXMLElement(''); + + $field->setup($element, null); + + return $field->__get('input'); + } + + /** + * Construct the HTML for the input field in a tree + * Logic from administrator\components\com_modules\views\module\tmpl\edit_assignment.php + */ + public static function treeselect(&$options, $name, $value, $id, $size = 300, $simple = 0, $class = '') + { + Functions::loadLanguage('com_menus', JPATH_ADMINISTRATOR); + Functions::loadLanguage('com_modules', JPATH_ADMINISTRATOR); + + if (empty($options)) + { + return '
' . Text::_('NR_NO_ITEMS_FOUND') . '
'; + } + + if (!is_array($value)) + { + $value = explode(',', $value); + } + + $count = 0; + if ($options != -1) + { + foreach ($options as $option) + { + $count++; + if (isset($option->links)) + { + $count += count($option->links); + } + } + } + + if ($options == -1) + { + if (is_array($value)) + { + $value = implode(',', $value); + } + if (!$value) + { + $input = ''; + } + else + { + $input = ''; + } + + return '
' . $input . '
'; + } + + if ($simple) + { + $attr = 'style="width: ' . $size . 'px" multiple="multiple"'; + + if (!empty($class)) + { + $attr .= ' class="' . $class . '"'; + } + + $html = HTMLHelper::_('select.genericlist', $options, $name, trim($attr), 'value', 'text', $value, $id); + + return $html; + } + + HTMLHelper::script('plg_system_nrframework/treeselect.js', ['relative' => true, 'version' => true]); + HTMLHelper::stylesheet('plg_system_nrframework/treeselect.css', ['relative' => true, 'version' => true]); + + $html = array(); + + $html[] = '
'; + $html[] = ' + '; + + $o = array(); + foreach ($options as $option) + { + $option->level = isset($option->level) ? $option->level : 0; + $o[] = $option; + if (isset($option->links)) + { + foreach ($option->links as $link) + { + $link->level = $option->level + (isset($link->level) ? $link->level : 1); + $o[] = $link; + } + } + } + + $html[] = '
    '; + $prevlevel = 0; + + foreach ($o as $i => $option) + { + if ($prevlevel < $option->level) + { + // correct wrong level indentations + $option->level = $prevlevel + 1; + + $html[] = '
      '; + } + else if ($prevlevel > $option->level) + { + $html[] = str_repeat('
    ', $prevlevel - $option->level); + } + else if ($i) + { + $html[] = ''; + } + + $labelclass = trim('pull-left ' . (isset($option->labelclass) ? $option->labelclass : '')); + + $html[] = '
  • '; + + $item = '
    '; + if (isset($option->title)) + { + $labelclass .= ' nav-header'; + } + + if (isset($option->title) && (!isset($option->value) || !$option->value)) + { + $item .= ''; + } + else + { + $selected = in_array($option->value, $value) ? ' checked="checked"' : ''; + $disabled = (isset($option->disable) && $option->disable) ? ' readonly="readonly" style="visibility:hidden"' : ''; + + $item .= ' + '; + } + $item .= '
    '; + $html[] = $item; + + if (!isset($o[$i + 1]) && $option->level > 0) + { + $html[] = str_repeat('
', (int) $option->level); + } + $prevlevel = $option->level; + } + $html[] = ''; + $html[] = ' + '; + $html[] = '
'; + + $html = implode('', $html); + return $html; + } + + public static function treeselectSimple(&$options, $name, $value, $id, $size = 300, $class = '') + { + return self::treeselect($options, $name, $value, $id, $size, 1, $class); + } + + /** + * Wrapper for the HTMLHelper::script method to support old method signatures in Joomla < 3.7.0. + * + * @param string $path + * + * @deprecated Since we no longer support 3.7.0, use HTMLHelper::script directly. + * @return void + */ + public static function script($path) + { + if (version_compare(JVERSION, '3.7.0', 'lt')) + { + HTMLHelper::script($path, false, true); + } else + { + HTMLHelper::script($path, ['relative' => true, 'version' => 'auto']); + } + } + + /** + * Wrapper for the HTMLHelper::stylesheet method to support old method signatures in Joomla < 3.7.0. + * + * @param string $path + * + * @return void + * @deprecated Since we no longer support 3.7.0, use HTMLHelper::script directly. + */ + public static function stylesheet($path) + { + if (version_compare(JVERSION, '3.7.0', 'lt')) + { + HTMLHelper::stylesheet($path, false, true); + } else + { + HTMLHelper::stylesheet($path, ['relative' => true, 'version' => 'auto']); + } + } + + /** + * For Backwards Compatibility + * + * @deprecated 4.9.50 + */ + public static function checkForOutdatedExtension($extension, $days_old = 120) + { + if (!Extension::isOutdated($extension, $days_old)) + { + return; + } + + // Load extension's language file + Functions::loadLanguage($extension); + + $payload = [ + 'extension' => Text::_($extension), + 'days_old' => $days_old + ]; + + // load template + return LayoutHelper::render('outdated_extension', $payload, dirname(__DIR__) . '/layouts'); + } + + public static function updateNotification($extension) + { + $version_installed = Extension::getVersion($extension); + $version_latest = Extension::getLatestVersion($extension); + + if (!$needsUpdate = version_compare($version_latest, $version_installed, 'gt')) + { + return; + } + + // Load extension's language file + Functions::loadLanguage($extension); + + // Extension Title + $title = Text::_($extension); + $title = str_replace('System -', '', $title); // Remove plugin folder prefix from plugins + + // Render Layout + $data = [ + 'title' => $title, + 'version_installed' => $version_installed, + 'version_latest' => $version_latest, + 'ispro' => Extension::isPro($extension), + 'upgradeurl' => Extension::getTassosExtensionUpgradeURL($extension), + 'product_url' => Extension::getProductURL($extension) + ]; + + return LayoutHelper::render('updatechecker', $data, JPATH_PLUGINS . '/system/nrframework/layouts'); + } + + /** + * TODO: Not used anywhere, should delete. + * + * @deprecated 4.11.7 + */ + public static function checkForUpdates($element) + { + HTMLHelper::script('plg_system_nrframework/updatechecker.js', ['relative' => true, 'version' => true]); + HTMLHelper::stylesheet('plg_system_nrframework/updatechecker.css', ['relative' => true, 'version' => true]); + + return ' +
+
+ '; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/CSS.php b/plugins/system/nrframework/NRFramework/Helpers/CSS.php new file mode 100644 index 00000000..536d40c0 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/CSS.php @@ -0,0 +1,39 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +class CSS +{ + /** + * Transforms an array of CSS variables (key, value) to + * a CSS output. + * + * @param array $cssVars + * @param string $namespace + * + * @return string + */ + public static function cssVarsToString($cssVars, $namespace) + { + $output = ''; + + foreach (array_filter($cssVars) as $key => $value) + { + $output .= '--' . $key . ': ' . $value . ';' . "\n"; + } + + return $namespace . ' { + ' . $output . ' + } + '; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/ChainedFields.php b/plugins/system/nrframework/NRFramework/Helpers/ChainedFields.php new file mode 100644 index 00000000..fcc2ce41 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/ChainedFields.php @@ -0,0 +1,175 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +class ChainedFields +{ + /** + * Loads a combined array of the inputs and choices of the CSV file. + * + * @param string $path + * @param string $data_source + * @param string $separator + * @param string $id_prefix + * @param string $name_prefix + * + * @return array + */ + public static function loadCSV($input, $data_source = 'custom', $separator = ',', $id_prefix = '', $name_prefix = '') + { + if (!$separator) + { + return []; + } + + if ($data_source === 'csv_file') + { + if (!file_exists($input)) + { + return []; + } + + if (!$input = file_get_contents($input)) + { + return []; + } + } + + if (!$data = self::getData($input, $separator, $id_prefix, $name_prefix)) + { + return []; + } + + return $data; + } + + /** + * Iterates over the given data and returns the inputs and choices. + * + * @param string $data + * @param string $separator + * @param string $id_prefix + * @param string $name_prefix + * + * @return array + */ + public static function getData($data = '', $separator = ',', $id_prefix = '', $name_prefix = '') + { + if (!$data || !is_string($data)) + { + return; + } + + if (!$rows = explode(PHP_EOL, $data)) + { + return; + } + + $choices = []; + $inputs = []; + + foreach ($rows as $row) + { + $row = explode($separator, $row); + + $row = array_filter($row, 'strlen'); + + // if an empty row was found, skip it + if (empty($row)) + { + continue; + } + + if (empty($inputs)) + { + $i = 1; + + foreach ($row as $index => $item) + { + if ($i % 10 == 0) + { + $i++; + } + + $inputs[] = [ + 'id' => $id_prefix . $i, + 'name' => $name_prefix . '[' . $i . ']', + 'label' => trim($item), + ]; + + $i++; + } + + continue; + } + + $parent = null; + + foreach($row as $item) + { + $item = trim($item); + + if ($parent === null) + { + $parent = &$choices; + } + + if (!isset($parent[$item])) + { + $item = trim($item); + + $parent[$item] = [ + 'text' => $item, + 'value' => $item, + 'isSelected' => false, + 'choices' => [] + ]; + } + + $parent = &$parent[$item]['choices']; + } + } + + self::array_values_recursive($choices); + + if (!isset($inputs) || !isset($choices)) + { + return; + } + + return compact('inputs', 'choices'); + } + + /** + * Transforms an array to using as key an index value instead of a alphanumeric. + * + * @param array $choices + * @param string $property + * + * @return array + */ + public static function array_values_recursive(&$choices, $property = 'choices') + { + $choices = array_values($choices); + + for($i = 0; $i <= count($choices); $i++) + { + if(empty($choices[$i][$property])) + { + continue; + } + + $choices[$i][$property] = self::array_values_recursive($choices[$i][$property], $property); + } + + return $choices; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Controls/CSS.php b/plugins/system/nrframework/NRFramework/Helpers/Controls/CSS.php new file mode 100644 index 00000000..068cd341 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Controls/CSS.php @@ -0,0 +1,76 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2022 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Controls; + +defined('_JEXEC') or die; + +class CSS +{ + public static function generateCSS($styles = []) + { + if (!$styles || !is_array($styles)) + { + return; + } + + $css = ''; + foreach ($styles as $breakpoint => $array) + { + if (!$selectors = self::groupCSSBySelectors($array)) + { + continue; + } + + $css_tmp = ''; + + // Get all the CSS for this breakpoint for all selectors + foreach ($selectors as $selector => $_styles) + { + $css_tmp .= $selector . '{' . implode('', $_styles) . '}'; + } + + // Then enapsulate all the breakpoint CSS in the breakpoint media query + $css_tmp = \NRFramework\Helpers\Responsive::renderResponsiveCSS([ + $breakpoint => [$css_tmp] + ]); + + if (!$css_tmp) + { + continue; + } + + $css .= $css_tmp; + } + + return $css; + } + + public static function groupCSSBySelectors($styles = []) + { + if (!$styles) + { + return; + } + + $selectors = []; + + foreach ($styles as $style) + { + $selectors[$style['selector']][] = $style['css']; + } + + if (!$selectors) + { + return; + } + + return $selectors; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Controls/Control.php b/plugins/system/nrframework/NRFramework/Helpers/Controls/Control.php new file mode 100644 index 00000000..1b54cf29 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Controls/Control.php @@ -0,0 +1,269 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Controls; + +defined('_JEXEC') or die; + +class Control +{ + /** + * Finds the value and unit in the given subject. + * + * @param string/array $subject + * + * @return array + */ + public static function findUnitInValue($subject = '') + { + if (is_null($subject)) + { + return; + } + + if (is_string($subject) && $subject === '') + { + return; + } + + if (is_array($subject) && count($subject) === 0) + { + return; + } + + if ($subject === 'auto') + { + return [ + 'value' => '', + 'unit' => 'auto' + ]; + } + + if (is_array($subject) && isset($subject['value'])) + { + $return = [ + 'value' => $subject['value'] + ]; + + if (isset($subject['unit'])) + { + $return['unit'] = $subject['unit']; + } + + return $return; + } + + $pattern = '/^([\d.]+)(\D+)?$/'; + if (is_scalar($subject) && preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE) === 1) + { + return [ + 'value' => $matches[1][0], + 'unit' => isset($matches[2][0]) ? $matches[2][0] : '' + ]; + } + + return [ + 'value' => $subject, + 'unit' => '' + ]; + } + + /** + * Parses the given value to a CSS value. + * + * @param mixed $value + * @param string $unit + * + * @return string + */ + public static function getCSSValue($value = '', $unit = '') + { + if (is_null($value) || $value === '') + { + return; + } + + // Is scalar, transform to array + if (is_scalar($value)) + { + $value = array_filter(explode(' ', $value), function($value) { + return $value !== ''; + }); + } + + if (!$value) + { + return; + } + + if (is_array($value)) + { + if (empty($value)) + { + return; + } + + // If all values are empty, abort + $isEmptyArray = array_filter($value, function($str) { + return $str === null || $str === false || $str === '' || (is_array($str) && empty($str)); + }); + if (count($isEmptyArray) === 4) + { + return; + } + + // Apply spacing positions + if ($positions = self::findSpacingPositions($value)) + { + $return = []; + foreach ($positions as $pos) + { + $return[$pos] = isset($value[$pos]) && $value[$pos] !== '' ? $value[$pos] : 0; + } + if (empty($return)) + { + return; + } + $value = $return; + } + + /** + * All values are duplicates, return only 1 number with their unit. + * + * Example: Given [5, 5, 5, 5] to print the margin in pixels, do not return `margin: 5px 5px 5px 5px`. + * Rather return `margin: 5px` + */ + if (count($value) === 4 && count(array_unique($value)) === 1) + { + $value = reset($value); + if ($value_data = self::findUnitInValue($value)) + { + $value = $value_data['value']; + $unit = !empty($value_data['unit']) ? $value_data['unit'] : $unit; + } + + if (is_array($value)) + { + return; + } + + return $value . ($value > 0 ? $unit : ''); + } + + /** + * If we were given 4 values and first/third & second/forth values are the same then return these only. + * + * Example: Given[5, 10, 5, 10] to print the margin in pixels, do not return `margin: 5px 10px 5px 10px`. + * Rather return `margin: 5px 10px` + */ + $keys = array_keys($value); + if (count($value) === 4 && $value[$keys[0]] === $value[$keys[2]] && $value[$keys[1]] === $value[$keys[3]]) + { + $value1 = $value[$keys[0]]; + $suffix1 = $suffix2 = $unit; + $value2 = $value[$keys[1]]; + + if ($value_1 = self::findUnitInValue($value1)) + { + $value1 = $value_1['value']; + $suffix1 = !empty($value_1['unit']) ? $value_1['unit'] : $unit; + } + if ($value_2 = self::findUnitInValue($value2)) + { + $value2 = $value_2['value']; + $suffix2 = !empty($value_2['unit']) ? $value_2['unit'] : $unit; + } + + return $value1 . ($value1 > 0 ? $suffix1 : '') . ' ' . $value2 . ($value2 > 0 ? $suffix2 : ''); + } + + // Different values + $data = []; + foreach ($value as $key => $_value) + { + $val = $_value; + if ($value_data = self::findUnitInValue($val)) + { + $val = $value_data['value']; + $unit = !empty($value_data['unit']) ? $value_data['unit'] : $unit; + } + $data[] = $val . ($val > 0 ? $unit : ''); + } + + return implode(' ', $data); + } + + return; + } + + /** + * Finds an array of positions of the given value that + * relates to margin/padding or border radius. + * + * @param array $value + * + * @return array + */ + public static function findSpacingPositions($value = []) + { + if (!is_array($value) || !count($value)) + { + return; + } + + $keys = array_keys($value); + + // Is margin/padding + $margin_padding = self::getPositions(); + if (in_array($keys[0], $margin_padding, true)) + { + return $margin_padding; + } + + // Is border radius + $border_radius = self::getPositions('border_radius'); + if (in_array($keys[0], $border_radius, true)) + { + return $border_radius; + } + + return; + } + + /** + * Return the position keys based on the control type. + * + * @param string $type + * + * @return array + */ + public static function getPositions($type = 'margin_padding') + { + if (!$type) + { + return []; + } + + $margin_padding = [ + 'top', + 'right', + 'bottom', + 'left' + ]; + + $border_radius = [ + 'top_left', + 'top_right', + 'bottom_right', + 'bottom_left' + ]; + + return $type === 'margin_padding' ? $margin_padding : $border_radius; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Controls/Spacing.php b/plugins/system/nrframework/NRFramework/Helpers/Controls/Spacing.php new file mode 100644 index 00000000..3b5b342d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Controls/Spacing.php @@ -0,0 +1,101 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Controls; + +defined('_JEXEC') or die; + +use NRFramework\Helpers\Controls\Control; + +class Spacing +{ + /** + * Parses the given value and returns the value expected by Spacing Control. + * + * @param mixed $value + * @param string $type This can be margin_padding or border_radius. + * + * @return array + */ + public static function parseInputValue($value = '', $type = 'margin_padding') + { + if (!$value) + { + return []; + } + + $linked = isset($value['linked']) ? $value['linked'] : '0'; + $unit = isset($value['unit']) ? $value['unit'] : 'px'; + $value = isset($value['value']) ? $value['value'] : $value; + + $positions = Control::getPositions($type); + + // If it's a string of values, prepare it to be an array and continue + if (is_scalar($value)) + { + $value = array_filter(explode(' ', $value), function($value) { + return $value !== ''; + }); + + // Get the unit from the first found value + foreach ($value as $val) + { + $_value = Control::findUnitInValue($val); + if (!isset($_value['unit'])) + { + continue; + } + + $unit = !empty($_value['unit']) ? $_value['unit'] : $unit; + break; + } + + // Ensure only ints are in the array + $value = array_map('intval', $value); + + // If only a single value is given, apply the value to all positions + if (count($value) === 1) + { + $value = array_merge($value, $value, $value, $value); + } + + if (count($value) === 2) + { + $value = [$value[0], $value[1], $value[0], $value[1]]; + } + + $tmp_value = []; + + foreach ($positions as $index => $pos) + { + $tmp_value[$pos] = isset($value[$index]) ? $value[$index] : ''; + } + + $value = $tmp_value; + } + + // Return value + $return = []; + + foreach ($positions as $pos) + { + $return[$pos] = isset($value[$pos]) && $value[$pos] !== '' ? intval($value[$pos]) : ''; + } + + if (empty($return)) + { + return []; + } + + $return['linked'] = $linked; + $return['unit'] = $unit; + + return $return; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Controls/index.php b/plugins/system/nrframework/NRFramework/Helpers/Controls/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/system/nrframework/NRFramework/Helpers/CustomField.php b/plugins/system/nrframework/NRFramework/Helpers/CustomField.php new file mode 100644 index 00000000..18177962 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/CustomField.php @@ -0,0 +1,53 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use \Joomla\Registry\Registry; + +class CustomField +{ + /** + * Get a custom field's data. + * + * @param integer $value + * @param string $selector + * + * @return object + */ + public static function getData($value, $selector = 'id') + { + if (!$value) + { + return; + } + + $db = Factory::getDbo(); + + $query = $db->getQuery(true); + + $query + ->select($db->quoteName(['fieldparams'])) + ->from($db->quoteName('#__fields')) + ->where($db->quoteName($selector) . ' = ' . $db->quote($value)) + ->where($db->quoteName('state') . ' = 1'); + + $db->setQuery($query); + + if (!$result = $db->loadResult()) + { + return; + } + + return new Registry($result); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/File.php b/plugins/system/nrframework/NRFramework/Helpers/File.php new file mode 100644 index 00000000..b787e927 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/File.php @@ -0,0 +1,61 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use Joomla\CMS\Uri\Uri; + +class File +{ + public static function getFileSources($sources, $allowedExtensions = null) + { + if (!$sources) + { + return; + } + + // Support comma separated values + $sources = is_array($sources) ? $sources : explode(',', $sources); + $result = []; + $ds = DIRECTORY_SEPARATOR; + + foreach ($sources as $source) + { + if (!$pathinfo = pathinfo($source)) + { + continue; + } + + if (!isset($pathinfo['extension'])) + { + continue; + } + + if ($allowedExtensions && !in_array($pathinfo['extension'], $allowedExtensions)) + { + continue; + } + + // Add root path to local source + if (strpos($source, 'http') === false) + { + $source = Uri::root() . ltrim($source, '/'); + } + + $result[] = [ + 'ext' => $pathinfo['extension'], + 'file' => $source + ]; + } + + return $result; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Geo.php b/plugins/system/nrframework/NRFramework/Helpers/Geo.php new file mode 100644 index 00000000..8550d2a2 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Geo.php @@ -0,0 +1,39 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +class Geo +{ + /** + * Detect and return the visitor's country. + * + * @return string The visitor's country code (GR) + */ + public static function getVisitorCountryCode() + { + $path = JPATH_PLUGINS . '/system/tgeoip/'; + + if (!is_dir($path)) + { + return ''; + } + + if (!class_exists('TGeoIP')) + { + @include_once $path . 'vendor/autoload.php'; + @include_once $path . 'helper/tgeoip.php'; + } + + $geo = new \TGeoIP(); + return $geo->getCountryCode(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/License.php b/plugins/system/nrframework/NRFramework/Helpers/License.php new file mode 100644 index 00000000..e3ec5543 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/License.php @@ -0,0 +1,45 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use Joomla\CMS\Http\HttpFactory; + +class License +{ + /** + * Returns the remote license data from the server for the given download key. + * + * @return array + */ + public static function getRemoteLicenseData($download_key = null) + { + if (!$download_key) + { + return; + } + + // License Check Endpoint + $url = TF_CHECK_LICENSE; + // Set Download Key + $url = str_replace('{{DOWNLOAD_KEY}}', $download_key, $url); + + $response = HttpFactory::getHttp()->get($url); + + // No response, abort + if (!$response = $response->body) + { + return; + } + + return json_decode($response, true); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Module.php b/plugins/system/nrframework/NRFramework/Helpers/Module.php new file mode 100644 index 00000000..90968901 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Module.php @@ -0,0 +1,60 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +class Module +{ + /** + * Get a module data. + * + * @param integer $value + * @param string $selector + * @param array $options + * + * @return object + */ + public static function getData($value, $selector = 'id', $options = ['access' => 1]) + { + if (!$value) + { + return; + } + + $db = Factory::getDbo(); + + $query = $db->getQuery(true); + + $query + ->select($db->quoteName(['params'])) + ->from($db->quoteName('#__modules')) + ->where($db->quoteName($selector) . ' = ' . $db->quote($value)); + + if (count($options)) + { + foreach ($options as $column => $value) + { + $query->where($db->quoteName($column) . ' = ' . $db->quote($value)); + } + } + + $db->setQuery($query); + + if (!$result = $db->loadResult()) + { + return; + } + + return new \Joomla\Registry\Registry($result); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Number.php b/plugins/system/nrframework/NRFramework/Helpers/Number.php new file mode 100644 index 00000000..d223797c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Number.php @@ -0,0 +1,63 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +class Number +{ + /** + * Converts a number into a short version, eg: 1000 -> 1k + * + * @param Number $n The number to create the shorter version + * @param integer $precision + * + * @return string The shorter version of the given number + */ + public static function toShortFormat($n, $precision = 1) + { + if ($n < 900) + { + // 0 - 900 + $n_format = number_format($n, $precision); + $suffix = ''; + } else if ($n < 900000) + { + // 0.9k-850k + $n_format = number_format($n / 1000, $precision); + $suffix = 'K'; + } else if ($n < 900000000) + { + // 0.9m-850m + $n_format = number_format($n / 1000000, $precision); + $suffix = 'M'; + } else if ($n < 900000000000) + { + // 0.9b-850b + $n_format = number_format($n / 1000000000, $precision); + $suffix = 'B'; + } else + { + // 0.9t+ + $n_format = number_format($n / 1000000000000, $precision); + $suffix = 'T'; + } + + // Remove unecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1" + // Intentionally does not affect partials, eg "1.50" -> "1.50" + if ($precision > 0) + { + $dotzero = '.' . str_repeat( '0', $precision ); + $n_format = str_replace( $dotzero, '', $n_format ); + } + + return $n_format . $suffix; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Responsive.php b/plugins/system/nrframework/NRFramework/Helpers/Responsive.php new file mode 100644 index 00000000..fc3236c1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Responsive.php @@ -0,0 +1,167 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use NRFramework\Cache; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Plugin\PluginHelper; + +class Responsive +{ + /** + * Renders the given CSS. + * + * @param array $css + * @param string $selector + * + * @return string + */ + public static function renderResponsiveCSS($css, $selector = '') + { + if (!$css || !is_array($css)) + { + return; + } + + $output = ''; + + foreach (self::getBreakpoints() as $breakpoint => $breakpoint_data) + { + if (!isset($css[$breakpoint]) || empty($css[$breakpoint])) + { + continue; + } + + /** + * If we were given an array of strings of CSS, transform them to a string so we can output it. + * + * i.e. transform + * [ + * 'color: #fff;', + * 'background: #000;' + * ] + * + * to: + * + * 'color: #fff;background: #000;' + */ + if (!is_string($css[$breakpoint])) + { + $css[$breakpoint] = implode('', $css[$breakpoint]); + } + + $max_width = isset($breakpoint_data['max_width']) ? $breakpoint_data['max_width'] : ''; + + $output .= self::getGenericTemplate($css[$breakpoint], $max_width, $selector); + } + + return $output; + } + + /** + * Returns the responsive output of a specific media query size. + * + * @param string $css The Custom CSS + * @param int $size This is the max-width in pixels + * @param string $selector The CSS Selector to apply the CSS + * + * @return string + */ + public static function getGenericTemplate($css, $size = '', $selector = '') + { + if (!is_string($css) || !is_scalar($size) || !is_string($selector)) + { + return ''; + } + + $selector_prefix = $selector_suffix = $size_prefix = $size_suffix = ''; + + if (!empty($size)) + { + $size_prefix = '@media screen and (max-width: ' . $size . 'px){'; + $size_suffix = '}'; + } + + if (!empty($selector)) + { + $selector_prefix = $selector . '{'; + $selector_suffix = '}'; + } + + return $size_prefix . $selector_prefix . $css . $selector_suffix . $size_suffix; + } + + /** + * Returns all breakpoints. + * + * @return array + */ + public static function getBreakpoints() + { + $breakpointsSettings = self::getBreakpointsSettings(); + + $tablet_max_width = isset($breakpointsSettings['tablet']) && !empty($breakpointsSettings['tablet']) ? $breakpointsSettings['tablet'] : 1024; + $mobile_max_width = isset($breakpointsSettings['mobile']) && !empty($breakpointsSettings['mobile']) ? $breakpointsSettings['mobile'] : 575; + + return [ + 'desktop' => [ + 'icon' => '', + 'label' => Text::_('NR_DESKTOP'), + 'desc' => Text::_('NR_DESKTOPS_WITH_BREAKPOINT_INFO') + ], + 'tablet' => [ + 'icon' => '', + 'label' => Text::_('NR_TABLET'), + 'desc' => Text::sprintf('NR_TABLETS_WITH_BREAKPOINT_INFO', $tablet_max_width), + 'max_width' => $tablet_max_width + ], + 'mobile' => [ + 'icon' => '', + 'label' => Text::_('NR_MOBILE'), + 'desc' => Text::sprintf('NR_MOBILES_WITH_BREAKPOINT_INFO', $mobile_max_width), + 'max_width' => $mobile_max_width + ] + ]; + } + + public static function getBreakpointsSettings() + { + $hash = 'tassosResponsiveBreakpoints'; + + if (Cache::has($hash)) + { + return Cache::get($hash); + } + + $settings = PluginHelper::getPlugin('system', 'nrframework'); + + $default = [ + 'desktop' => 'any', + 'tablet' => 1024, + 'mobile' => 575 + ]; + + if (!isset($settings->params)) + { + return $default; + } + + if (!$params = json_decode($settings->params, true)) + { + return $default; + } + + $data = isset($params['breakpoints']) ? $params['breakpoints'] : []; + + return Cache::set($hash, $data); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Settings.php b/plugins/system/nrframework/NRFramework/Helpers/Settings.php new file mode 100644 index 00000000..e61356ef --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Settings.php @@ -0,0 +1,38 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; + +class Settings +{ + /** + * Get the value of a specific setting from the Tassos Framework plugin. + * + * @param string $key The key of the setting to retrieve. + * @param string $default The default value to return if the setting is not found. + * + * @return string The value of the setting, or the default value if the setting is not found. + */ + public static function getValue($key = '', $default = '') + { + if (!$framework = PluginHelper::getPlugin('system', 'nrframework')) + { + return $this->defaultAPIKey; + } + + // Get plugin params + $params = new Registry($framework->params); + return $params->get($key, $default); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Template.php b/plugins/system/nrframework/NRFramework/Helpers/Template.php new file mode 100644 index 00000000..c6742415 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Template.php @@ -0,0 +1,53 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +use NRFramework\Cache; +use Joomla\CMS\Factory; + +class Template +{ + /** + * Returns the current template name. + * + * @return string + */ + public static function getTemplateName() + { + $hash = 'TFGetTemplateName'; + + if (Cache::has($hash)) + { + return Cache::get($hash); + } + + $template = null; + + if (Factory::getApplication()->isClient('site')) + { + $template = Factory::getApplication()->getTemplate(); + } + else + { + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('template')) + ->from($db->quoteName('#__template_styles')) + ->where($db->quoteName('client_id') . ' = 0') + ->where($db->quoteName('home') . ' = 1'); + $db->setQuery($query); + $template = $db->loadResult(); + } + + return Cache::set($hash, $template); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Video.php b/plugins/system/nrframework/NRFramework/Helpers/Video.php new file mode 100644 index 00000000..d32b3ae8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Video.php @@ -0,0 +1,98 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers; + +defined('_JEXEC') or die; + +class Video +{ + /** + * Returns the Video URL details. + * + * Supported platforms: + * - YouTube + * - Vimeo + * + * @param string $url + * + * @return array + */ + public static function getDetails($url) + { + $id = ''; + $provider = ''; + + if (preg_match(self::getYouTubePattern(), $url, $matches)) + { + $id = !empty($matches[1]) ? $matches[1] : $matches[2]; + $provider = 'youtube'; + } + else if (preg_match(self::getVimeoPattern(), $url, $matches)) + { + $id = !empty($matches[1]) ? $matches[1] : null; + $provider = 'vimeo'; + } + else if (preg_match(self::getFacebookVideoPattern(), $url)) + { + $id = $url; + $provider = 'facebookvideo'; + } + else if (preg_match(self::getDailymotionPattern(), $url, $matches)) + { + $id = end($matches); + $provider = 'dailymotion'; + } + + return [ + 'id' => $id, + 'provider' => $provider + ]; + } + + /** + * Get YouTube Pattern. + * + * @return string + */ + public static function getYouTubePattern() + { + return '/^https?:\/\/(?:m\.|www\.)?youtube\.com\/(?:watch\?v=|embed\/|shorts\/)?([a-zA-Z0-9_-]{11})|^https?:\/\/youtu\.be\/([a-zA-Z0-9_-]{11})/'; + } + + /** + * Get Vimeo Pattern. + * + * @return string + */ + public static function getVimeoPattern() + { + return '/^https?:\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(\d+)/'; + } + + /** + * Get Facebook Video Pattern. + * + * @return string + */ + public static function getFacebookVideoPattern() + { + return '/^(?:(?:https?:)?\/\/)?(?:www\.)?facebook\.com\/(?:watch\/\?v=|[\w\.]+\/videos\/(?:[\w\.]+\/)?)?(\d+)/'; + } + + /** + * Get Dailymotion Pattern. + * + * @return string + */ + public static function getDailymotionPattern() + { + return '/(?:dailymotion\.com\/(?:video|hub)\/|dai\.ly\/)([a-zA-Z0-9]+)/'; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Widgets/Gallery.php b/plugins/system/nrframework/NRFramework/Helpers/Widgets/Gallery.php new file mode 100644 index 00000000..a3f2d54b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Widgets/Gallery.php @@ -0,0 +1,575 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Widgets; + +defined('_JEXEC') or die; + +use NRFramework\Mimes; +use Joomla\CMS\Helper\ModuleHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; + +class Gallery +{ + /** + * Stores all gallery parsed directories info txt file `*.gallery_info.txt` data in format: + * GALLERY DIRECTORY => ARRAY OF `*.gallery_info.txt` file data + * + * @var array + */ + static $gallery_directories_info_file = []; + + /** + * Stores all galleries info file names in format: + * + * GALLERY DIRECTORY => INFO FILE NAME + * + * @var array + */ + static $gallery_directories_info_file_names = []; + + /** + * The directory information file holding all gallery item details. + * + * @var string + */ + const directory_gallery_info_file = 'gallery_info.txt'; + + /** + * Parses the given gallery items. + * + * @param mixed $input A string to a directory/path/URL or an array of a URL item containing its information. + * @param array $allowed_file_types The allowed file types. + * + * @return mixed + */ + public static function parseGalleryItems($input, $allowed_file_types = []) + { + if (is_string($input)) + { + $fullpath_input = JPATH_ROOT . DIRECTORY_SEPARATOR . ltrim($input, DIRECTORY_SEPARATOR); + + // Parse Directory + if (is_dir($fullpath_input)) + { + return self::parseDirectory($fullpath_input, $allowed_file_types); + } + + // Skip invalid URLs + if ($url = self::parseURL($input)) + { + return [$url]; + } + + // Parse Image + if ($image_data = self::parseImage($fullpath_input, $allowed_file_types)) + { + return [$image_data]; + } + } + + return [self::parseURL($input)]; + } + + /** + * Parse the directory by finding all of its images and their information. + * + * @param string $dir + * @param array $allowed_file_types + * + * @return mixed + */ + public static function parseDirectory($dir, $allowed_file_types = []) + { + if (!is_string($dir) || !is_dir($dir) || empty($allowed_file_types)) + { + return; + } + + $items = []; + + // Get images + $files = array_diff(scandir($dir), ['.', '..', '.DS_Store']); + + foreach ($files as $key => $filename) + { + // Skip directories + if (is_dir($dir . DIRECTORY_SEPARATOR . $filename)) + { + continue; + } + + $image_path = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename; + + if (!$image_data = self::parseImage($image_path, $allowed_file_types)) + { + continue; + } + + $items[] = $image_data; + } + + return $items; + } + + /** + * Parse the directory image and return its information. + * + * @param string $image_path + * @param string $allowed_file_types + * + * @return mixed + */ + public static function parseImage($image_path, $allowed_file_types = null) + { + if (!is_string($image_path)) + { + return; + } + + $data = [ + 'path' => $image_path, + 'url' => self::directoryImageToURL($image_path) + ]; + + if (!is_file($image_path)) + { + return array_merge($data, [ + 'invalid' => true + ]); + } + + // Skip not allowed file types + if (!is_null($allowed_file_types) && !Mimes::check($allowed_file_types, Mimes::detectFileType($image_path))) + { + return; + } + + // Check if there is a `*.gallery_info.txt` helper file and get any information about the image + $gallery_info_file_data = self::getGalleryInfoFileData(dirname($image_path)); + if (!$gallery_info_file_data) + { + return $data; + } + + $image_filename = pathinfo($image_path, PATHINFO_BASENAME); + + // If no information from the text field about this image was found, stop + if (!isset($gallery_info_file_data[$image_filename])) + { + return $data; + } + + $image_data = $gallery_info_file_data[$image_filename]; + + return array_merge($data, [ + 'caption' => isset($image_data['caption']) ? $image_data['caption'] : '' + ]); + } + + /** + * Parses a single URL either as a String or as an Array. + * + * @param mixed $item + * + * @return mixed + */ + public static function parseURL($item) + { + // URL is a string + if (is_string($item)) + { + if (!filter_var($item, FILTER_VALIDATE_URL)) + { + return; + } + + return [ + 'url' => $item + ]; + } + + // URL is an array + if (!is_array($item) || !count($item)) + { + return; + } + + // If a thumbnail URL is given but no URL, use it as the full image URL + if (isset($item['thumbnail_url']) && !isset($item['url'])) + { + $item['url'] = $item['thumbnail_url']; + } + + if (!isset($item['url'])) + { + return; + } + + if (!filter_var($item['url'], FILTER_VALIDATE_URL)) + { + return; + } + + return $item; + } + + /** + * Loads a module by its ID. + * + * @param string $id + * + * @return string + */ + public static function loadModule($id) + { + $module = ModuleHelper::getModuleById($id); + $params = ['style' => 'none']; + + return $module->id > 0 ? Factory::getDocument()->loadRenderer('module')->render($module, $params) : ''; + } + + /** + * Read the `*.gallery_info.txt` file for the given directory. + * + * @param string $dir + * + * @return mixed + */ + public static function getGalleryInfoFileData($dir) + { + if (isset(self::$gallery_directories_info_file[$dir]) && !empty(self::$gallery_directories_info_file[$dir])) + { + return self::$gallery_directories_info_file[$dir]; + } + + if (!$file = self::findGalleryInfoFile($dir)) + { + return []; + } + + // Read file + if (!$handle = fopen($file, 'r')) + { + return []; + } + + $data = []; + + $line_defaults = ['', '', '']; + + // Loop each line + while (($line = fgets($handle)) !== false) + { + list($filename, $caption, $hash) = explode('|', $line) + $line_defaults; + + // If no filename is given, continue + if (!$filename) + { + continue; + } + + $data[$filename] = [ + 'filename' => $filename, + 'caption' => trim($caption), + 'hash' => trim($hash) + ]; + } + + // Close file + fclose($handle); + + self::$gallery_directories_info_file[$dir] = $data; + + return $data; + } + + /** + * Finds the source image and whether it has been edited. + * + * @param string $source + * @param string $destination_folder + * + * @return mixed + */ + public static function findSourceImageDetails($source, $destination_folder) + { + $source_filename = pathinfo($source, PATHINFO_BASENAME); + + $data = self::getGalleryInfoFileData(dirname($source)); + + $image_data = isset($data[$source_filename]) ? $data[$source_filename] : false; + + if (!$image_data) + { + return false; + } + + if (empty($image_data['hash'])) + { + return false; + } + + $sourceHash = self::calculateFileHash($source); + + return [ + 'path' => $destination_folder . $image_data['filename'], + 'edited' => $image_data['hash'] !== $sourceHash + ]; + } + + /** + * Updates or Inserts the given image information from the gallery info file. + * + * @param string $source + * @param array $image_data + * + * @return mixed + */ + public static function updateImageDataInGalleryInfoFile($source, $image_data) + { + // Source directory + $source_directory = dirname($source); + + // Check whether the gallery info file exists, if not, create it + if (!$file = self::findGalleryInfoFile($source_directory)) + { + $file = self::createGalleryInfoFile($source_directory); + } + + // Open files + $reading = fopen($file, 'r'); + $writing = fopen($file . '.tmp', 'w'); + + $replaced = false; + + while (!feof($reading)) + { + // Get each file line + $line = fgets($reading); + + // Remove new line at the end + $line = trim(preg_replace('/\s\s+/', ' ', $line)); + + // Skip empty lines + if (empty($line)) + { + continue; + } + + list($filename, $caption, $hash) = explode('|', $line) + ['', '', '']; + + // We need to manipulate current file + if (strtolower($filename) !== strtolower(basename($image_data['path']))) + { + fputs($writing, $line . "\n"); + continue; + } + + $replaced = true; + + $line = $filename . '|' . $caption . '|' . self::calculateFileHash($source) . "\n";; + + // Write changed line + fputs($writing, $line); + } + + // Close files + fclose($reading); + fclose($writing); + + // If we replaced a line, update the text file + if ($replaced) + { + rename($file . '.tmp', $file); + } + // No line was replaced, append image details + else + { + unlink($file . '.tmp'); + + self::appendImageDataToGalleryInfoFile($file, $source, $image_data); + } + } + + /** + * Removes the image from the gallery info file. + * + * @param string $source + * + * @return boolean + */ + public static function removeImageFromGalleryInfoFile($source) + { + // Get the gallery info file from destination folder + if (!$file = self::findGalleryInfoFile(dirname($source))) + { + return false; + } + + // Open files + $reading = fopen($file, 'r'); + $writing = fopen($file . '.tmp', 'w'); + + $found = false; + + while (!feof($reading)) + { + // Get each file line + $line = fgets($reading); + + // Remove new line at the end + $line = trim(preg_replace('/\s\s+/', ' ', $line)); + + // Skip empty lines + if (empty($line)) + { + continue; + } + + list($filename, $caption, $hash) = explode('|', $line) + ['', '', '']; + + // We need to manipulate current file + if ($filename !== pathinfo($source, PATHINFO_BASENAME)) + { + $found = true; + fputs($writing, $line . "\n"); + continue; + } + } + + // Close files + fclose($reading); + fclose($writing); + + if (!$found) + { + return false; + } + + // Save the changes + rename($file . '.tmp', $file); + + return true; + } + + /** + * Appends the image data into the info file. + * + * @param string $dir + * + * @return void + */ + public static function createGalleryInfoFile($dir) + { + $file = self::getLanguageInfoFileName($dir); + + file_put_contents($file, ''); + + return $file; + } + + /** + * Appends the image data into the info file. + * + * @param string $file + * @param string $source + * @param object $image_data + * + * @return void + */ + public static function appendImageDataToGalleryInfoFile($file, $source, $image_data) + { + $caption = isset($image_data['caption']) ? $image_data['caption'] : ''; + + $hash = self::calculateFileHash($source); + + $line = pathinfo($source, PATHINFO_BASENAME) . '|' . $caption . '|' . $hash . "\n"; + + file_put_contents($file, $line, FILE_APPEND); + } + + /** + * Finds the `*.gallery_info.txt` file if it exists in the given directory. + * + * @param string $dir + * + * @return mixed + */ + public static function findGalleryInfoFile($dir) + { + if (isset(self::$gallery_directories_info_file_names[$dir])) + { + return self::$gallery_directories_info_file_names[$dir]; + } + + // Method 1: With language prefix + $file = self::getLanguageInfoFileName($dir); + + // Check if the file exists + if (file_exists($file)) + { + self::$gallery_directories_info_file_names[$dir] = $file; + + return $file; + } + + // Method 2: Without the language prefix + $file = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . self::directory_gallery_info_file; + + // Check if the file exists + if (file_exists($file)) + { + self::$gallery_directories_info_file_names[$dir] = $file; + + return $file; + } + + return false; + } + + /** + * Returns the info file with the language prefix. + * + * @param string $dir + * + * @return string + */ + public static function getLanguageInfoFileName($dir) + { + return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Factory::getLanguage()->getTag() . '.' . self::directory_gallery_info_file; + } + + /** + * Calculates the file hash of a file. + * + * Hash = md5(file path + last modified date of file) + * + * @param string $file_path + * + * @return string + */ + public static function calculateFileHash($file_path) + { + return md5($file_path . filemtime($file_path)); + } + + /** + * Transforms an image path to a URL. + * + * @param string $image_path + * + * @return string + */ + public static function directoryImageToURL($image_path) + { + return rtrim(Uri::root(), DIRECTORY_SEPARATOR) . mb_substr($image_path, strlen(JPATH_BASE), null);; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Widgets/GalleryManager.php b/plugins/system/nrframework/NRFramework/Helpers/Widgets/GalleryManager.php new file mode 100644 index 00000000..d24d9a78 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Widgets/GalleryManager.php @@ -0,0 +1,942 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Widgets; + +defined('_JEXEC') or die; + +use NRFramework\File; +use NRFramework\Image; +use NRFramework\Functions; +use Joomla\Registry\Registry; +use Joomla\CMS\Table\Table; +use Joomla\CMS\Factory; +use Joomla\Filesystem\Path; +use Joomla\Filesystem\File as JoomlaCMSFile; + +class GalleryManager +{ + /** + * How long the files can stay in the temp folder. + * + * After each save a clean up is run and all files older + * than this value in days are removed. + * + * @var int + */ + private static $temp_files_cleanup_days = 1; + + /** + * Upload file + * + * @param array $file The request file as posted by form + * @param string $upload_settings The upload settings + * @param array $media_uploader_file_data Media uploader related file settings + * @param array $resizeSettings The resize settings + * + * @return mixed String on success, Null on failure + */ + public static function upload($file, $upload_settings, $media_uploader_file_data, $resizeSettings) + { + // The source file name + $source = ''; + + // Move the image to the tmp folder + try { + $source = File::upload($file, self::getFullTempFolder(), $upload_settings['allowed_types'], $upload_settings['allow_unsafe']); + } catch (\Throwable $th) + { + return false; + } + + if (!$source) + { + return false; + } + + $source_file_path = $source; + + $ds = DIRECTORY_SEPARATOR; + + // If the file came from the Media Manager file and we are copying it, fix its filename + if ($media_uploader_file_data['is_media_uploader_file']) + { + $media_uploader_file_data['media_uploader_filename'] = self::getFilePathFromMediaUploaderFile($media_uploader_file_data['media_uploader_filename']); + } + + $source_image_relative = ''; + $original_image_relative = ''; + + // Create source image by cloning the original image + $original_image_extension = pathinfo($source, PATHINFO_EXTENSION); + $original_image_destination = str_replace('.' . $original_image_extension, '_original.' . $original_image_extension, $source); + + // Thumbnail file name + $thumb_image_destination = str_replace('.' . $original_image_extension, '_thumb.' . $original_image_extension, $source); + + // Check whether to copy and resize the original image + if ($resizeSettings['original_image_resize']) + { + if ($resizeSettings['original_image_resize_width'] && $resizeSettings['original_image_resize_height']) + { + $original_image_full = Image::resize($source, $resizeSettings['original_image_resize_width'], $resizeSettings['original_image_resize_height'], 70, 'crop', $original_image_destination, true); + } + else if ($resizeSettings['original_image_resize_width']) + { + $original_image_full = Image::resizeAndKeepAspectRatio($source, $resizeSettings['original_image_resize_width'], 70, $original_image_destination, true); + } + else if ($resizeSettings['original_image_resize_height']) + { + $original_image_full = Image::resizeByHeight($source, $resizeSettings['original_image_resize_height'], $original_image_destination, 70, true); + } + + $original_image_relative = str_replace(JPATH_ROOT . $ds, '', $original_image_full); + + // Delete raw image as not needed + JoomlaCMSFile::delete($source); + } + else + { + // Original image must always be cloned by the resized original image + $original_image_full = File::move($source, $original_image_destination); + $original_image_relative = str_replace(JPATH_ROOT . $ds, '', $original_image_full); + } + + // Generate thumbnails + if (!$thumb_data = self::generateThumbnail($original_image_full, $thumb_image_destination, $resizeSettings)) + { + return false; + } + + // Add watermark image + if (isset($upload_settings['watermark']['type']) && $upload_settings['watermark']['type'] !== 'disabled') + { + // Clone source image from original image and hash it + $source_image_full = File::copy($original_image_full, $source_file_path, false, true); + $source_image_relative = str_replace(JPATH_ROOT . $ds, '', $source_image_full); + + // Add watermark to original image + $payload = array_merge($upload_settings['watermark'], [ + 'source' => $original_image_full + ]); + \NRFramework\Image::applyWatermark($payload); + + if (isset($upload_settings['watermark']['apply_on_thumbnails']) && $upload_settings['watermark']['apply_on_thumbnails']) + { + // Add watermark to original image + $payload = array_merge($upload_settings['watermark'], [ + 'source' => implode($ds, [self::getFullTempFolder(), $thumb_data['resized_filename']]) + ]); + \NRFramework\Image::applyWatermark($payload); + } + } + + $tmp_folder = self::getTempFolder(); + + return [ + 'source' => $source_image_relative ? $source_image_relative : '', + 'original' => $original_image_relative, + 'thumbnail' => implode($ds, [$tmp_folder, $thumb_data['resized_filename']]) + ]; + } + + /** + * Moves all given `tmp` items over to the destination folder. + * + * @param array $value + * @param object $field + * @param string $destination_folder + * + * @return void + */ + public static function moveTempItemsToDestination($value, $field, $destination_folder) + { + if (!$destination_folder) + { + return; + } + + // Create destination folder if missing + if (!File::createDirs($destination_folder)) + { + return; + } + + // Make field params use Registry + if (!$field->fieldparams instanceof Registry) + { + $field->fieldparams = new Registry($field->fieldparams); + } + + /** + * Prepare the items for backwards compatibility + */ + $items = is_string($value) ? json_decode($value, true) ?? [['value' => $value]] : $value; + + $items = isset($items['items']) ? $items['items'] : $items; + + if (isset($items['value'])) + { + $items = [$items]; + } + + $limit_files = (int) $field->fieldparams->get('limit_files', 0); + + // Handle single file + if ($limit_files === 1 && is_array($items)) + { + $items = [reset($items)]; + } + + $ds = DIRECTORY_SEPARATOR; + + // Move all files from `tmp` folder over to the `upload folder` + foreach ($items as $key => &$item) + { + /** + * Skip invalid files. + * + * These "files" can appear when we try to move files + * over to the destination folder when the gallery manager + * is still working to upload queueed files. + * + * Also skip any items that have no value. + */ + if ($key === 'ITEM_ID' || empty($item['thumbnail'])) + { + continue; + } + + $moved = false; + + // Ensure thumbnail in temp folder file exists + $thumbnail_clean = pathinfo($item['thumbnail'], PATHINFO_BASENAME); + $thumbnail_path = implode($ds, [JPATH_ROOT, $item['thumbnail']]); + // Move thumbnail image + if (Functions::startsWith($item['thumbnail'], self::getTempFolder()) && file_exists($thumbnail_path)) + { + // Move thumbnail + $thumb = File::move($thumbnail_path, $destination_folder . $thumbnail_clean); + + // Update thumbnail file name + $item['thumbnail'] = pathinfo($thumb, PATHINFO_BASENAME); + + $moved = true; + } + + // Check if we have uploaded the full image as well and set it + $image_clean = pathinfo($item['image'], PATHINFO_BASENAME); + $image_path = implode($ds, [JPATH_ROOT, $item['image']]); + // Move original image + if (Functions::startsWith($item['image'], self::getTempFolder()) && file_exists($image_path)) + { + // Move image + $image = File::move($image_path, $destination_folder . $image_clean); + + // Update image file name + $item['image'] = pathinfo($image, PATHINFO_BASENAME); + + $moved = true; + } + + // Ensure source in temp folder file exists + $item['source'] = isset($item['source']) ? $item['source'] : ''; + + // If source does not exist, create it from the original image, only if watermark is enabled + if (!$item['source'] && $field->fieldparams->get('watermark.type', 'disabled') !== 'disabled') + { + // Create source from original image + $source = File::copy($image_path, $image_path, false, true); + + // Update source file name + $item['source'] = pathinfo($source, PATHINFO_BASENAME); + + $moved = true; + } + + // Move source image + $source_clean = pathinfo($item['source'], PATHINFO_BASENAME); + $source_path = implode($ds, [JPATH_ROOT, $item['source']]); + if (Functions::startsWith($item['source'], self::getTempFolder()) && file_exists($source_path)) + { + // Move source + $thumb = File::move($source_path, $destination_folder . $source_clean); + + // Update source file name + $item['source'] = pathinfo($thumb, PATHINFO_BASENAME); + + $moved = true; + } + + if ($moved) + { + // Update destination path + self::updateDestinationPath($item, $destination_folder); + } + } + + return $items; + } + + /** + * Saves the tags for each item. + * + * @param array $value + * + * @return array + */ + public static function saveItemTags($value = []) + { + if (!is_array($value)) + { + return $value; + } + + foreach ($value as &$item) + { + if (!isset($item['tags']) || !is_string($item['tags'])) + { + $item['tags'] = []; + continue; + } + + if (!$itemTags = json_decode($item['tags'], true)) + { + $item['tags'] = []; + continue; + } + + if (!is_array($itemTags)) + { + $item['tags'] = []; + continue; + } + + if (!$itemTags) + { + $item['tags'] = []; + continue; + } + + // Make $itemTags an array of strings + $itemTags = array_map(function($tag) { + return (string) $tag; + }, $itemTags); + + /** + * Creates the new tags in the #__tags table. + * + * This returns an array of the new tag ids. If a tag isn't new (doesn't have #new# prefix), it will return 0 as its id. + * + * We will now store the IDs returned as the tags for the item. + */ + $item['tags'] = self::createTagsFromField($itemTags); + } + + return $value; + } + + /** + * Create any new tags by looking for #new# in the strings + * + * @param array $tags Tags text array from the field + * + * @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false. + * + * @since 3.1 + */ + public static function createTagsFromField($tags) + { + if (empty($tags) || $tags[0] == '') + { + return; + } + + // We will use the tags table to store them + if (defined('nrJ4')) + { + $tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); + } + else + { + Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables'); + $tagTable = Table::getInstance('Tag', 'TagsTable'); + } + + $newTags = []; + + foreach ($tags as $key => $tag) + { + // Remove the #new# prefix that identifies new tags + $tagText = str_replace('#new#', '', $tag); + + if ($tagText === $tag) + { + $newTags[] = (int) $tag; + } + else + { + // Clear old data if exist + $tagTable->reset(); + + // Try to load the selected tag + if ($tagTable->load(['title' => $tagText])) + { + $newTags[] = (int) $tagTable->id; + } + else + { + // Prepare tag data + $tagTable->id = 0; + $tagTable->title = $tagText; + $tagTable->published = 1; + $tagTable->description = ''; + + $tagTable->language = '*'; + $tagTable->access = 1; + + // Make this item a child of the root tag + $tagTable->setLocation($tagTable->getRootId(), 'last-child'); + + // Try to store tag + if ($tagTable->check()) + { + // Assign the alias as path (autogenerated tags have always level 1) + $tagTable->path = $tagTable->alias; + + if ($tagTable->store()) + { + $newTags[] = (int) $tagTable->id; + } + } + } + } + } + + // At this point $newTags is an array of all tag ids + return $newTags; + } + + /** + * Updates the destination path for the image and its thumbnail to the final destination folder. + * + * @param array $item + * @param string $destination_folder + * + * @return mixed + */ + private static function updateDestinationPath(&$item, $destination_folder) + { + $ds = DIRECTORY_SEPARATOR; + + // Ensure destination folder is a relative path + $destination_folder = ltrim(rtrim(str_replace(JPATH_ROOT, '', $destination_folder), $ds), $ds); + + $item = array_merge($item, [ + 'source' => !empty($item['source']) && !Functions::startsWith($item['source'], $destination_folder) ? implode($ds, [$destination_folder, $item['source']]) : $item['source'], + 'thumbnail' => !Functions::startsWith($item['thumbnail'], $destination_folder) ? implode($ds, [$destination_folder, $item['thumbnail']]) : $item['thumbnail'], + 'image' => !Functions::startsWith($item['image'], $destination_folder) ? implode($ds, [$destination_folder, $item['image']]) : $item['image'] + ]); + } + + /** + * Sets the custom field item id > field id value "source" to given source image path for the original image path + */ + public static function setItemFieldSource($item_id, $field_id, $sourceImagePath, $originalImagePath) + { + // Get "value" column from #__fields_values where item_id = $item_id and $field_id = $field_id + $db = Factory::getDbo(); + + $query = $db->getQuery(true) + ->select($db->qn('value')) + ->from($db->qn('#__fields_values')) + ->where($db->qn('item_id') . ' = ' . $db->q($item_id)) + ->where($db->qn('field_id') . ' = ' . $db->q($field_id)); + + $db->setQuery($query); + + $value = $db->loadResult(); + + // If value is empty, return + if (!$value) + { + return; + } + + // Decode value + $value = json_decode($value, true); + + // If value is empty, return + if (!$value) + { + return; + } + + // If value is not an array, return + if (!is_array($value)) + { + return; + } + + // If value has no items, return + if (!isset($value['items'])) + { + return; + } + + // Loop all items until we find a "image" = $originalImagePath and then set 'source' = $sourceImagePath + foreach ($value['items'] as $key => &$item) + { + if ($item['image'] !== $originalImagePath) + { + continue; + } + + $item['source'] = $sourceImagePath; + } + + // Update value + $query = $db->getQuery(true) + ->update($db->qn('#__fields_values')) + ->set($db->qn('value') . ' = ' . $db->q(json_encode($value))) + ->where($db->qn('item_id') . ' = ' . $db->q($item_id)) + ->where($db->qn('field_id') . ' = ' . $db->q($field_id)); + + $db->setQuery($query); + + $db->execute(); + } + + /** + * Media Uploader files look like: https://example.com/images/sampledata/parks/banner_cradle.png + * We remove the first part (https://example.com/images/) and keep the other part (relative path to image). + * + * @param string $filename + * + * @return string + */ + private static function getFilePathFromMediaUploaderFile($filename) + { + $filenameArray = explode('images/', $filename, 2); + unset($filenameArray[0]); + $new_filepath = join($filenameArray); + return 'images/' . $new_filepath; + } + + /** + * Generates thumbnail + * + * @param string $source Source image path. + * @param string $destination Destination image path. + * @param array $resizeSettings Resize Settings. + * @param string $destination_folder Destination folder. + * @param boolean $unique_filename Whether the thumbnails will have a unique filename. + * + * @return array + */ + public static function generateThumbnail($source = '', $destination = '', $resizeSettings = [], $destination_folder = null, $unique_filename = true) + { + if (!$destination) + { + $parts = pathinfo($source); + $destination_folder = !is_null($destination_folder) ? $destination_folder : $parts['dirname'] . DIRECTORY_SEPARATOR; + $destination = $destination_folder . $parts['filename'] . '_thumb.' . $parts['extension']; + } + + $resized_image = null; + + $thumb_width = isset($resizeSettings['thumb_width']) ? (int) $resizeSettings['thumb_width'] : null; + $thumb_height = isset($resizeSettings['thumb_height']) ? (int) $resizeSettings['thumb_height'] : null; + + // If thumbnail width is null, and we have item height set, we are resizing by height + if (is_null($thumb_width) && $thumb_height && !is_null($thumb_height)) + { + $resized_image = Image::resizeByHeight($source, $thumb_height, $destination, 70, $unique_filename, true, 'resize'); + } + else + { + if (is_null($thumb_width) || !$thumb_width) + { + return; + } + + /** + * If height is zero, then we suppose we want to keep aspect ratio. + * + * Resize with width & height: If thumbnail height is not set + * Resize and keep aspect ratio: If thumbnail height is set + */ + $resized_image = $thumb_height && !is_null($thumb_height) + ? + Image::resize($source, $thumb_width, $thumb_height, 70, $resizeSettings['thumb_resize_method'], $destination, $unique_filename, true, 'resize') + : + Image::resizeAndKeepAspectRatio($source, $thumb_width, 70, $destination, $unique_filename, true, 'resize'); + + } + + if (!$resized_image) + { + return; + } + + return [ + 'filename' => basename($source), + 'resized_filename' => basename($resized_image) + ]; + } + + /** + * Deletes an uploaded files: source, original, and thumbnail. + * + * @param string $source The source image path. + * @param string $original The original image path. + * @param string $thumbnail The thumbnail image path. + * + * @return bool + */ + public static function deleteFile($source = null, $original = null, $thumbnail = null) + { + return [ + 'deleted_source_image' => self::findAndDeleteFile($source), + 'deleted_original_image' => self::findAndDeleteFile($original), + 'deleted_thumbnail' => self::findAndDeleteFile($thumbnail) + ]; + } + + /** + * Deletes the file. + * + * @param string $filepath + * + * @return mixed + */ + private static function findAndDeleteFile($filepath) + { + if (!$filepath) + { + return; + } + + $file = Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $filepath])); + + return file_exists($file) ? JoomlaCMSFile::delete($file) : false; + } + + /** + * Cleans the temp folder. + * + * Removes any image that is 1 day or older. + * + * @return void + */ + public static function clean() + { + $temp_folder = self::getFullTempFolder(); + + if (!is_dir($temp_folder)) + { + return; + } + + // Get images + $files = array_diff(scandir($temp_folder), ['.', '..', '.DS_Store', 'index.html']); + + $found = []; + + foreach ($files as $key => $filename) + { + $file_path = implode(DIRECTORY_SEPARATOR, [$temp_folder, $filename]); + + // Skip directories + if (is_dir($file_path)) + { + continue; + } + + $diff_in_miliseconds = time() - filemtime($file_path); + + // Skip the file if it's not old enough + if ($diff_in_miliseconds < (60 * 60 * 24 * self::$temp_files_cleanup_days)) + { + continue; + } + + $found[] = $file_path; + } + + if (!$found) + { + return; + } + + // Delete found old files + foreach ($found as $file) + { + unlink($file); + } + } + + /** + * Full temp directory where images are uploaded + * prior to them being saved in the final directory. + * + * @return string + */ + private static function getFullTempFolder() + { + return implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, self::getTempFolder()]); + } + + /** + * Temp folder where images are uploaded + * prior to them being saved in the final directory. + * + * @var string + */ + public static function getTempFolder() + { + return implode(DIRECTORY_SEPARATOR, ['media', 'tfgallerymanager', 'tmp']); + } + + /** + * Deletes a specific tag from every gallery item. + * + * @param int $tag_id + * @param string $context + * + * @return void + */ + public static function deleteTagFromFieldsValues($tag_id = null, $context = '') + { + if (!$tag_id) + { + return; + } + + if ($context === '') + { + self::deleteTagFromCustomFieldsByTagId($tag_id); + self::deleteTagFromSubformCustomFieldsByTagId($tag_id); + } + } + + /** + * Deletes a specific tag from every gallery item custom field. + * + * @param int $tag_id + * + * @return void + */ + private static function deleteTagFromCustomFieldsByTagId($tag_id = null) + { + if (!$tag_id) + { + return; + } + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('f.id as field_id, fv.item_id as item_id, fv.value as value') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('f.type = ' . $db->quote('acfgallery')); + + $db->setQuery($query); + + $fields = $db->loadAssocList(); + + if (!$fields) + { + return; + } + + foreach ($fields as $field) + { + if (!$decoded_value = json_decode($field['value'], true)) + { + continue; + } + + if (!isset($decoded_value['items'])) + { + continue; + } + + $update = false; + + foreach ($decoded_value['items'] as &$item) + { + if (!isset($item['tags'])) + { + continue; + } + + if (!is_array($item['tags'])) + { + continue; + } + + if (!count($item['tags'])) + { + continue; + } + + $item['tags'] = array_values($item['tags']); + + if (($key = array_search($tag_id, $item['tags'])) !== false) + { + $update = true; + unset($item['tags'][$key]); + } + + $item['tags'] = array_values($item['tags']); + } + + if (!$update) + { + continue; + } + + $field['value'] = json_encode($decoded_value); + + // Update field value + $query->clear() + ->update('#__fields_values') + ->set($db->quoteName('value') . ' = ' . $db->quote($field['value'])) + ->where($db->quoteName('field_id') . ' = ' . $db->quote($field['field_id'])) + ->where($db->quoteName('item_id') . ' = ' . $db->quote($field['item_id'])); + $db->setQuery($query); + $db->execute(); + } + } + + /** + * Deletes a specific tag from every gallery item that exists in a subform custom field. + * + * @param int $tag_id + * + * @return void + */ + private static function deleteTagFromSubformCustomFieldsByTagId($tag_id = null) + { + if (!$tag_id) + { + return; + } + + if (!$tag_id) + { + return; + } + + $db = Factory::getDbo(); + + // Get all ACF Gallery custom field IDs + $query = $db->getQuery(true) + ->select('distinct f.id') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('f.type = ' . $db->quote('acfgallery')); + $db->setQuery($query); + $gallery_field_ids = array_keys($db->loadAssocList('id')); + + if (!$gallery_field_ids) + { + return; + } + + // Get all Subform custom fields + $query->clear() + ->select('f.id as field_id, fv.item_id as item_id, fv.value as value') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('f.type = ' . $db->quote('subform')); + $db->setQuery($query); + $subform_fields = $db->loadAssocList(); + + foreach ($subform_fields as $subform_field) + { + if (!$subform_field_items = json_decode($subform_field['value'], true)) + { + continue; + } + + $update = false; + + foreach ($subform_field_items as $row => &$row_items) + { + if (!is_array($row_items)) + { + continue; + } + + foreach ($row_items as $field_name => &$field_value) + { + // Get the field id + $field_id = str_replace('field', '', $field_name); + + // Check if its a gallery field + if (!in_array($field_id, $gallery_field_ids)) + { + continue; + } + + if (!isset($field_value['items'])) + { + continue; + } + + foreach ($field_value['items'] as &$item) + { + if (!isset($item['tags'])) + { + continue; + } + + if (!is_array($item['tags'])) + { + continue; + } + + if (!count($item['tags'])) + { + continue; + } + + $item['tags'] = array_values($item['tags']); + + if (($key = array_search($tag_id, $item['tags'])) !== false) + { + $update = true; + unset($item['tags'][$key]); + } + + $item['tags'] = array_values($item['tags']); + } + } + } + + if (!$update) + { + continue; + } + + $subform_field['value'] = json_encode($subform_field_items); + + // Update subform field value + $query->clear() + ->update('#__fields_values') + ->set($db->quoteName('value') . ' = ' . $db->quote($subform_field['value'])) + ->where($db->quoteName('field_id') . ' = ' . $db->quote($subform_field['field_id'])) + ->where($db->quoteName('item_id') . ' = ' . $db->quote($subform_field['item_id'])); + $db->setQuery($query); + $db->execute(); + } + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Widgets/GalleryManager2.php b/plugins/system/nrframework/NRFramework/Helpers/Widgets/GalleryManager2.php new file mode 100644 index 00000000..32da780d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Widgets/GalleryManager2.php @@ -0,0 +1,1250 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Widgets; + +defined('_JEXEC') or die; + +use NRFramework\File; +use NRFramework\Image; +use NRFramework\Functions; +use Joomla\Registry\Registry; +use Joomla\CMS\Table\Table; +use Joomla\CMS\Factory; +use Joomla\Filesystem\Path; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File as JoomlaCMSFile; + +class GalleryManager2 +{ + /** + * How long the files can stay in the temp folder. + * + * After each save a clean up is run and all files older + * than this value in days are removed. + * + * @var int + */ + private static $temp_files_cleanup_days = 1; + + /** + * Uploads the source image to the temp folder. + * + * @param array $file The request file as posted by form + * @param string $upload_settings The upload settings + * + * @return array|bool The uploaded image paths or false on failure + */ + public static function upload($file, $uploadSettings) + { + $fullTempFolder = self::getFullTempFolder($uploadSettings['context'], $uploadSettings['field_id'], $uploadSettings['item_id']); + + // Create source folder if not exists + File::createDirs($fullTempFolder); + + // Move the image to the tmp folder + try { + $source = File::upload($file, $fullTempFolder, $uploadSettings['allowed_types'], $uploadSettings['allow_unsafe'], null, $uploadSettings['random_suffix']); + } catch (\Throwable $th) + { + return false; + } + + if (!$source) + { + return false; + } + + return str_replace(JPATH_ROOT . DIRECTORY_SEPARATOR, '', $source); + } + + public static function maybeRegenerateImages($context = 'default', $items = [], $field_id = null, $item_id = null, $oldData = []) + { + if (!$oldData) + { + return false; + } + + if (!$field_data = self::getSettings($context, $field_id, $item_id)) + { + return false; + } + + /** + * In order to proceed, there must be changes in the following data: + * + * - provider + * - full size image + * - slideshow image + * - thumbnails + * - watermark + */ + $images = self::canRegenerateImages($field_data, $oldData); + + $items = is_string($items) ? json_decode($items, true) : $items; + + foreach ($items as &$item) + { + self::generateFromSource($item, $images, $field_data, false); + } + + return $items; + } + + /** + * Checks if the images need to be regenerated. + * + * @param object $new_data + * @param object $old_data + * + * @return array + */ + public static function canRegenerateImages($new_data = [], $old_data = []) + { + $images = [ + 'thumb' => false, + 'slideshow' => false, + 'full' => false + ]; + + // On provider change + if ($old_data->get('provider') != $new_data->get('provider')) + { + if ($new_data->get('provider') === 'slideshow') + { + $images['slideshow'] = true; + + if ($new_data->get('show_thumbnails') === '1') + { + $images['thumb'] = true; + } + } + else + { + $images['thumb'] = true; + } + } + // On thumbnails dimensions change + else if ($old_data->get('thumbnail_size') != $new_data->get('thumbnail_size') || $old_data->get('justified_item_height') != $new_data->get('justified_item_height') || $old_data->get('show_thumbnails') != $new_data->get('show_thumbnails') || $old_data->get('masonry_thumbnails_width') != $new_data->get('masonry_thumbnails_width') || $old_data->get('slideshow_thumbnail_size') != $new_data->get('slideshow_thumbnail_size')) + { + $images['thumb'] = true; + } + + // Check if full image has changed + if ($old_data->get('full_image') != $new_data->get('full_image') || ($old_data->get('lightbox') != $new_data->get('lightbox') && $new_data->get('lightbox') === '1')) + { + $images['full'] = true; + } + + // Check if slideshow image has changed + if ($new_data->get('provider') === 'slideshow' && $old_data->get('slideshow_image') != $new_data->get('slideshow_image')) + { + $images['slideshow'] = true; + } + + // Check if watermark settings have changed + if ($old_data->get('watermark') != $new_data->get('watermark')) + { + if ($new_data->get('lightbox') === '1') + { + $images['full'] = true; + } + + if ($old_data->get('slideshow_image') != $new_data->get('slideshow_image') || $new_data->get('provider') === 'slideshow') + { + $images['slideshow'] = true; + } + + if ($old_data->get('provider') !== 'slideshow' || ($new_data->get('provider') === 'slideshow' && $new_data->get('show_thumbnails') === '1')) + { + $images['thumb'] = true; + } + } + + return $images; + } + + /** + * Returns the settings for the given context. + * + * @param string $context + * @param int $field_id + * @param int $item_id + * + * @return mixed + */ + public static function getSettings($context = 'default', $field_id = null, $item_id = null) + { + // Make sure we have a valid context + if (!$context) + { + return false; + } + + $field_data = []; + + if ($context === 'default') + { + // Make sure we have a valid field id + if (!$field_id) + { + return Text::_('NR_GALLERY_MANAGER_FIELD_ID_ERROR'); + } + + if (!$field_data = \NRFramework\Helpers\CustomField::getData($field_id)) + { + return Text::_('NR_GALLERY_MANAGER_INVALID_FIELD_DATA'); + } + } + else if ($context === 'module') + { + // Make sure we have a valid item id + if (!$item_id) + { + return Text::_('NR_GALLERY_MANAGER_ITEM_ID_ERROR'); + } + + if (!$field_data = \NRFramework\Helpers\Module::getData($item_id)) + { + return Text::_('NR_GALLERY_MANAGER_INVALID_FIELD_DATA'); + } + } + + return $field_data; + } + + /** + * Moves all given temp items over to the destination folder. + * + * @param array $value + * @param object $field + * @param string $destination_folder + * + * @return void + */ + public static function moveTempItemsToDestination($value, $field, $destination_folder) + { + if (!$destination_folder) + { + return; + } + + // Create destination folder if missing + if (!File::createDirs($destination_folder)) + { + return; + } + + // Make field params use Registry + if (!$field->fieldparams instanceof Registry) + { + $field->fieldparams = new Registry($field->fieldparams); + } + + /** + * Prepare the items for backwards compatibility + */ + $items = is_string($value) ? json_decode($value, true) ?? [['value' => $value]] : $value; + + $items = isset($items['items']) ? $items['items'] : $items; + + if (isset($items['value'])) + { + $items = [$items]; + } + + $limit_files = (int) $field->fieldparams->get('limit_files', 0); + + // Handle single file + if ($limit_files === 1 && is_array($items)) + { + $items = [reset($items)]; + } + + // Compatibility Start: Migrate old items to new folder structure + self::maybeMigrateOldItems($items, $field->fieldparams); + // Compatibility End + + $ds = DIRECTORY_SEPARATOR; + + $images = [ + 'thumb' => in_array($field->fieldparams->get('provider', 'grid'), ['grid', 'masonry', 'justified']) || ($field->fieldparams->get('provider', 'grid') === 'slideshow' && $field->fieldparams->get('show_thumbnails', '0') === '1'), + 'slideshow' => $field->fieldparams->get('provider', 'grid') === 'slideshow', + 'full' => $field->fieldparams->get('lightbox', '0') === '1' + ]; + + $tmpdir = Factory::getConfig()->get('tmp_path'); + $tmpRelativeDirectory = ltrim(str_replace(JPATH_ROOT, '', $tmpdir), $ds); + + // Move all files from the temp folder over to the `upload folder` + foreach ($items as $key => &$item) + { + /** + * Skip invalid files. + * + * These "files" can appear when we try to move files + * over to the destination folder when the gallery manager + * is still working to upload queueed files. + */ + if ($key === 'ITEM_ID') + { + continue; + } + + // Skip if source does not start with the temp relative directory + $testSourcePath = ltrim(rtrim($item['source'], $ds), $ds) . $ds; + if (!Functions::startsWith($testSourcePath, $tmpRelativeDirectory . $ds)) + { + continue; + } + + // Move source image to final directory + try { + $source_clean = pathinfo($item['source'], PATHINFO_BASENAME); + $source_path = implode($ds, [JPATH_ROOT, $item['source']]); + + $new_source_path = implode($ds, [rtrim($destination_folder, $ds), md5('source'), $source_clean]); + $new_source_path = File::move($source_path, $new_source_path); + $item['source'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $new_source_path), $ds), $ds); + } catch (\Throwable $th) {} + + // Generate the rest of images from the source image + self::generateFromSource($item, $images, $field->fieldparams); + } + + return $items; + } + + /** + * Check and migrate old items to the new folder structure. + * + * @param array $items + * @param object $field_data + * + * @return void + */ + public static function maybeMigrateOldItems(&$items = [], $field_data = []) + { + if (!is_array($items) || empty($items)) + { + return; + } + + $ds = DIRECTORY_SEPARATOR; + + $tmpdir = Factory::getConfig()->get('tmp_path'); + + // Migrate images to new folders and create source images from the full image if source is missing + foreach ($items as &$value) + { + // We skip this process if the "source" directory exists in the uploaded files path. + // This means that the new structure is present, so don't do anything. + if (!empty($value['source'])) + { + $fullSourcePath = implode($ds, [JPATH_ROOT, $value['source']]); + $checkDirectory = dirname($fullSourcePath); + + if (is_dir($checkDirectory) && (Functions::startsWith($checkDirectory, $tmpdir) || Functions::endsWith($checkDirectory, '/' . md5('source')))) + { + continue; + } + } + + $sourceNewPath = null; + + $fullImageCurrentPath = implode($ds, [JPATH_ROOT, $value['image']]); + $fullImageData = pathinfo($fullImageCurrentPath); + + // Create source if missing + if (empty($value['source'])) + { + $sourceNewPath = implode($ds, [dirname($fullImageCurrentPath), md5('source'), $fullImageData['basename']]); + $sourceNewPath = File::copy($fullImageCurrentPath, $sourceNewPath); + $value['source'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $sourceNewPath), $ds), $ds); + } + else + { + // Move source image + $sourceOldPath = implode($ds, [JPATH_ROOT, $value['source']]); + $sourceNewPath = implode($ds, [JPATH_ROOT, dirname($value['source']), md5('source'), $fullImageData['basename']]); + $sourceNewPath = File::move($sourceOldPath, $sourceNewPath); + $value['source'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $sourceNewPath), $ds), $ds); + } + + // Delete thumbnail and recreate from source + $needThumb = false; + $thumbOldPath = implode($ds, [JPATH_ROOT, $value['thumbnail']]); + if (is_file($thumbOldPath)) + { + $needThumb = true; + JoomlaCMSFile::delete($thumbOldPath); + $value['thumbnail'] = ''; + + if ($field_data->get('provider', 'grid') === 'slideshow' && $field_data->get('show_thumbnails', '0') === '0') + { + $needThumb = false; + } + } + + // Delete full image and recreate from source + $needFull = false; + $fullImageOldPath = implode($ds, [JPATH_ROOT, $value['image']]); + if (is_file($fullImageOldPath)) + { + JoomlaCMSFile::delete($fullImageOldPath); + $value['image'] = ''; + + if ($field_data->get('lightbox', '0') === '1') + { + $needFull = true; + } + } + + self::generateFromSource($value, [ + 'thumb' => $needThumb, + 'slideshow' => $field_data->get('provider', 'grid') === 'slideshow', + 'full' => $needFull + ], $field_data); + } + } + + /** + * Clean up unneeded images. + * + * @param array $item + * @param object $field_data + */ + public static function cleanUpImages(&$item = [], $field_data = []) + { + $ds = DIRECTORY_SEPARATOR; + $provider = $field_data->get('provider', 'grid'); + + /** + * Clean up of unneeded images. + */ + /** + * Delete thumbs if: + * 1) We're using the slideshow provider and show_thumbnails is disabled and the item has a thumbnail + * 2) We're not using the slideshow provider and the item has a thumbnail and a slideshow image + */ + if (($provider === 'slideshow' && $field_data->get('show_thumbnails', '0') === '0' && $item['thumbnail']) || ($provider !== 'slideshow' && $item['thumbnail'] && $item['slideshow'])) + { + $thumbnails_path = implode($ds, [JPATH_ROOT, $item['thumbnail']]); + if (is_file($thumbnails_path)) + { + JoomlaCMSFile::delete($thumbnails_path); + } + $item['thumbnail'] = null; + } + + // Delete full image if lightbox is disabled and the item has a full image + if ($field_data->get('lightbox') === '0' && $item['image']) + { + $full_image_path = implode($ds, [JPATH_ROOT, $item['image']]); + if (is_file($full_image_path)) + { + JoomlaCMSFile::delete($full_image_path); + } + $item['image'] = null; + } + + // Delete slideshow image if exists and not in slideshow provider + if ($provider !== 'slideshow' && $item['slideshow']) + { + $slideshow_image_path = implode($ds, [JPATH_ROOT, $item['slideshow']]); + if (is_file($slideshow_image_path)) + { + JoomlaCMSFile::delete($slideshow_image_path); + } + $item['slideshow'] = null; + } + } + + /** + * This method generates the full image, thumbnail, and slideshow images from the sources. + * + * @param array $item + * @param array $images + * @param object $field_data + * @param bool $unique_filename + * + * @return void + */ + public static function generateFromSource(&$item, $images = [], $field_data = [], $unique_filename = true) + { + $ds = DIRECTORY_SEPARATOR; + $provider = $field_data->get('provider', 'grid'); + + /** + * Clean up of unneeded images. + */ + self::cleanUpImages($item, $field_data); + + $full_source_path = implode($ds, [JPATH_ROOT, $item['source']]); + + // Create full image + if ($images['full']) + { + $full_image_path = implode($ds, [JPATH_ROOT, dirname(dirname($item['source'])), 'full', basename($item['source'])]); + $full_image_resizing_dimensions = self::getImageResizingDimensions('full_image', $field_data); + if (!empty(array_filter($full_image_resizing_dimensions))) + { + // Create the full image directory if not exists + File::createDirs(dirname($full_image_path)); + + // Resize the full image + $full_image_path = Image::resizeByWidthOrHeight($full_source_path, $full_image_resizing_dimensions['width'], $full_image_resizing_dimensions['height'], 70, $full_image_path, 'crop', $unique_filename); + + $item['image'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $full_image_path), $ds), $ds); + } + else if ($field_data->get('full_image.by', '') === 'disabled') + { + // Create the full image directory if not exists + File::createDirs(dirname($full_image_path)); + + // Copy image to full folder + $full_image_path = File::copy($full_source_path, $full_image_path, !$unique_filename); + + $item['image'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $full_image_path), $ds), $ds); + } + } + + // Create thumbnail image + if ($images['thumb']) + { + $thumbs_folder_name = $provider === 'slideshow' ? 'thumb' : ''; + $thumbnail_image_path = implode($ds, array_filter([JPATH_ROOT, dirname(dirname($item['source'])), $thumbs_folder_name, basename($item['source'])])); + $thumbnail_resizing_dimensions = [ + 'width' => null, + 'height' => null + ]; + + if ($provider === 'justified') + { + $thumbnail_resizing_dimensions['height'] = $field_data->get('justified_item_height', 200); + } + else if ($provider === 'masonry') + { + $thumbnail_resizing_dimensions['width'] = $field_data->get('masonry_thumbnails_width', 200); + } + else + { + $thumbnail_size = $provider === 'slideshow' ? $field_data->get('slideshow_thumbnail_size', 200) : $field_data->get('thumbnail_size', 200); + + $thumbnail_resizing_dimensions = [ + 'width' => $thumbnail_size, + 'height' => $thumbnail_size + ]; + + // If slideshow, require show_thumbnails to be enabled + if ($provider === 'slideshow' && $field_data->get('show_thumbnails', '0') === '0') + { + $thumbnail_resizing_dimensions = null; + } + } + + if ($thumbnail_resizing_dimensions) + { + // Create the full image directory if not exists + File::createDirs(dirname($thumbnail_image_path)); + + // Resize the full image + $thumbnail_image_path = Image::resizeByWidthOrHeight($full_source_path, $thumbnail_resizing_dimensions['width'], $thumbnail_resizing_dimensions['height'], 70, $thumbnail_image_path, 'crop', $unique_filename); + + $item['thumbnail'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $thumbnail_image_path), $ds), $ds); + } + } + + // Create slideshow image + if ($images['slideshow'] && $provider === 'slideshow') + { + $slideshow_image_path = implode($ds, [JPATH_ROOT, dirname(dirname($item['source'])), basename($item['source'])]); + if ($slideshow_resizing_dimensions = self::getImageResizingDimensions('slideshow_image', $field_data)) + { + // Create the full image directory if not exists + File::createDirs(dirname($slideshow_image_path)); + + // Resize the full image + $slideshow_image_path = Image::resizeByWidthOrHeight($full_source_path, $slideshow_resizing_dimensions['width'], $slideshow_resizing_dimensions['height'], 70, $slideshow_image_path, 'crop', $unique_filename); + + $item['slideshow'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $slideshow_image_path), $ds), $ds); + } + } + + self::applyWatermarkOnImages($item, $images, $field_data); + } + + /** + * Applies the watermark on the images. + * + * @param array $payload + * @param array $images + * @param object $field_data + * + * @return void + */ + public static function applyWatermarkOnImages($payload = [], $images = [], $field_data = []) + { + if ($field_data->get('watermark.type', 'disabled') === 'disabled') + { + return; + } + + $ds = DIRECTORY_SEPARATOR; + + $watermarkSettings = (array) $field_data->get('watermark', []); + + $apply_on_thumbnails = $watermarkSettings['apply_on_thumbnails'] === '1'; + + $watermarkSettings = array_merge($watermarkSettings, [ + 'image' => !empty($watermarkSettings['image']) ? explode('#', JPATH_SITE . DIRECTORY_SEPARATOR . $watermarkSettings['image'])[0] : null, + ]); + + if ($images['full']) + { + // Add watermark to full image + $watermarkPayload = array_merge($watermarkSettings, [ + 'source' => implode($ds, [JPATH_ROOT, $payload['image']]) + ]); + \NRFramework\Image::applyWatermark($watermarkPayload); + } + + if ($images['slideshow']) + { + // Add watermark to slideshow image + if ($field_data->get('provider', 'grid') === 'slideshow') + { + $watermarkPayload = array_merge($watermarkSettings, [ + 'source' => implode($ds, [JPATH_ROOT, $payload['slideshow']]) + ]); + \NRFramework\Image::applyWatermark($watermarkPayload); + } + } + + if ($apply_on_thumbnails && $images['thumb']) + { + // Add watermark to thumbnail + $watermarkPayload = array_merge($watermarkSettings, [ + 'source' => implode($ds, [JPATH_ROOT, $payload['thumbnail']]) + ]); + \NRFramework\Image::applyWatermark($watermarkPayload); + } + } + + public static function getImageResizingDimensions($key, $field_data) + { + $width = $height = null; + + if (in_array($field_data->get($key . '.by', ''), ['width', 'custom'])) + { + $width = $field_data->get($key . '.width', null); + } + if (in_array($field_data->get($key . '.by', ''), ['height', 'custom'])) + { + $height = $field_data->get($key . '.height', null); + } + + $data = array_filter([ + 'width' => $width, + 'height' => $height + ]); + + if (!isset($data['width'])) + { + $data['width'] = null; + } + + if (!isset($data['height'])) + { + $data['height'] = null; + } + + return $data; + } + + /** + * Saves the tags for each item. + * + * @param array $value + * + * @return array + */ + public static function saveItemTags($value = []) + { + if (!is_array($value)) + { + return $value; + } + + foreach ($value as &$item) + { + if (!isset($item['tags']) || !is_string($item['tags'])) + { + $item['tags'] = []; + continue; + } + + if (!$itemTags = json_decode($item['tags'], true)) + { + $item['tags'] = []; + continue; + } + + if (!is_array($itemTags)) + { + $item['tags'] = []; + continue; + } + + if (!$itemTags) + { + $item['tags'] = []; + continue; + } + + // Make $itemTags an array of strings + $itemTags = array_map(function($tag) { + return (string) $tag; + }, $itemTags); + + /** + * Creates the new tags in the #__tags table. + * + * This returns an array of the new tag ids. If a tag isn't new (doesn't have #new# prefix), it will return 0 as its id. + * + * We will now store the IDs returned as the tags for the item. + */ + $item['tags'] = self::createTagsFromField($itemTags); + } + + return $value; + } + + /** + * Create any new tags by looking for #new# in the strings + * + * @param array $tags Tags text array from the field + * + * @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false. + * + * @since 3.1 + */ + public static function createTagsFromField($tags) + { + if (empty($tags) || $tags[0] == '') + { + return; + } + + // We will use the tags table to store them + if (defined('nrJ4')) + { + $tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); + } + else + { + Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables'); + $tagTable = Table::getInstance('Tag', 'TagsTable'); + } + + $newTags = []; + + foreach ($tags as $key => $tag) + { + // Remove the #new# prefix that identifies new tags + $tagText = str_replace('#new#', '', $tag); + + if ($tagText === $tag) + { + $newTags[] = (int) $tag; + } + else + { + // Clear old data if exist + $tagTable->reset(); + + // Try to load the selected tag + if ($tagTable->load(['title' => $tagText])) + { + $newTags[] = (int) $tagTable->id; + } + else + { + // Prepare tag data + $tagTable->id = 0; + $tagTable->title = $tagText; + $tagTable->published = 1; + $tagTable->description = ''; + + $tagTable->language = '*'; + $tagTable->access = 1; + + // Make this item a child of the root tag + $tagTable->setLocation($tagTable->getRootId(), 'last-child'); + + // Try to store tag + if ($tagTable->check()) + { + // Assign the alias as path (autogenerated tags have always level 1) + $tagTable->path = $tagTable->alias; + + if ($tagTable->store()) + { + $newTags[] = (int) $tagTable->id; + } + } + } + } + } + + // At this point $newTags is an array of all tag ids + return $newTags; + } + + /** + * Sets the custom field item id > field id value "source" to given source image path for the original image path + */ + public static function setItemFieldSource($item_id, $field_id, $sourceImagePath, $originalImagePath) + { + // Get "value" column from #__fields_values where item_id = $item_id and $field_id = $field_id + $db = Factory::getDbo(); + + $query = $db->getQuery(true) + ->select($db->qn('value')) + ->from($db->qn('#__fields_values')) + ->where($db->qn('item_id') . ' = ' . $db->q($item_id)) + ->where($db->qn('field_id') . ' = ' . $db->q($field_id)); + + $db->setQuery($query); + + $value = $db->loadResult(); + + // If value is empty, return + if (!$value) + { + return; + } + + // Decode value + $value = json_decode($value, true); + + // If value is empty, return + if (!$value) + { + return; + } + + // If value is not an array, return + if (!is_array($value)) + { + return; + } + + // If value has no items, return + if (!isset($value['items'])) + { + return; + } + + foreach ($value['items'] as $key => &$item) + { + if ($item['image'] !== $originalImagePath) + { + continue; + } + + $item['source'] = $sourceImagePath; + } + + // Update value + $query = $db->getQuery(true) + ->update($db->qn('#__fields_values')) + ->set($db->qn('value') . ' = ' . $db->q(json_encode($value))) + ->where($db->qn('item_id') . ' = ' . $db->q($item_id)) + ->where($db->qn('field_id') . ' = ' . $db->q($field_id)); + + $db->setQuery($query); + + $db->execute(); + } + + /** + * Media Uploader files look like: https://example.com/images/sampledata/parks/banner_cradle.png + * We remove the first part (https://example.com/images/) and keep the other part (relative path to image). + * + * @param string $filename + * + * @return string + */ + private static function getFilePathFromMediaUploaderFile($filename) + { + $filenameArray = explode('images/', $filename, 2); + unset($filenameArray[0]); + $new_filepath = join($filenameArray); + return 'images/' . $new_filepath; + } + + /** + * Deletes an uploaded files: source, slideshow, original, and thumbnail. + * + * @param string $source The source image path. + * @param string $slideshow The slideshow image path. + * @param string $original The original image path. + * @param string $thumbnail The thumbnail image path. + * + * @return bool + */ + public static function deleteFile($source = null, $slideshow = null, $original = null, $thumbnail = null) + { + return [ + 'deleted_source_image' => self::findAndDeleteFile($source), + 'deleted_slideshow_image' => self::findAndDeleteFile($slideshow), + 'deleted_full_image' => self::findAndDeleteFile($original), + 'deleted_thumbnail' => self::findAndDeleteFile($thumbnail) + ]; + } + + /** + * Deletes the file. + * + * @param string $filepath + * + * @return mixed + */ + private static function findAndDeleteFile($filepath) + { + if (!$filepath) + { + return; + } + + $file = Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $filepath])); + + return is_file($file) ? JoomlaCMSFile::delete($file) : false; + } + + /** + * Cleans the temp folder. + * + * Removes any image that is 1 day or older. + * + * @return void + */ + public static function clean() + { + $temp_folder = self::getFullTempFolder(); + + if (!is_dir($temp_folder)) + { + return; + } + + // Get images + $files = array_diff(scandir($temp_folder), ['.', '..', '.DS_Store', 'index.html']); + + $found = []; + + foreach ($files as $key => $filename) + { + $file_path = implode(DIRECTORY_SEPARATOR, [$temp_folder, $filename]); + + // Skip directories + if (is_dir($file_path)) + { + continue; + } + + $diff_in_miliseconds = time() - filemtime($file_path); + + // Skip the file if it's not old enough + if ($diff_in_miliseconds < (60 * 60 * 24 * self::$temp_files_cleanup_days)) + { + continue; + } + + $found[] = $file_path; + } + + if (!$found) + { + return; + } + + // Delete found old files + foreach ($found as $file) + { + unlink($file); + } + } + + /** + * Full temp directory where images are uploaded + * prior to them being saved in the final directory. + * + * @param string $context + * @param string $field_id + * @param string $item_id + * + * @return string + */ + public static function getFullTempFolder($context = 'default', $field_id = '', $item_id = '') + { + $tmpdir = Factory::getConfig()->get('tmp_path'); + + $paths = [ + $tmpdir, + 'tassos', + \NRFramework\VisitorToken::getInstance()->get(), + $context === 'module' ? 'smilepack' : 'acf', + 'gallery', + $item_id, + $field_id + ]; + + $paths = array_filter($paths); + + return implode(DIRECTORY_SEPARATOR, $paths); + } + + /** + * Deletes a specific tag from every gallery item. + * + * @param int $tag_id + * @param string $context + * + * @return void + */ + public static function deleteTagFromFieldsValues($tag_id = null, $context = '') + { + if (!$tag_id) + { + return; + } + + if ($context === '') + { + self::deleteTagFromCustomFieldsByTagId($tag_id); + self::deleteTagFromSubformCustomFieldsByTagId($tag_id); + } + } + + /** + * Deletes a specific tag from every gallery item custom field. + * + * @param int $tag_id + * + * @return void + */ + private static function deleteTagFromCustomFieldsByTagId($tag_id = null) + { + if (!$tag_id) + { + return; + } + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('f.id as field_id, fv.item_id as item_id, fv.value as value') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('f.type = ' . $db->quote('acfgallery')); + + $db->setQuery($query); + + $fields = $db->loadAssocList(); + + if (!$fields) + { + return; + } + + foreach ($fields as $field) + { + if (!$decoded_value = json_decode($field['value'], true)) + { + continue; + } + + if (!isset($decoded_value['items'])) + { + continue; + } + + $update = false; + + foreach ($decoded_value['items'] as &$item) + { + if (!isset($item['tags'])) + { + continue; + } + + if (!is_array($item['tags'])) + { + continue; + } + + if (!count($item['tags'])) + { + continue; + } + + $item['tags'] = array_values($item['tags']); + + if (($key = array_search($tag_id, $item['tags'])) !== false) + { + $update = true; + unset($item['tags'][$key]); + } + + $item['tags'] = array_values($item['tags']); + } + + if (!$update) + { + continue; + } + + $field['value'] = json_encode($decoded_value); + + // Update field value + $query->clear() + ->update('#__fields_values') + ->set($db->quoteName('value') . ' = ' . $db->quote($field['value'])) + ->where($db->quoteName('field_id') . ' = ' . $db->quote($field['field_id'])) + ->where($db->quoteName('item_id') . ' = ' . $db->quote($field['item_id'])); + $db->setQuery($query); + $db->execute(); + } + } + + /** + * Deletes a specific tag from every gallery item that exists in a subform custom field. + * + * @param int $tag_id + * + * @return void + */ + private static function deleteTagFromSubformCustomFieldsByTagId($tag_id = null) + { + if (!$tag_id) + { + return; + } + + if (!$tag_id) + { + return; + } + + $db = Factory::getDbo(); + + // Get all ACF Gallery custom field IDs + $query = $db->getQuery(true) + ->select('distinct f.id') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('f.type = ' . $db->quote('acfgallery')); + $db->setQuery($query); + $gallery_field_ids = array_keys($db->loadAssocList('id')); + + if (!$gallery_field_ids) + { + return; + } + + // Get all Subform custom fields + $query->clear() + ->select('f.id as field_id, fv.item_id as item_id, fv.value as value') + ->from('#__fields as f') + ->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id') + ->where('f.type = ' . $db->quote('subform')); + $db->setQuery($query); + $subform_fields = $db->loadAssocList(); + + foreach ($subform_fields as $subform_field) + { + if (!$subform_field_items = json_decode($subform_field['value'], true)) + { + continue; + } + + $update = false; + + foreach ($subform_field_items as $row => &$row_items) + { + if (!is_array($row_items)) + { + continue; + } + + foreach ($row_items as $field_name => &$field_value) + { + // Get the field id + $field_id = str_replace('field', '', $field_name); + + // Check if its a gallery field + if (!in_array($field_id, $gallery_field_ids)) + { + continue; + } + + if (!isset($field_value['items'])) + { + continue; + } + + foreach ($field_value['items'] as &$item) + { + if (!isset($item['tags'])) + { + continue; + } + + if (!is_array($item['tags'])) + { + continue; + } + + if (!count($item['tags'])) + { + continue; + } + + $item['tags'] = array_values($item['tags']); + + if (($key = array_search($tag_id, $item['tags'])) !== false) + { + $update = true; + unset($item['tags'][$key]); + } + + $item['tags'] = array_values($item['tags']); + } + } + } + + if (!$update) + { + continue; + } + + $subform_field['value'] = json_encode($subform_field_items); + + // Update subform field value + $query->clear() + ->update('#__fields_values') + ->set($db->quoteName('value') . ' = ' . $db->quote($subform_field['value'])) + ->where($db->quoteName('field_id') . ' = ' . $db->quote($subform_field['field_id'])) + ->where($db->quoteName('item_id') . ' = ' . $db->quote($subform_field['item_id'])); + $db->setQuery($query); + $db->execute(); + } + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Helpers/Widgets/MapAddress.php b/plugins/system/nrframework/NRFramework/Helpers/Widgets/MapAddress.php new file mode 100644 index 00000000..9267d102 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Helpers/Widgets/MapAddress.php @@ -0,0 +1,51 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Helpers\Widgets; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class MapAddress +{ + /** + * Returns the default address details layout. + * + * @param array $address + * @param array $showAddressDetails + * + * @return string + */ + public static function getDefaultAddressDetailsLayout($address = [], $showAddressDetails = []) + { + if (empty($address) || empty($showAddressDetails)) + { + return; + } + + $html = ''; + + $template = '
%s: %s
'; + + foreach ($showAddressDetails as $key) + { + $value = isset($address[$key]) ? $address[$key] : ''; + + if (empty($value)) + { + continue; + } + + $html .= sprintf($template, Text::_('NR_' . strtoupper($key)), $value); + } + + return $html; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Image.php b/plugins/system/nrframework/NRFramework/Image.php new file mode 100644 index 00000000..e8b3dc0f --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Image.php @@ -0,0 +1,832 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework; + +// No direct access +defined('_JEXEC') or die; + +use NRFramework\Mimes; +use NRFramework\File; +use Joomla\CMS\Image\Image as JoomlaImage; +use Joomla\Filesystem\Path; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; + +class Image +{ + /** + * Resize an image. + * + * @param string $source + * @param string $width + * @param string $height + * @param integer $quality + * @param string $mode + * @param boolean $unique_filename + * @param boolean $fix_orientation + * @param string $gif_mode If the uploaded image is a GIF image, how will it be copied? Options: "copy" source, "resize" source + * + * @return mixed + */ + public static function resize($source, $width, $height, $quality = 70, $mode = 'crop', $destination = '', $unique_filename = false, $fix_orientation = true, $gif_mode = 'copy') + { + $width = (int) $width; + $height = (int) $height; + + // Destination file name + $destination = empty($destination) ? $source : $destination; + + // size must be WIDTHxHEIGHT + $size = $width . 'x' . $height; + + switch ($mode) + { + // Crop and Resize + case 'crop': + $mode = 5; + break; + // Scale Fill + case 'stretch': + $mode = 1; + break; + // Fit, will fill empty space with black + case 'fit': + $mode = 6; + break; + default: + $mode = 5; + break; + } + + try { + $image = new JoomlaImage($source); + + $origWidth = $image->getWidth(); + $origHeight = $image->getHeight(); + + /** + * If the image width is less than the given width, + * set the image width we are resizing to the image's width. + */ + if ($origWidth < $width) + { + $size = $origWidth . 'x'; + + if ($origHeight < $height) + { + $size .= $origHeight; + } + else + { + $size .= $height; + } + } + else if ($origHeight < $height) + { + $prefix = $width; + + if ($origWidth < $width) + { + $prefix = $origWidth; + } + + $size = $prefix . 'x' . $origHeight; + } + + // Fix orientation + if ($fix_orientation) + { + self::fixOrientation($image); + } + + // Determine the MIME of the original file to get the proper type + $mime = Mimes::detectFileType($source); + + // PNG images should not have a quality value + $options = $mime == 'image/png' ? ['quality' => 9] : ['quality' => $quality]; + + // Get the image type + $image_type = self::getImageType($mime); + + if ($unique_filename) + { + // Make destination file unique + File::uniquefy($destination); + } + + $destination = Path::clean($destination); + + // Resize image + if ($mime === 'image/gif') + { + if ($gif_mode === 'copy') + { + File::copy($source, $destination, true); + } + else + { + foreach ($image->generateThumbs($size, $mode) as $thumb) + { + $thumb->toFile($destination, $image_type, $options); + } + } + } + else + { + foreach ($image->generateThumbs($size, $mode) as $thumb) + { + $thumb->toFile($destination, $image_type, $options); + } + } + + return $destination; + } catch(\Exception $e) {} + + return false; + } + + /** + * Resizes an image by height. + * + * @param string $src + * @param string $height + * @param string $destination + * @param int $quality + * @param bool $unique_filename + * @param bool $fix_orientation + * @param string $gif_mode If the uploaded image is a GIF image, how will it be copied? Options: "copy" source, "resize" source + * + * @return bool + */ + public static function resizeByHeight($src, $height, $destination = null, $quality = 70, $unique_filename = false, $fix_orientation = true, $gif_mode = 'copy') + { + $height = (int) $height; + + // Create a new JImage object from the source image path + $image = new JoomlaImage($src); + + // Fix orientation + if ($fix_orientation) + { + self::fixOrientation($image); + } + + // Determine the MIME of the original file to get the proper type + $mime = Mimes::detectFileType($src); + + // Get the image type + $image_type = self::getImageType($mime); + + // Output file name + $destination = empty($destination) ? $src : $destination; + + if ($unique_filename) + { + // Make destination file unique + File::uniquefy($destination); + } + + $destination = Path::clean($destination); + + // PNG images should not have a quality value + $options = $mime == 'image/png' ? ['quality' => 9] : ['quality' => $quality]; + + // Get the original width and height of the image + $origWidth = $image->getWidth(); + $origHeight = $image->getHeight(); + + // Calculate the new width based on the desired height + $newWidth = ($origWidth / $origHeight) * $height; + + // Resize image + if ($mime === 'image/gif') + { + if ($gif_mode === 'copy') + { + File::copy($source, $destination, true); + } + else + { + $resizedImage = $image->resize($newWidth, $height); + $resizedImage->toFile($destination, $image_type, $options); + } + } + else + { + $resizedImage = $image->resize($newWidth, $height); + $resizedImage->toFile($destination, $image_type, $options); + } + + // Return true if the image was successfully resized and saved, false otherwise + return $destination; + } + + /** + * Resizes an image by keeping the aspect ratio + * + * @param string $source + * @param array $width + * @param integer $quality + * @param array $destination + * @param boolean $unique_filename + * @param boolean $fix_orientation + * @param string $gif_mode If the uploaded image is a GIF image, how will it be copied? Options: "copy" source, "resize" source + * + * @return boolean + */ + public static function resizeAndKeepAspectRatio($source, $width, $quality = 70, $destination = '', $unique_filename = false, $fix_orientation = true, $gif_mode = 'copy') + { + // Ensure we have received valid image dimensions + if (!count($image_dimensions = getimagesize($source))) + { + return false; + } + + // Get the image width + if (!$uploaded_image_width = (int) $image_dimensions[0]) + { + return false; + } + + // Get the image height + if (!$uploaded_image_height = (int) $image_dimensions[1]) + { + return false; + } + + $width = (int) $width; + + /** + * If the image width is less than the given width, + * set the image width we are resizing to the image's width. + */ + if ($uploaded_image_width < (int) $width) + { + $width = $uploaded_image_width; + } + + // Determine the MIME of the original file to get the proper type + $mime = Mimes::detectFileType($source); + + // PNG images should not have a quality value + $options = $mime == 'image/png' ? ['quality' => 9] : ['quality' => $quality]; + + // Get the image type + $image_type = self::getImageType($mime); + + try { + // Get image object + $image = new JoomlaImage($source); + + // Fix orientation + if ($fix_orientation) + { + self::fixOrientation($image); + } + + // Calculate aspect ratio + $ratio = $uploaded_image_width / $uploaded_image_height; + + // Get new height based on aspect ratio + $targetHeight = $width / $ratio; + + // Output file name + $destination = empty($destination) ? $source : $destination; + + if ($unique_filename) + { + // Make destination file unique + File::uniquefy($destination); + } + + $destination = Path::clean($destination); + + // Resize image + if ($mime === 'image/gif') + { + if ($gif_mode === 'copy') + { + File::copy($source, $destination, true); + } + else + { + $resizedImage = $image->resize($width, $targetHeight, true); + $resizedImage->toFile($destination, $image_type, $options); + } + } + else + { + $resizedImage = $image->resize($width, $targetHeight, true); + $resizedImage->toFile($destination, $image_type, $options); + } + + return $destination; + } catch(\Exception $e) {} + + return false; + } + + public static function resizeByWidthOrHeight($source, $width, $height, $quality = 80, $destination = '', $resize_method = 'crop', $unique_filename = false, $fix_orientation = true) + { + $resized_image = null; + + // If width is null, and we have height set, we are resizing by height + if (is_null($width) && $height && !is_null($height)) + { + $resized_image = Image::resizeByHeight($source, $height, $destination, 80, $unique_filename, $fix_orientation); + } + else + { + /** + * If height is zero, then we suppose we want to keep aspect ratio. + * + * Resize with width & height: If height is not set + * Resize and keep aspect ratio: If height is set + */ + $resized_image = $height && !is_null($height) + ? + Image::resize($source, $width, $height, 80, $resize_method, $destination, $unique_filename, $fix_orientation) + : + Image::resizeAndKeepAspectRatio($source, $width, 80, $destination, $unique_filename, $fix_orientation); + + } + + return $resized_image; + } + + /** + * Returns the orientation of the image. + * + * @param string $path + * + * @return int + */ + public static function getOrientation($path) + { + if (!$exif = @exif_read_data($path)) + { + return; + } + + return intval(@$exif['Orientation']); + } + + /** + * Fixes the orientation of the generated image and ensures it appears with the same orientation as the source. + * + * @param string $path + * @param int $orientation + * + * @return void + */ + public static function fixOrientation(&$image, $orientation = null) + { + $orientation = self::getOrientation($image->getPath()); + + if(!in_array($orientation, [3, 6, 8])) + { + return; + } + + switch ($orientation) + { + case 3: + $image->rotate(180, -1, false); + break; + + case 6: + $image->rotate(270, -1, false); + break; + + case 8: + $image->rotate(90, -1, false); + break; + } + + return true; + } + + /** + * Returns the image type based on its mime type + * + * @param string $mime + * + * @return int + */ + public static function getImageType($mime) + { + switch ($mime) + { + case 'image/png': + return IMAGETYPE_PNG; + break; + case 'image/gif': + return IMAGETYPE_GIF; + break; + case 'image/webp': + return IMAGETYPE_WEBP; + break; + case 'image/jpeg': + default: + return IMAGETYPE_JPEG; + break; + } + } + + /** + * Creates a watermark from text. + * + * @param string $text + * @param integer $font_size + * @param integer $opacity + * @param string $color + * @param integer $originalWidth + * @param integer $originalHeight + * + * @return object + */ + public static function createWatermarkText($text = '', $_font_size = 30, $opacity = 60, $color = '#ffffff', $originalWidth = null, $originalHeight = null) + { + $font = implode(DIRECTORY_SEPARATOR, [JPATH_SITE, 'media', 'plg_system_nrframework', 'font', 'arial.ttf']); + + // Scale down font size based on the original image width or height + $dimension = $originalWidth > $originalHeight ? $originalWidth : $originalHeight; + $font_size = $dimension ? $_font_size * ($dimension / 1000) * 1.2 : $_font_size; + + if ($font_size > $_font_size) + { + $font_size = $_font_size; + } + + if ($font_size < 14) + { + $font_size = 14; + } + + $TextSize = @ImageTTFBBox($font_size, 0, $font, $text) or die; + $TextWidth = abs($TextSize[2]) + abs($TextSize[0]); + $TextHeight = abs($TextSize[7]) + abs($TextSize[1]); + + $watermarkImage = imagecreatetruecolor($TextWidth, $TextHeight); + + imagealphablending($watermarkImage, false); + imagesavealpha($watermarkImage, true); + $bgText = imagecolorallocatealpha($watermarkImage, 255, 255, 255, 127); + imagefill($watermarkImage, 0, 0, $bgText); + $wmTransp = 127 - ($opacity * 1.27); + $rgb = self::hex2rgb($color, false); + $colorResource = imagecolorallocatealpha($watermarkImage, $rgb[0], $rgb[1], $rgb[2], $wmTransp); + + // Create watermark + imagettftext($watermarkImage, $font_size, 0, 0, abs($TextSize[5]), $colorResource, $font, $text); + + return $watermarkImage; + } + + /** + * Apply the watermark. + * + * @param array $opts + * + * @return void + */ + public static function applyWatermark($opts = []) + { + $defaults = [ + 'source' => null, + 'destination' => null, + 'preset' => 'custom', + 'type' => 'text', + 'text' => null, + 'position' => 'bottom-right', + 'angle' => 0, + 'opacity' => 50, + 'size' => 30, + 'color' => '#fff' + ]; + + $opts = array_merge($defaults, $opts); + + if (!$opts['source']) + { + return false; + } + + if (!is_file($opts['source'])) + { + return false; + } + + $destination = $opts['destination'] ? $opts['destination'] : $opts['source']; + + $originalImage = new JoomlaImage($opts['source']); + + // Get the dimensions of the original image + $originalWidth = $originalImage->getWidth(); + $originalHeight = $originalImage->getHeight(); + + $watermarkSource = null; + + switch ($opts['type']) + { + case 'image': + $watermarkSource = $opts['image']; + break; + + case 'text': + default: + if (!$watermarkText = self::getWatermarkText($opts['text_preset'], $opts['text'], $destination)) + { + return; + } + + $watermarkSource = self::createWatermarkText($watermarkText, (int) $opts['size'], 100, $opts['color'], $originalWidth, $originalHeight); + break; + } + + if (!$watermarkSource) + { + return; + } + + $original_image_mime = $originalImage->getImageFileProperties($opts['source'])->mime; + + // Create the final image + $finalImage = imagecreatetruecolor($originalWidth, $originalHeight); + + $watermarkOpacity = (int) $opts['opacity']; + + // Add a black background color if the image is PNG and watermark opacity is not 100, as imagecopymerge() doesn't work with transparent PNG images + if ($original_image_mime === 'image/png') + { + if ($watermarkOpacity === 100) + { + imagesavealpha($finalImage, true); + $trans_background = imagecolorallocatealpha($finalImage, 0, 0, 0, 127); + imagefill($finalImage, 0, 0, $trans_background); + } + else + { + $black = imagecolorallocate($finalImage, 0, 0, 0); + imagefill($finalImage, 0, 0, $black); + } + } + + // Copy original image to the final image + imagecopy($finalImage, $originalImage->getHandle(), 0, 0, 0, 0, $originalWidth, $originalHeight); + + // Get watermark image + $watermarkImage = new JoomlaImage($watermarkSource); + + // Rotate it + if ($opts['angle']) + { + $angle = $opts['angle'] ? 360 - (int) $opts['angle'] : 0; + $watermarkImage->rotate($angle, -1, false); + } + + // Get the dimensions of the watermark image + $watermarkWidth = $watermarkImage->getWidth(); + $watermarkHeight = $watermarkImage->getHeight(); + + if (!$watermarkWidth || !$watermarkHeight) + { + return false; + } + + // Final watermark width/height + $width = $watermarkWidth; + $height = $watermarkHeight; + + // Scale watermark image + if ($opts['type'] === 'image') + { + $scaleFactor = min($originalWidth / $watermarkWidth, $originalHeight / $watermarkHeight); + + // Calculate the new dimensions of the watermark image + $width = $watermarkWidth * $scaleFactor; + $height = $watermarkHeight * $scaleFactor; + + if ($width > $watermarkWidth) + { + $width = $watermarkWidth; + $height = $watermarkHeight; + } + } + + $width = (int) $width; + $height = (int) $height; + + list($dest_x, $dest_y) = self::getWatermarkPosition($opts['position'], $originalWidth, $originalHeight, $width, $height); + + // Resize watermark image before applying it into the final image + $watermarkImage = $watermarkImage->resize($width, $height); + + // Copy the watermark image into the final image + if ($watermarkOpacity === 100) + { + imagecopy($finalImage, $watermarkImage->getHandle(), round($dest_x), round($dest_y), 0, 0, $width, $height); + } + else + { + self::imagecopymerge_alpha($finalImage, $watermarkImage->getHandle(), round($dest_x), round($dest_y), 0, 0, $width, $height, $watermarkOpacity); + } + + // Save final image + switch ($original_image_mime) + { + case 'image/gif': + imagegif($finalImage, $destination); + break; + + case 'image/webp': + imagewebp($finalImage, $destination, 70); + break; + + case 'image/png': + imagepng($finalImage, $destination, 6); + break; + + default: + imagejpeg($finalImage, $destination, 70); + break; + } + + imagedestroy($finalImage); + $originalImage->destroy(); + $watermarkImage->destroy(); + } + + /** + * imagecopy but with alpha channel support. + * + * @param object $dst_im + * @param object $src_im + * @param integer $dst_x + * @param integer $dst_y + * @param integer $src_x + * @param integer $src_y + * @param integer $src_w + * + * @return void + */ + public static function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct) + { + $cut = imagecreatetruecolor($src_w, $src_h); + + // copying relevant section from background to the cut resource + imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h); + + // copying relevant section from watermark to the cut resource + imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h); + + // insert cut resource to destination image + imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct); + } + + /** + * Returns the watermark position. + * + * @param string $position + * @param integer $originalWidth + * @param integer $originalHeight + * @param integer $width + * @param integer $height + * + * @return array + */ + public static function getWatermarkPosition($position, $originalWidth, $originalHeight, $width, $height) + { + /** + * Position watermark based on given position. + * + * top-left + * top-center + * top-right + * center-left + * center-center + * center-right + * bottom-left + * bottom-center + * bottom-right + */ + $position = explode('-', $position); + + // Padding from corner + $yPOS = $xPOS = 10; + $dest_x = $dest_y = 0; + + if (isset($position[0])) + { + switch ($position[0]) + { + case 'top': + $dest_y = 0 + $yPOS; + break; + case 'center': + $dest_y = round($originalHeight / 2) - round($height / 2); + break; + case 'bottom': + $dest_y = $originalHeight - $height - $yPOS; + break; + } + } + + if (isset($position[1])) + { + switch ($position[1]) + { + case 'left': + $dest_x = 0 + $xPOS; + break; + case 'center': + $dest_x = round($originalWidth / 2) - round($width / 2); + break; + case 'right': + $dest_x = $originalWidth - $width - $xPOS; + break; + } + } + + return [$dest_x, $dest_y]; + } + + /** + * Returns the watermark text. + * + * @param string $preset + * @param string $text + * @param string $filename + * + * @return string + */ + public static function getWatermarkText($preset = '', $text = '', $filename = '') + { + switch ($preset) + { + case 'site_name': + $text = Factory::getApplication()->get('sitename'); + break; + + case 'site_url': + $text = Uri::root(); + break; + + case 'custom': + $st = new \NRFramework\SmartTags(); + + // Add file Smart Tags + $file_data = File::pathinfo($filename); + $source_basename = $file_data['basename']; + $file_data['filename'] = $file_data['filename']; + $file_data['basename'] = $source_basename; + + $st->add($file_data, 'file.'); + + + $text = $st->replace($text); + break; + } + + return $text; + } + + /** + * Converts hexidecimal color value to rgb values and returns as array/string + * + * @param string $hex + * @param bool $asString + * + * @return array|string + */ + public static function hex2rgb($hex, $asString = false) + { + // strip off any leading # + if (0 === strpos($hex, '#')) + { + $hex = substr($hex, 1); + } + else if (0 === strpos($hex, '&H')) + { + $hex = substr($hex, 2); + } + + // break into hex 3-tuple + $cutpoint = ceil(strlen($hex) / 2)-1; + $rgb = explode(':', wordwrap($hex, $cutpoint, ':', $cutpoint), 3); + + // convert each tuple to decimal + $rgb[0] = (isset($rgb[0]) ? hexdec($rgb[0]) : 0); + $rgb[1] = (isset($rgb[1]) ? hexdec($rgb[1]) : 0); + $rgb[2] = (isset($rgb[2]) ? hexdec($rgb[2]) : 0); + + return ($asString ? "{$rgb[0]} {$rgb[1]} {$rgb[2]}" : $rgb); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/ActiveCampaign.php b/plugins/system/nrframework/NRFramework/Integrations/ActiveCampaign.php new file mode 100644 index 00000000..334587ca --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/ActiveCampaign.php @@ -0,0 +1,457 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class ActiveCampaign extends Integration +{ + /** + * Create a new instance + * @param array $options The service's required options + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options); + $this->setEndpoint($options['endpoint'] . '/api/3'); + $this->options->set('headers.Api-Token', $this->key); + } + + /** + * Subscribe user to ActiveCampaign List + * + * https://developers.activecampaign.com/v3/reference#create-contact + * + * @param string $email The Email of the Contact + * @param string $name The name of the Contact (Name can be also declared in Custom Fields) + * @param string $list List ID + * @param string $tags Tags for this contact (comma-separated). Example: "tag1, tag2, etc" + * @param array $customfields Custom Fields + * @param boolean $updateexisting Update Existing User + * + * @return void + */ + public function subscribe($email, $name, $lists, $tags = '', $customfields = [], $updateexisting = true) + { + // Detect name + $name = (is_null($name) || empty($name)) ? $this->getNameFromCustomFields($customfields) : explode(' ', $name, 2); + $apiAction = ($updateexisting) ? 'contact/sync' : 'contacts'; + + $data = [ + 'contact' => [ + 'email' => $email, + 'phone' => $this->getPhone($customfields), + 'ip4' => \NRFramework\User::getIP() + ], + ]; + + // Add first and last name only if they are not empty, as ActiveCampaign will empty the fields if they are empty. + if (isset($name[0]) && $name[0]) + { + $data['contact']['firstName'] = $name[0]; + } + if (isset($name[1]) && $name[1]) + { + $data['contact']['lastName'] = $name[1]; + } + + $this->post($apiAction, $data); + + if (!$this->request_successful) + { + return; + } + + // Retrive the contact's ID + $contact_id = $this->getContactIDFromResponse(); + + // Add Lists to Contact + $this->addListsToContact($contact_id, $lists); + + // Add Tags to Contact + if (!empty($tags)) + { + $tags = is_array($tags) ? $tags : explode(',', $tags); + + $tag_ids = $this->convertTagNamesToIDs($tags); + + if ($tag_ids && !empty($tag_ids)) + { + $this->addTagsToContact($tag_ids, $contact_id); + } + } + + // Add Custom Fields to Contact + $this->addCustomFieldsToContact($customfields, $contact_id); + } + + /** + * Returns the phone number of the contact. + * + * @param array $customfields + * + * @return string + */ + private function getPhone($customfields) + { + $phone = $this->getCustomFieldValue('phone', $customfields); + + if (is_string($phone)) + { + return $phone; + } + + if (isset($phone['code']) && isset($phone['value']) && $phone['value']) + { + $calling_code = \NRFramework\Countries::getCallingCodeByCountryCode($phone['code']); + $calling_code = $calling_code !== '' ? '+' . $calling_code : ''; + + $phone = $calling_code . $phone['value']; + } + else + { + $phone = ''; + } + + return $phone; + } + + /** + * Update Custom Field Values for a Contact + * + * API Reference: https://developers.activecampaign.com/v3/reference#fieldvalues + * + * @param array $custom_fields Array of custom field values + * @param integer $contact_id The contact's ID + * + * @return mixed Null on failure, void on success + */ + private function addCustomFieldsToContact($custom_fields, $contact_id) + { + if (empty($custom_fields)) + { + return; + } + + $custom_fields = array_change_key_case($custom_fields); + + if (!$all_custom_fields = $this->getAllCustomFields()) + { + return; + } + + foreach ($custom_fields as $custom_field_key => $custom_field_value) + { + if (empty($custom_field_value)) + { + continue; + } + + $custom_field = strtolower(trim($custom_field_key)); + + if (!array_key_exists($custom_field, $all_custom_fields)) + { + continue; + } + + // Let's add Custom Field to our contact + $custom_field_data = $all_custom_fields[$custom_field]; + + // Radio buttons expect a string. Not an array. + if ($custom_field_data['type'] == 'checkbox' && is_array($custom_field_value)) + { + $custom_field_value = implode('||', $custom_field_value); + $custom_field_value = '||' . $custom_field_value . '||'; + } + + $this->post('fieldValues', [ + 'fieldValue' => [ + 'contact' => $contact_id, + 'field' => $custom_field_data['id'], + 'value' => $custom_field_value + ] + ]); + } + } + + /** + * Add tags to contact + * + * API Reference: https://developers.activecampaign.com/v3/reference#create-contact-tag + * + * @param array $tag_ids Array of tag IDs + * @param integer $contact_id The contact's ID + * + * @return void + */ + private function addTagsToContact($tag_ids, $contact_id) + { + foreach ($tag_ids as $tag_id) + { + $this->post('contactTags', [ + 'contactTag' => [ + 'contact' => $contact_id, + 'tag' => $tag_id, + ] + ]); + } + } + + /** + * Convert a list of tag names to tag IDs + * + * @param array $tags Array ot tag names + * + * @return mixed Null on failure, assosiative tag name-based array on success. + */ + private function convertTagNamesToIDs($tags) + { + if (!$account_tags = $this->getAllTags()) + { + return; + } + + $account_tags = array_map('strtolower', $account_tags); + + $tag_ids = []; + + foreach ($tags as $tag) + { + if (empty($tag)) + { + continue; + } + + $tag = strtolower(trim($tag)); + + if (!$tag_id = array_search($tag, $account_tags)) + { + continue; + } + + $tag_ids[] = $tag_id; + } + + return $tag_ids; + } + + /** + * Retrieve all contact-based tags + * + * API Reference: https://developers.activecampaign.com/v3/reference#list-all-tasks + * + * @return mixed Null on failure, assosiative array on success + */ + private function getAllTags() + { + $tags = $this->get('tags'); + + if (!$tags || !is_array($tags) || !isset($tags['tags'])) + { + return; + } + + $tags_ = []; + + foreach ($tags['tags'] as $tag) + { + if ($tag['tagType'] != 'contact') + { + continue; + } + + $tags_[$tag['id']] = $tag['tag']; + } + + return $tags_; + } + + /** + * Add lists to contact + * + * @param integer $contact_id The Active Campaign Contact ID + * @param mixed $lists The list ID to add the contact to. + * + * @return void + */ + private function addListsToContact($contact_id, $lists) + { + $lists = is_array($lists) ? $lists : explode(',', $lists); + + foreach ($lists as $list) + { + $this->post('contactLists', [ + 'contactList' => [ + 'list' => $list, + 'contact' => $contact_id, + 'status' => 1 + ] + ]); + } + } + + /** + * Determine the newly created contact's ID + * + * @return string + */ + private function getContactIDFromResponse() + { + $response = $this->last_response; + + if (isset($response->body) && isset($response->body['contact']) && isset($response->body['contact']['id'])) + { + return $response->body['contact']['id']; + } + } + + /** + * Search for First Name and Last Name in Custom Fields and return an array with both values. + * + * @param array $customfields The Custom Fields array passed by the user. + * + * @return array + */ + private function getNameFromCustomFields($customfields) + { + return [ + (string) $this->getCustomFieldValue(['first_name', 'First Name'], $customfields), + (string) $this->getCustomFieldValue(['last_name', 'Last Name'], $customfields) + ]; + } + + /** + * Retrieve all account lists + * + * API Reference: https://developers.activecampaign.com/v3/reference#retrieve-all-lists + * + * @return mixed Null on failure, Array on success + */ + public function getLists() + { + $data = $this->get('lists'); + + if (!$data || !isset($data['lists']) || count($data['lists']) == 0) + { + return; + } + + $lists = []; + + foreach ($data['lists'] as $list) + { + $lists[] = [ + 'id' => $list['id'], + 'name' => $list['name'] + ]; + } + + return $lists; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * API Reference: https://developers.activecampaign.com/v3/reference#errors + * + * @return string + */ + public function getLastError() + { + $error_code = $this->last_response->code; + $error_message = 'Active Campaign Error'; + + switch ((int) $error_code) + { + case 403: + $error_message = 'The request could not be authenticated or the authenticated user is not authorized to access the requested resource.'; + break; + case 404: + $error_message = 'The requested resource does not exist.'; + break; + case 422: + $error_message = 'The request could not be processed, usually due to a missing or invalid parameter.'; + + if (isset($this->last_response->body['errors']) && isset($this->last_response->body['errors'][0])) + { + $error_message = $this->last_response->body['errors'][0]['title']; + } + + break; + } + + return $error_message; + } + + /** + * Returns the Active Campaign Account's Custom Fields + * + * API Reference: https://developers.activecampaign.com/v3/reference#retrieve-fields-1 + * + * @return array + */ + public function getAllCustomFields() + { + $fields = $this->get('fields'); + + if (!$fields || !isset($fields['fields'])) + { + return; + } + + + // Make our life easier by creating a title-based assosiative array + $f = []; + + foreach ($fields['fields'] as $key => $field) + { + if (!$field || !isset($field['title'])) + { + continue; + } + + $key = strtolower(trim($field['title'])); + + $f[$key] = $field; + } + + return $f; + } + + /** + * Make an HTTP GET request for retrieving data. + * + * ActiveCampaign has a limit of max 100 results per page. + * https://developers.activecampaign.com/reference#pagination + * + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (usually your data) + * + * @return array|false Assoc array of API response, decoded from JSON + */ + public function get($method, $args = array()) + { + $args['limit'] = isset($args['limit']) ? $args['limit'] : 100; + $args['offset'] = isset($args['offset']) ? $args['offset'] : 0; + + $response = parent::get($method, $args); + + if ($args['offset'] < (int) $response['meta']['total']) + { + $args['offset'] += $args['limit']; + $response_next = $this->get($method, $args); + $response[$method] = array_merge($response[$method], $response_next[$method]); + } + + return $response; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Brevo.php b/plugins/system/nrframework/NRFramework/Integrations/Brevo.php new file mode 100644 index 00000000..8bb1c24b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Brevo.php @@ -0,0 +1,118 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class Brevo extends Integration +{ + /** + * Create a new instance + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options); + $this->setEndpoint('https://api.brevo.com/v3'); + $this->options->set('headers.api-key', $this->key); + } + + /** + * Subscribes a user to a Brevo Account + * + * API Reference v3: + * https://developers.brevo.com/reference/createcontact + * + * @param string $email The user's email + * @param array $params All the form fields + * @param string $listid The List ID + * @param boolean $update_existing Whether to update the existing contact (Only in v3) + * + * @return boolean + */ + public function subscribe($email, $params, $listid = false, $update_existing = true) + { + $data = [ + 'email' => $email, + 'attributes' => (object) $params, + 'updateEnabled' => $update_existing + ]; + + if ($listid) + { + $data['listIds'] = [(int) $listid]; + } + + $this->post('contacts', $data); + + return true; + } + + /** + * Returns all Campaign lists + * + * API Reference v3: + * https://developers.brevo.com/reference/getlists-1 + * + * @return array + */ + public function getLists() + { + $data = [ + 'offset' => 0, + 'limit' => 50 + ]; + + $lists = []; + + $data = $this->get('contacts/lists', $data); + + // sanity check + if (!isset($data['lists']) || !is_array($data['lists']) || $data['count'] == 0) + { + return $lists; + } + + foreach ($data['lists'] as $key => $list) + { + $lists[] = [ + 'id' => $list['id'], + 'name' => $list['name'] + ]; + } + + return $lists; + + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * API Reference: + * https://developers.brevo.com/docs/how-it-works#error-codes + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + $message = ''; + + if (!isset($body['code'])) + { + return $message; + } + + return $body['message']; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/CampaignMonitor.php b/plugins/system/nrframework/NRFramework/Integrations/CampaignMonitor.php new file mode 100644 index 00000000..3e8ef614 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/CampaignMonitor.php @@ -0,0 +1,205 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class CampaignMonitor extends Integration +{ + /** + * Create a new instance + * + * @param array $options The service's required options + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options); + $this->setEndpoint('https://api.createsend.com/api/v3.1'); + $this->options->set('userauth', $this->key); + $this->options->set('passwordauth', 'nopass'); + } + + /** + * Subscribe user to Campaign Monitor + * + * API References: + * https://www.campaignmonitor.com/api/subscribers/#importing_many_subscribers + * Reminder: + * The classic add_subscriber method of Campaign Monitor's API is NOT instantaneous! + * It is suggested to use their import method for instantaneous subscriptions! + * + * @param string $email User's email address + * @param string $name User's Name + * @param string $list The Campaign Monitor list unique ID + * @param array $custom_fields Custom Fields + * + * @return void + */ + public function subscribe($email, $name, $list, $customFields = array()) + { + $data = array( + 'Subscribers' => array( + array( + 'EmailAddress' => $email, + 'Name' => $name, + 'Resubscribe' => true, + ), + ), + ); + + if (is_array($customFields) && count($customFields)) + { + $data['Subscribers'][0]['CustomFields'] = $this->validateCustomFields($customFields, $list); + } + + $this->post('subscribers/' . $list . '/import.json', $data); + + return true; + } + + /** + * Returns a new array with valid only custom fields + * + * @param array $formCustomFields Array of custom fields + * + * @return array Array of valid only custom fields + */ + public function validateCustomFields($formCustomFields, $list) + { + $fields = array(); + + if (!is_array($formCustomFields)) + { + return $fields; + } + + $listCustomFields = $this->get('lists/' . $list . '/customfields.json'); + + if (!$this->request_successful) + { + return $fields; + } + + $formCustomFieldsKeys = array_keys($formCustomFields); + + foreach ($listCustomFields as $listCustomField) + { + $field_name = $listCustomField['FieldName']; + + if (!in_array($field_name, $formCustomFieldsKeys)) + { + continue; + } + + $value = $formCustomFields[$field_name]; + + // Always convert custom field value to array, to support multiple values in a custom field. + $value = is_array($value) ? $value : (array) $value; + + foreach ($value as $val) + { + $fields[] = array( + 'Key' => $field_name, + 'Value' => $val, + ); + } + } + + return $fields; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + $message = ''; + + if (isset($body['Message'])) + { + $message = $body['Message']; + } + + if (isset($body['ResultData']['FailureDetails'][0]['Message'])) + { + $message .= ' - ' . $body['ResultData']['FailureDetails'][0]['Message']; + } + + return $message; + } + + /** + * Returns all Client lists + * + * https://www.campaignmonitor.com/api/clients/#getting-subscriber-lists + * + * @return array + */ + public function getLists() + { + $clients = $this->getClients(); + + if (!is_array($clients)) + { + return; + } + + $lists = array(); + + foreach ($clients as $key => $client) + { + if (!isset($client['ClientID'])) + { + continue; + } + + $clientLists = $this->get('/clients/' . $client['ClientID'] . '/lists.json'); + + if (!is_array($clientLists)) + { + continue; + } + + foreach ($clientLists as $key => $clientList) + { + $lists[] = array( + 'id' => $clientList['ListID'], + 'name' => $clientList['Name'] + ); + } + } + + return $lists; + } + + /** + * Get Clients + * + * https://www.campaignmonitor.com/api/account/ + * + * @return mixed Array on success, Null on fail + */ + private function getClients() + { + $clients = $this->get('/clients.json'); + + if (!$this->success()) + { + return; + } + + return $clients; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/ConvertKit.php b/plugins/system/nrframework/NRFramework/Integrations/ConvertKit.php new file mode 100644 index 00000000..991bfb34 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/ConvertKit.php @@ -0,0 +1,161 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\String\StringHelper; + +class ConvertKit extends Integration +{ + /** + * Create a new instance + * + * @param string $api_key Your ConvertKit API Key + */ + public function __construct($api_key) + { + parent::__construct(); + + $this->setKey($api_key); + $this->setEndpoint('https://api.convertkit.com/v3'); + } + + /** + * Subscribe a user to a ConvertKit Form + * + * API Reference: + * http://help.convertkit.com/article/33-api-documentation-v3 + * + * @param string $email The subscriber's email + * @param string $formid The account owner's form id + * @param array $params The form's parameters + * + * @return boolean + */ + public function subscribe($email, $formid, $params) + { + $first_name = (isset($params['first_name'])) ? $params['first_name'] : ''; + $tags = (isset($params['tags'])) ? $this->convertTagnamesToTagIDs($params['tags']) : ''; + $fields = $this->validateCustomFields($params); + + $data = array( + 'api_key' => $this->key, + 'email' => $email, + 'first_name' => $first_name, + 'tags' => $tags, + 'fields' => $fields, + ); + + $this->post('forms/' . $formid . '/subscribe', $data); + + return true; + } + + /** + * Converts tag names to tag IDs for the subscribe method + * + * @param string $tagnames comma separated list of tagnames + * + * @return string comma separated list of tag IDs + */ + public function convertTagnamesToTagIDs($tagnames) + { + if (empty($tagnames)) + { + return; + } + + $tagArray = !is_array($tagnames) ? explode(',', $tagnames) : $tagnames; + $tagnames = array_map('trim', $tagArray); + $accountTags = $this->get('tags', array('api_key' => $this->key)); + + if (empty($accountTags) || !$this->request_successful) + { + return; + } + + $tagIDs = array(); + + foreach ($accountTags['tags'] as $tag) + { + foreach ($tagnames as $tagname) + { + if (StringHelper::strcasecmp($tag['name'], $tagname) == 0) + { + $tagIDs[] = $tag['id']; + break; + } + } + } + + return implode(',', $tagIDs); + } + + /** + * Returns a new array with valid only custom fields + * + * @param array $formCustomFields Array of custom fields + * + * @return array Array of valid only custom fields + */ + public function validateCustomFields($formCustomFields) + { + if (!is_array($formCustomFields)) + { + return; + } + + $customFields = $this->get('custom_fields', array('api_key' => $this->key)); + + if (!$this->request_successful) + { + return; + } + + $fields = array(); + + $formCustomFieldsKeys = array_keys($formCustomFields); + + foreach ($customFields['custom_fields'] as $customField) + { + if (in_array($customField['key'], $formCustomFieldsKeys)) + { + $fields[$customField['key']] = $formCustomFields[$customField['key']]; + } + } + + return $fields; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + $message = ''; + + if (isset($body['error']) && !empty($body['error'])) + { + $message = $body['error']; + } + + if (isset($body['message']) && !empty($body['message'])) + { + $message .= ' - ' . $body['message']; + } + + return $message; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Drip.php b/plugins/system/nrframework/NRFramework/Integrations/Drip.php new file mode 100644 index 00000000..bf186a34 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Drip.php @@ -0,0 +1,287 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class Drip extends Integration +{ + /** + * Create a new instance + * + * @param string $key Your Drip API key + * @param string $account_id Your Drip Account ID + */ + public function __construct($options) + { + parent::__construct(); + + if (!(isset($options['api']) && isset($options['account_id']))) { + return; + } + + $this->setKey($options['api']); + $this->setEndpoint('https://api.getdrip.com/v2/' . $options['account_id']); + $this->options->set('headers.Authorization', 'Basic ' . base64_encode($this->key . ':')); + } + + /** + * Subscribe user to Drip + * + * API References: + * https://developer.drip.com/#create-or-update-a-subscriber + * + * @param string $email User's email address + * @param string $campaign_id The Campaign ID + * @param string $name The name of the Contact (Name can be also declared in Custom Fields) + * @param Object $custom_fields Custom Fields + * @param mixed $tags Tags for this contact (comma-separated). Example: 'tag1, tag2, etc' + * @param boolean $update_existing Update existing user + * @param boolean $double_optin Send MailChimp confirmation email? + * + * @return void + */ + public function subscribe($email, $campaign_id, $name = null, $custom_fields = array(), $tags = '', $update_existing = true, $double_optin = false) + { + // Detect name + $name = (is_null($name) || empty($name)) ? $this->getNameFromCustomFields($custom_fields) : explode(' ', $name, 2); + + // We use this boolean to see if the user has subscribed the campaign + // This is used for the `update_existing` parameter + $subscriber_exists = $this->subscriberIsInCampaign($email, $campaign_id); + + // Check if we need to update the user + if ($update_existing == false && $subscriber_exists) + { + throw new \Exception(Text::_('NR_YOU_ARE_ALREADY_A_SUBSCRIBER'), 1); + } + + // Remove tags from custom fields + $custom_fields_parse = $custom_fields; + if (isset($custom_fields_parse['tags'])) + { + unset($custom_fields_parse['tags']); + } + + // Create or Update a Subscriber + $data = [ + 'subscribers' => [ + [ + 'email' => $email, + 'first_name' => isset($name[0]) ? $name[0] : '', + 'last_name' => isset($name[1]) ? $name[1] : '', + 'address1' => $this->getCustomFieldValue('address1', $custom_fields), + 'address2' => $this->getCustomFieldValue('address2', $custom_fields), + 'city' => $this->getCustomFieldValue('city', $custom_fields), + 'state' => $this->getCustomFieldValue('state', $custom_fields), + 'zip' => $this->getCustomFieldValue('zip', $custom_fields), + 'country' => $this->getCustomFieldValue('country', $custom_fields), + 'phone' => $this->getCustomFieldValue('phone', $custom_fields), + 'custom_fields' => $custom_fields_parse, + 'tags' => $this->getTags($tags) + ] + ] + ]; + + $this->post('subscribers', $data); + + // If we are updating a user, dont try re-assigning him to a campaign + // If we are updating a user but he just subscribed, then assign him to a campaign + if ($update_existing == false || $subscriber_exists == false) + { + // Assign the newly created subscriber to the campaign + $this->assignSubscriberToCampaign($email, $campaign_id, $double_optin); + } + + + return true; + } + + /** + * Assign a Subscriber to a Campaign + * + * https://developer.drip.com/?shell#subscribe-someone-to-a-campaign + * + * @return void + */ + private function assignSubscriberToCampaign($email, $campaign_id, $double_optin) + { + + // Subscribe user to a campaign + $campaignSubAPI = 'campaigns/' . $campaign_id . '/subscribers'; + + $data = [ + 'subscribers' => [ + [ + 'email' => $email, + 'double_optin' => (bool) $double_optin + ] + ] + ]; + + $this->post($campaignSubAPI, $data); + } + + /** + * Returns an array of tags or an empty string if no tags provided + * + * @return mixed + */ + private function getTags($tags) { + + if (empty($tags)) + { + return; + } + + if (is_string($tags)) + { + $tags = array_map('trim', explode(',', $tags)); + } + + return $tags; + } + + /** + * Returns whether the subscriber is in a campaign + * + * https://developer.drip.com/?shell#list-all-of-a-subscriber-39-s-campaign-subscriptions + * + * @return bool + */ + private function subscriberIsInCampaign($email, $campaign_id) + { + $found_campaign = false; + + $subscriber_id = $this->getSubscriberIdFromEmail($email); + + // Use does not exist in Drip + if (empty($subscriber_id)) + { + return false; + } + + $subscriber_campaigns = $this->getSubscriberCampaigns($subscriber_id); + + foreach ($subscriber_campaigns as $c) + { + if ($c['campaign_id'] == $campaign_id) + { + $found_campaign = true; + break; + } + } + + return $found_campaign; + + } + + /** + * Returns the ID of the subscriber from email + * + * https://developer.drip.com/?shell#fetch-a-subscriber + * + * @return string + */ + private function getSubscriberIdFromEmail($email) + { + $data = $this->get('subscribers/' . $email); + + return isset($data['subscribers']) ? $data['subscribers'][0]['id'] : ''; + } + + /** + * Returns all subscriber's campaigns + * + * https://developer.drip.com/?javascript#list-all-of-a-subscriber-39-s-campaign-subscriptions + * + * @return array + */ + private function getSubscriberCampaigns($subscriberId) + { + $data = $this->get('subscribers/' . $subscriberId . '/campaign_subscriptions'); + + return isset($data['campaign_subscriptions']) ? $data['campaign_subscriptions'] : array(); + } + + /** + * Returns all available Drip campaigns + * + * https://developer.drip.com/?shell#list-all-campaigns + * + * @return array + */ + public function getLists() + { + $data = $this->get('campaigns'); + + if (!$this->success()) + { + return; + } + + if (!isset($data['campaigns']) || !is_array($data['campaigns'])) + { + return; + } + + $campaigns = []; + + foreach ($data['campaigns'] as $key => $campaign) + { + $campaigns[] = array( + 'id' => $campaign['id'], + 'name' => $campaign['name'] + ); + } + + return $campaigns; + } + + /** + * Search for First Name and Last Name in Custom Fields and return an array with both values. + * + * @param array $custom_fields The Custom Fields array passed by the user. + * + * @return array + */ + private function getNameFromCustomFields($custom_fields) + { + return [ + (string) $this->getCustomFieldValue(['first_name', 'First Name'], $custom_fields), + (string) $this->getCustomFieldValue(['last_name', 'Last Name'], $custom_fields) + ]; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + $messages = ''; + + if (isset($body['errors'])) + { + foreach ($body['errors'] as $error) + { + $messages .= ' - ' . $error['message']; + } + } + + return $messages; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/ElasticEmail.php b/plugins/system/nrframework/NRFramework/Integrations/ElasticEmail.php new file mode 100644 index 00000000..5684237e --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/ElasticEmail.php @@ -0,0 +1,184 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class ElasticEmail extends Integration +{ + protected $endpoint = 'https://api.elasticemail.com/v2'; + + /** + * Create a new instance + * + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options); + } + + /** + * Subscribe user to ElasticEmail + * + * API References: + * http://api.elasticemail.com/public/help#Contact_Add + * http://api.elasticemail.com/public/help#Contact_Update + * + * @param string $email User's email address + * @param string $list The ElasticEmail List unique ID + * @param string $publicAccountID The ElasticEmail PublicAccountID + * @param array $params The form's parameters + * @param boolean $update_existing Update existing user + * @param boolean $double_optin Send ElasticEmail confirmation email? + * + * @return void + */ + public function subscribe($email, $list, $publicAccountID, $params = array(), $update_existing = true, $double_optin = false) + { + $data = array( + 'apikey' => $this->key, + 'email' => $email, + 'publicAccountID' => $publicAccountID, + 'publicListID' => $list, + 'sendActivation' => $double_optin ? 'true' : 'false', + 'consentIP' => \NRFramework\User::getIP() + ); + + if (is_array($params) && count($params)) + { + foreach ($params as $param_key => $param_value) + { + $data[$param_key] = (is_array($param_value)) ? implode(',', $param_value) : $param_value; + } + } + + if (!$update_existing) + { + return $this->get('/contact/add', $data); + } + + if ($this->getContact($email)) + { + $data['clearRestOfFields'] = 'false'; + $this->get('/contact/update', $data); + } + else + { + $this->get('/contact/add', $data); + } + + return true; + } + + /** + * Returns all available ElasticEmail lists + * + * http://api.elasticemail.com/public/help#List_list + * + * @return array + */ + public function getLists() + { + $data = $this->get('/list/list', array('apikey' => $this->key)); + + if (!$this->success()) + { + return; + } + + $lists = array(); + + if (!isset($data['data']) || !is_array($data['data'])) + { + return $lists; + } + + foreach ($data['data'] as $key => $list) + { + $lists[] = array( + 'id' => $list['publiclistid'], + 'name' => $list['listname'] + ); + } + + return $lists; + } + + /** + * Check to see if a contact exists + * + * @param string $email The contact's email + * + * @return boolean + */ + public function getContact($email) + { + $contact = $this->get('/contact/loadcontact', array('apikey' => $this->key, 'email' => $email)); + + return (bool) $contact['success']; + } + + /** + * Get the Elastic Email Public Account ID + * + * @return string + */ + public function getPublicAccountID() + { + $data = $this->get('/account/load', array('apikey' => $this->key)); + + if (isset($data['data']['publicaccountid'])) + { + return $data['data']['publicaccountid']; + } + + throw new \Exception(Text::_('NR_ELASTICEMAIL_UNRETRIEVABLE_PUBLICACCOUNTID'), 1); + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + if (isset($body['error'])) + { + return $body['error']; + } + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + protected function determineSuccess() + { + $code = $this->last_response->code; + $body = $this->last_response->body; + + if ($code >= 200 && $code <= 299 && !isset($body['error'])) + { + return ($this->request_successful = true); + } + + $this->last_error = 'Unknown error, call getLastResponse() to find out what happened.'; + return false; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/GetResponse.php b/plugins/system/nrframework/NRFramework/Integrations/GetResponse.php new file mode 100644 index 00000000..9eec61d4 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/GetResponse.php @@ -0,0 +1,297 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class GetResponse extends Integration +{ + /** + * Create a new instance + * + * @param array $options The service's required options + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options); + $this->endpoint = 'https://api.getresponse.com/v3'; + $this->options->set('headers.X-Auth-Token', 'api-key ' . $this->key); + $this->options->set('headers.Accept-Encoding', 'gzip,deflate'); + } + + /** + * Subscribe user to GetResponse Campaign + * + * https://apidocs.getresponse.com/v3/resources/contacts#contacts.create + * + * TODO: Update existing contact + * + * @param string $email Email of the Contact + * @param string $name The name of the Contact + * @param int $dayOfCycle Enter 0 to add to the start day of the cycle. + * @param object $campaign Campaign ID + * @param object $customFields Collection of custom fields + * @param object $update_existing Update existing contact + * @param array $tags Set user tags + * @param string $tags_replace Determines what changes to make to the subscriber's tags. Values: add_only, replace_all + * + * @return void + */ + public function subscribe($email, $name, $campaign, $customFields, $update_existing, $dayOfCycle = 0, $tags = [], $tags_replace = 'add_only') + { + $data = [ + 'email' => $email, + 'name' => $name, + 'dayOfCycle' => $dayOfCycle, + 'campaign' => ['campaignId' => $campaign], + 'customFieldValues' => $this->validateCustomFields($customFields), + 'ipAddress' => \NRFramework\User::getIP() + ]; + + if (empty($name) || is_null($name)) + { + unset($data['name']); + } + + $contactId = null; + $service_tags = []; + + if ($tags) + { + $service_tags = $this->getServiceTags(); + } + + // Replace all existing contact tags with new ones + if ($tags && $tags_replace === 'replace_all') + { + $data['tags'] = $this->validateTags($tags, $service_tags, $tags_replace); + } + + if ($update_existing) + { + $contactId = $this->getContact($email); + } + + $endpoint = 'contacts'; + $endpoint = !empty($contactId) ? $endpoint . '/' . $contactId : $endpoint; + + $this->post($endpoint, $data); + + // Add new tags to the contact + if ($tags && $tags_replace === 'add_only' && $contactId) + { + $data = ['tags' => $this->validateTags($tags, $service_tags, $tags_replace)]; + + $this->post('contacts/' . $contactId . '/tags', $data); + } + } + + /** + * Return all service tags. + * + * @return array + */ + private function getServiceTags() + { + $tags = []; + + foreach ($this->get('tags') as $tag) + { + $tags[$tag['tagId']] = $tag['name']; + } + + return $tags; + } + + /** + * Validates and returns the valid tags. + * + * @param array $tags + * @param array $service_tags + * + * @return array + */ + private function validateTags($tags = [], $service_tags = [], $tags_replace = 'add_only') + { + $final_tags = []; + + foreach ($tags as $index => $tag) + { + $valid = false; + + // Find tag in service tags and add it to final tags list + foreach ($service_tags as $tagId => $tagName) + { + if ($tagId === $tag || $tagName === $tag) + { + $valid = true; + + // Add to final list + $final_tags[] = [ + 'tagId' => $tagId + ]; + } + } + + // Add invalid tags + if (!$valid && $tags_replace == 'add_only') + { + $new_tag = $this->createTag($tag); + $final_tags[] = [ + 'tagId' => $new_tag['tagId'] + ]; + } + } + + return $final_tags; + } + + private function createTag($tag) + { + $data = [ + 'name' => $tag + ]; + + return $this->post('tags', $data); + } + + /** + * Returns a new array with valid only custom fields + * + * @param array $customFields Array of custom fields + * + * @return array Array of valid only custom fields + */ + public function validateCustomFields($customFields) + { + $fields = []; + + if (!is_array($customFields)) + { + return $fields; + } + + $accountCustomFields = $this->get('custom-fields'); + + if (!$this->request_successful) + { + return $fields; + } + + foreach ($accountCustomFields as $key => $customField) + { + if (!isset($customFields[$customField['name']])) + { + continue; + } + + $fields[] = [ + 'customFieldId' => $customField['customFieldId'], + 'value' => [$customFields[$customField['name']]] + ]; + } + + return $fields; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * If something didn't work, this should contain the string describing the problem. + * + * @return string describing the error + */ + public function getLastError() + { + $body = $this->last_response->body; + + if (!isset($body['context']) || !isset($body['context'][0])) + { + return $body['codeDescription'] . ' - ' . $body['message']; + } + + $error = $body['context'][0]; + + // GetResponse returns a JSON string as $error and we try to decode it so we can return a more human-friendly error message + $error = is_string($error) && json_encode($error, true) ? json_decode($error, true) : $error; + + if (is_array($error) && isset($error['fieldName'])) + { + $errorFieldName = is_array($error['fieldName']) ? implode(' ', $error['fieldName']) : $error['fieldName']; + return $errorFieldName . ': ' . $error['message']; + } + + return (is_array($error)) ? implode(' ', $error) : $error; + + } + + /** + * Returns all available GetResponse campaigns + * + * https://apidocs.getresponse.com/v3/resources/campaigns#campaigns.get.all + * + * @return array + */ + public function getLists() + { + $data = $this->get('campaigns'); + + if (!$this->success()) + { + return; + } + + if (!is_array($data) || !count($data)) + { + return; + } + + $lists = []; + + foreach ($data as $key => $list) + { + $lists[] = [ + 'id' => $list['campaignId'], + 'name' => $list['name'] + ]; + } + + return $lists; + } + + /** + * Get the Contact resource + * + * @param string $email The email of the contact which we want to retrieve + * + * @return string The Contact ID + */ + public function getContact($email) + { + if (!isset($email)) + { + return; + } + + $data = $this->get('contacts', ['query[email]' => $email]); + + if (empty($data)) + { + return; + } + + // the returned data is an array with only one contact + $contactId = $data[0]['contactId']; + + return ($contactId) ? $contactId : null; + + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/HCaptcha.php b/plugins/system/nrframework/NRFramework/Integrations/HCaptcha.php new file mode 100644 index 00000000..75cb2390 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/HCaptcha.php @@ -0,0 +1,109 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +/** + * The HCaptcha Wrapper + */ +class HCaptcha extends Integration +{ + /** + * Service Endpoint + * + * @var string + */ + protected $endpoint = 'https://hcaptcha.com/siteverify'; + + /** + * Create a new instance + * + * @param array $options + * + * @throws \Exception + */ + public function __construct($options = []) + { + parent::__construct(); + + if (!array_key_exists('secret', $options)) + { + $this->setError('NR_RECAPTCHA_INVALID_SECRET_KEY'); + throw new \Exception($this->getLastError()); + } + + $this->setKey($options['secret']); + } + + /** + * Calls the hCaptcha siteverify API to verify whether the user passes hCaptcha test. + * + * @param string $response Response string from hCaptcha verification. + * @param string $remoteip IP address of end user + * + * @return bool Returns true if the user passes hCaptcha test + */ + public function validate($response, $remoteip = null) + { + if (empty($response) || is_null($response)) + { + return $this->setError('NR_RECAPTCHA_PLEASE_VALIDATE'); + } + + // remove these headers in order for hCaptcha to be abl to process the request + $this->options->remove('headers.Accept'); + $this->options->remove('headers.Content-Type'); + + // do not encode request + $this->setEncode(false); + + $data = [ + 'secret' => $this->key, + 'response' => $response, + ]; + + $this->post('', $data); + + return true; + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + protected function determineSuccess() + { + $success = parent::determineSuccess(); + $body = $this->last_response->body; + + if ($body['success'] == false && array_key_exists('error-codes', $body) && count($body['error-codes']) > 0) + { + $success = $this->setError(implode(', ', $body['error-codes'])); + } + + return ($this->request_successful = $success); + } + + /** + * Set wrapper error text + * + * @param String $error The error message to display + */ + private function setError($error) + { + $this->last_error = Text::_('NR_HCAPTCHA') . ': ' . Text::_($error); + return false; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/HubSpot.php b/plugins/system/nrframework/NRFramework/Integrations/HubSpot.php new file mode 100644 index 00000000..1e558e32 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/HubSpot.php @@ -0,0 +1,140 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class HubSpot extends Integration +{ + /** + * Create a new instance + * + * @param string $key Your HubSpot API key + */ + public function __construct($options) + { + parent::__construct(); + + $this->setKey(is_array($options) ? $options['api'] : $options); + $this->setEndpoint('https://api.hubapi.com'); + } + + /** + * Subscribe user to HubSpot + * + * API References: + * http://developers.hubspot.com/docs/methods/contacts/update_contact-by-email + * + * @param string $email User's email address + * @param string $params The forms extra fields + * + * @return void + */ + public function subscribe($email, $params) + { + $fields = $this->validateCustomFields($params); + + $fields[] = array('property' => 'email', 'value' => $email); + + $data = array( + 'properties' => $fields + ); + + $this->post('contacts/v1/contact/createOrUpdate/email/' . $email . '/?hapikey=' . $this->key, $data); + + return true; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * API References: + * http://developers.hubspot.com/docs/faq/api-error-responses + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + $message = ''; + + if ((isset($body['status'])) && ($body['status'] == 'error')) + { + $message = $body['message']; + } + + if (isset($body['validationResults']) && is_array($body['validationResults']) && count($body['validationResults'])) + { + foreach ($body['validationResults'] as $key => $validation) + { + if ($validation['isValid'] === false) + { + $message .= ' - ' . $validation['message']; + } + } + } + + return $message; + } + + /** + * Returns a new array with valid only custom fields + * + * API References: + * http://developers.hubspot.com/docs/methods/contacts/v2/get_contacts_properties + * + * @param array $formCustomFields Array of custom fields + * + * @return array Array of valid only custom fields + */ + public function validateCustomFields($formCustomFields) + { + + $fields = array(); + + if (!is_array($formCustomFields)) + { + return $fields; + } + + $accountFields = $this->get('properties/v1/contacts/properties?hapikey='.$this->key); + + if (!$this->request_successful) + { + return $fields; + } + + $accountFieldsNames = array_map( + function ($ar) + { + return $ar['name']; + }, $accountFields + ); + + $formCustomFieldsKeys = array_keys($formCustomFields); + + foreach ($accountFieldsNames as $accountFieldsName) + { + if (!in_array($accountFieldsName, $formCustomFieldsKeys)) + { + continue; + } + + $fields[] = array( + "property" => $accountFieldsName, + "value" => $formCustomFields[$accountFieldsName], + ); + } + + return $fields; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/HubSpot3.php b/plugins/system/nrframework/NRFramework/Integrations/HubSpot3.php new file mode 100644 index 00000000..7c27ecf8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/HubSpot3.php @@ -0,0 +1,220 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class HubSpot3 extends Integration +{ + /** + * Create a new instance + * + * @param string $key Your HubSpot API key + */ + public function __construct($options) + { + parent::__construct(); + + $this->setKey($options); + + $this->setEndpoint('https://api.hubapi.com/crm/v3'); + + $this->options->set('headers.Authorization', 'Bearer ' . $this->key); + } + + /** + * Create/Update a HubSpot Contact + * + * API References: + * https://developers.hubspot.com/docs/api/crm/contacts + * + * @param string $email User's email address + * @param string $params The forms extra fields + * @param bool $update_existing Set whether to update an existing user + * + * @return void + */ + public function subscribe($email, $params, $update_existing = true) + { + $contact_data = $this->contactExists($email); + + if (!$update_existing) + { + if ($contact_data) + { + throw new \Exception('Contact already exists.'); + } + } + + $default_property = ['email' => $email]; + + $other_properties = $this->validateCustomFields($params); + + $data = [ + 'properties' => array_merge($default_property, $other_properties) + ]; + + $method = 'post'; + $endpoint = 'objects/contacts'; + + if ($update_existing && $contact_data) + { + $method = 'patch'; + $endpoint .= '/' . $contact_data['id']; + }; + + $this->$method($endpoint, $data); + + // If a list exists, add the contact to that list. + if ($this->success() && isset($params['list']) && !empty($params['list'])) + { + $this->addContactToStaticList($email, $params['list']); + } + } + + /** + * Returns all lists. + * + * @return array + */ + public function getLists() + { + $this->endpoint = $this->getV1Endpoint(); + + $data = $this->get('lists/static'); + + if (!$this->success()) + { + return; + } + + if (!is_array($data) || !count($data) || !isset($data['lists'])) + { + return; + } + + $lists = []; + + foreach ($data['lists'] as $key => $list) + { + $lists[] = [ + 'id' => $list['listId'], + 'name' => $list['name'] + ]; + } + + return $lists; + } + + /** + * Add contact to a static list. + * + * @param string $email + * @param int $list_id + * + * @return void + */ + public function addContactToStaticList($email, $list_id) + { + $this->endpoint = $this->getV1Endpoint(); + + $data = (object) [ 'emails' => [ $email ] ]; + + $this->post('lists/' . $list_id . '/add', $data); + } + + /** + * Return the v1 endpoint. + * + * @return string + */ + private function getV1Endpoint() + { + return 'https://api.hubapi.com/contacts/v1'; + } + + /** + * Check whether contact already exists. + * + * @param string $email + * + * @return bool + */ + public function contactExists($email) + { + $contact = $this->get('objects/contacts/' . $email . '?idProperty=email'); + + return $this->success() ? $contact : false; + } + + /** + * Returns a new array with valid only custom fields + * + * API References: + * https://developers.hubspot.com/docs/api/crm/properties + * + * @param array $formCustomFields Array of custom fields + * + * @return array Array of valid only custom fields + */ + public function validateCustomFields($formCustomFields) + { + $fields = []; + + if (!is_array($formCustomFields)) + { + return $fields; + } + + $contactCustomFields = $this->get('properties/Contact'); + + if (!$this->request_successful) + { + return $fields; + } + + $customFieldNames = array_map( + function ($ar) + { + return $ar['name']; + }, $contactCustomFields['results'] + ); + + $formCustomFieldsKeys = array_keys($formCustomFields); + + foreach ($customFieldNames as $accountFieldName) + { + if (!in_array($accountFieldName, $formCustomFieldsKeys)) + { + continue; + } + + $fields[$accountFieldName] = $formCustomFields[$accountFieldName]; + } + + return $fields; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + if (isset($body['status']) && $body['status'] === 'error') + { + return $body['message']; + } + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/IContact.php b/plugins/system/nrframework/NRFramework/Integrations/IContact.php new file mode 100644 index 00000000..abaf7ce5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/IContact.php @@ -0,0 +1,209 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class IContact extends Integration +{ + public $accountID; + + public $clientFolderID; + + /** + * Create a new instance + * @param array $options The service's required options + */ + public function __construct($options) + { + parent::__construct(); + $this->endpoint = 'https://app.icontact.com/icp/a'; + $this->options->set('headers.API-Version', '2.2'); + $this->options->set('headers.API-AppId', $options['appID']); + $this->options->set('headers.API-Username', $options['username']); + $this->options->set('headers.API-Password', $options['appPassword']); + $this->setAccountID($options['accountID']); + $this->setClientFolderID($options['clientFolderID']); + } + + /** + * Finds and sets the iContact AccountID + * + * @param mixed $accountID + */ + public function setAccountID($accountID = false) + { + if ($accountID) + { + $this->accountID = $accountID; + } + + $accounts = $this->get(''); + + if (!$this->success()) + { + throw new \Exception($this->getLastError()); + } + + // Make sure the account is active + if (intval($accounts['accounts'][0]['enabled']) === 1) + { + $this->accountID = (integer) $accounts['accounts'][0]['accountId']; + } + else + { + throw new \Exception(Text::_('NR_ICONTACT_ACCOUNTID_ERROR'), 1); + } + } + + /** + * Finds and sets the iContact ClientFolderID + * + * @param mixed $clientFolderID + */ + public function setClientFolderID($clientFolderID = false) + { + if ($clientFolderID) + { + $this->clientFolderID = $clientFolderID; + } + + // We need an existant accountID + if (empty($this->accountID)) + { + try + { + $this->setAccountID(); + } + catch (Exception $e) + { + throw $e; + } + } + + if ($clientFolder = $this->get($this->accountID . '/c/')) + { + $this->clientFolderID = $clientFolder['clientfolders'][0]['clientFolderId']; + } + } + + /** + * Subscribes a user to an iContact List + * + * API REFERENCE + * https://www.icontact.com/developerportal/documentation/contacts + * + * @param string $email + * @param object $params The extra form fields + * @param mixed $list The iContact List ID + * + * @return boolean + */ + public function subscribe($email, $params, $list) + { + $data = array('contact' => array_merge(array('email' => $email, 'status' => 'normal'), (array) $params)); + + try + { + $contact = $this->post($this->accountID .'/c/' . $this->clientFolderID . '/contacts', $data); + } + catch (Exception $e) + { + throw $e; + } + + if ((isset($contact['contacts'])) && (is_array($contact['contacts'])) && (count($contact['contacts']) > 0)) + { + $this->addToList($list, $contact['contacts'][0]['contactId']); + } + + return true; + } + + /** + * Adds a contact to an iContact List + * + * API REFERENCE + * https://www.icontact.com/developerportal/documentation/subscriptions + * + * @param string $listID + * @param string $contactID + */ + public function addToList($listID, $contactID) + { + $data = array( + array( + 'contactId' => $contactID, + 'listId' => $listID, + 'status' => 'normal' + ) + ); + $this->post($this->accountID .'/c/' . $this->clientFolderID . '/subscriptions',$data); + } + + /** + * Returns all Client lists + * + * API REFERENCE + * https://www.icontact.com/developerportal/documentation/lists + * + * @return array + */ + public function getLists() + { + $data = $this->get($this->accountID .'/c/' . $this->clientFolderID . '/lists'); + + if (!$this->success()) + { + return; + } + + $lists = array(); + + if (!isset($data["lists"]) || !is_array($data["lists"])) + { + return $lists; + } + + foreach ($data["lists"] as $key => $list) + { + $lists[] = array( + 'id' => $list['listId'], + 'name' => $list['name'] + ); + } + + return $lists; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * If something didn't work, this should contain the string describing the problem. + * + * @return string describing the error + */ + public function getLastError() + { + $body = $this->last_response->body; + $message = ''; + + if (isset($body['errors'])) + { + foreach ($body['errors'] as $error) { + $message .= $error . ' '; + } + } + + return trim($message); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Integration.php b/plugins/system/nrframework/NRFramework/Integrations/Integration.php new file mode 100644 index 00000000..faa61141 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Integration.php @@ -0,0 +1,323 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\Registry\Registry; +use Joomla\CMS\Http\HttpFactory; + +class Integration +{ + protected $key; + protected $endpoint; + protected $request_successful = false; + protected $last_error = ''; + protected $last_response = []; + protected $last_request = []; + protected $timeout = 60; + protected $options; + protected $encode = true; + protected $response_type = 'json'; + + public function __construct() + { + $this->options = new Registry; + $this->options->set('timeout', $this->timeout); + $this->options->set('headers.Accept', 'application/json'); + $this->options->set('headers.Content-Type', 'application/json'); + } + + /** + * Setter method for the API Key or Access Token + * + * @param string $apiKey + */ + public function setKey($apiKey) + { + $apiKey = is_array($apiKey) && isset($apiKey['api']) ? $apiKey['api'] : $apiKey; + + if (!is_string($apiKey) || empty($apiKey) || is_null($apiKey)) + { + throw new \Exception('Invalid API Key supplied.'); + } + + $this->key = trim($apiKey); + } + + /** + * Setter method for the endpoint + * @param string $url The URL which is set in the account's developer settings + * @throws \Exception + */ + public function setEndpoint($url) + { + if (!empty($url)) + { + $this->endpoint = $url; + } + else + { + throw new \Exception("Invalid Endpoint URL `{$url}` supplied."); + } + } + + /** + * Was the last request successful? + * @return bool True for success, false for failure + */ + public function success() + { + return $this->request_successful; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * If something didn't work, this should contain the string describing the problem. + * @return array|false describing the error + */ + public function getLastError() + { + return $this->last_error ?: false; + } + + /** + * Get an array containing the HTTP headers and the body of the API response. + * @return array Assoc array with keys 'headers' and 'body' + */ + public function getLastResponse() + { + return $this->last_response; + } + + /** + * Get an array containing the HTTP headers and the body of the API request. + * @return array Assoc array + */ + public function getLastRequest() + { + return $this->last_request; + } + + /** + * Make an HTTP DELETE request - for deleting data + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (if any) + * @return array|false Assoc array of API response, decoded from JSON + */ + public function delete($method, $args = []) + { + return $this->makeRequest('delete', $method, $args); + } + + /** + * Make an HTTP GET request - for retrieving data + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (usually your data) + * @return array|false Assoc array of API response, decoded from JSON + */ + public function get($method, $args = []) + { + return $this->makeRequest('get', $method, $args); + } + + /** + * Make an HTTP PATCH request - for performing partial updates + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (usually your data) + * @return array|false Assoc array of API response, decoded from JSON + */ + public function patch($method, $args = []) + { + return $this->makeRequest('patch', $method, $args); + } + + /** + * Make an HTTP POST request - for creating and updating items + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (usually your data) + * @return array|false Assoc array of API response, decoded from JSON + */ + public function post($method, $args = []) + { + return $this->makeRequest('post', $method, $args); + } + + /** + * Make an HTTP PUT request - for creating new items + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (usually your data) + * @return array|false Assoc array of API response, decoded from JSON + */ + public function put($method, $args = []) + { + return $this->makeRequest('put', $method, $args); + } + + /** + * Performs the underlying HTTP request. Not very exciting. + * @param string $http_verb The HTTP verb to use: get, post, put, patch, delete + * @param string $method The API method to be called + * @param array $args Assoc array of parameters to be passed + * @return array|false Assoc array of decoded result + * @throws \Exception + */ + protected function makeRequest($http_verb, $method, $args = []) + { + $url = $this->endpoint; + + if (!empty($method) && !is_null($method) && strpos($url, '?') === false) + { + $url .= '/' . $method; + } + + $this->last_error = ''; + $this->request_successful = false; + $this->last_response = []; + $this->last_request = [ + 'method' => $http_verb, + 'path' => $method, + 'url' => $url, + 'body' => '', + 'timeout' => $this->timeout, + ]; + + $http = HttpFactory::getHttp($this->options); + + switch ($http_verb) + { + case 'post': + $this->attachRequestPayload($args); + $response = $http->post($url, $this->last_request['body']); + break; + + case 'get': + $query = http_build_query($args, '', '&'); + $this->last_request['body'] = $query; + $response = (strpos($url,'?') !== false) ? $http->get($url . '&' . $query) : $http->get($url . '?' . $query); + break; + + case 'delete': + $response = $http->delete($url); + break; + + case 'patch': + $this->attachRequestPayload($args); + $response = $http->patch($url, $this->last_request['body']); + break; + + case 'put': + $this->attachRequestPayload($args); + $response = $http->put($url, $this->last_request['body']); + break; + } + + // Do not touch directly the $response object to prevent the PHP 8.2 "Creation of dynamic property" deprecation notice. + $this->last_response = (object) [ + 'body' => $this->convertResponse($response->body), + 'headers' => $response->headers, + 'code' => $response->code + ]; + + $this->determineSuccess(); + + return $this->last_response->body; + } + + /** + * Encode the data and attach it to the request + * @param array $data Assoc array of data to attach + */ + protected function attachRequestPayload($data) + { + if (!$this->encode) + { + $this->last_request['body'] = http_build_query($data); + return; + } + + $this->last_request['body'] = json_encode($data); + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + protected function determineSuccess() + { + $status = $this->last_response->code; + $success = ($status >= 200 && $status <= 299) ? true : false; + + return ($this->request_successful = $success); + } + + /** + * Converts the HTTP Call response to a traversable type + * + * @param json|xml $response + * + * @return array|object + */ + protected function convertResponse($response) + { + switch ($this->response_type) + { + case 'json': + return json_decode($response, true); + case 'xml': + return new \SimpleXMLElement($response); + case 'text': + return $response; + } + } + + /** + * Search Custom Fields declared by the user for a specific custom field. If exists return its value. + * + * @param array $needles The custom field names + * @param array $haystack The custom fields array + * + * @return string The value of the custom field or an empty string if not found + */ + protected function getCustomFieldValue($needles, $haystack) + { + $needles = is_array($needles) ? $needles : (array) $needles; + $haystack = array_change_key_case($haystack); + + $found = ''; + + foreach ($needles as $needle) + { + $needle = strtolower($needle); + + if (array_key_exists($needle, $haystack)) + { + $found = is_string($haystack[$needle]) ? trim($haystack[$needle]) : $haystack[$needle]; + break; + } + } + + return $found; + } + + /** + * Set encode + * + * @param boolean $encode + * + * @return void + */ + public function setEncode($encode) + { + $this->encode = $encode; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/MailChimp.php b/plugins/system/nrframework/NRFramework/Integrations/MailChimp.php new file mode 100644 index 00000000..d2362561 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/MailChimp.php @@ -0,0 +1,511 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +use NRFramework\Functions; + +// No direct access +defined('_JEXEC') or die; + +class MailChimp extends Integration +{ + /** + * MailChimp Endpoint URL + * + * @var string + */ + protected $endpoint = 'https://.api.mailchimp.com/3.0'; + + /** + * Create a new instance + * + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + + $this->setKey($options); + + if (strpos($this->key, '-') === false) + { + return; + } + + list(, $data_center) = explode('-', $this->key); + $this->endpoint = str_replace('', $data_center, $this->endpoint); + + $this->options->set('headers.Authorization', 'apikey ' . $this->key); + } + + /** + * Subscribe user to MailChimp + * + * @param string $list_id The ID of the MailChimp list + * @param string $email The email address of the subscriber + * @param object $merge_fields The custom field that are associated with the subscriber where the keys are the merge tags. + * @param boolean $double_optin If true, the subscriber will be added with status "pending" and a confirmation email will be sent to the user. + * @param boolean $allow_update If true, the subscriber will be updated if it already exists. Otherwise, an error will be thrown. + * @param array $tags The tags that are associated with the subscriber. + * @param string $tags_replace Determines what changes to make to the subscriber's tags. Values: add_only, replace_all + * @param array $interests The interests that are associated with the subscriber. + * @param string $interests_replace Determines what changes to make to the subscriber's groups/interests. Values: add_only, replace_all + * + * @return void + */ + public function subscribeV2($list_id, $email, $merge_fields = null, $double_optin = true, $allow_update = true, $tags = null, $tags_replace = 'add_only', $interests = [], $interests_replace = 'add_only') + { + $data = [ + 'email_address' => $email, + 'status' => $double_optin ? 'pending' : 'subscribed', + 'merge_fields' => (object) $merge_fields, + 'tags' => Functions::cleanArray($tags) + ]; + + $member = $this->getMemberByEmail($list_id, $email); + + // Prepare Interests + $interests = Functions::cleanArray($interests); + $interests = $interests ? array_fill_keys($interests, true) : []; + + if ($member && isset($member['interests']) && $interests_replace == 'replace_all') + { + // Disable all existing groups + $memberInterests = array_fill_keys(array_keys($member['interests']), false); + + // Merge new interests with existing interests + $interests = array_merge($memberInterests, $interests); + } + + $data['interests'] = (object) $interests; + + if (!$member) + { + // API Doc: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members + $this->post('lists/' . $list_id . '/members', $data); + return; + } + + // Member exists + // Since member exists and we don't allow updating existing member, throw an error. + if (!$allow_update) + { + throw new \Exception('Member already exists'); + } + + // Skip double opt-in if the existing member is already confirmed + if (isset($member['status']) && $member['status'] == 'subscribed') + { + $data['status'] = $member['status']; + } + + // Update existing member + // API Doc: https://mailchimp.com/developer/marketing/api/list-members/add-or-update-list-member + $this->put('lists/' . $list_id . '/members/' . $member['id'], $data); + + // Remove existing member tags not included in the given Tags. + if ($member['tags'] && $tags_replace == 'replace_all') + { + $currentTags = array_map(function($item) { return $item['name']; }, $member['tags']); + + if ($removeTags = array_diff($currentTags, $data['tags'])) + { + $rTags = []; + + foreach ($removeTags as $removeTag) + { + $rTags[] = [ + 'name' => $removeTag, + 'status' => 'inactive' + ]; + } + + // API Doc: https://mailchimp.com/developer/marketing/api/list-member-tags/add-or-remove-member-tags/ + $this->post('lists/' . $list_id . '/members/' . $member['id'] . '/tags', ['tags' => $rTags]); + } + } + } + + /** + * Subscribe user to MailChimp + * + * API References: + * https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-put_lists_list_id_members_subscriber_hash + * https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members + * + * @param string $email User's email address + * @param string $list The MailChimp list unique ID + * @param Object $merge_fields Merge Fields + * @param boolean $update_existing Update existing user + * @param boolean $double_optin Send MailChimp confirmation email? + * + * @deprecated Use subscribeV2() + * + * @return void + */ + public function subscribe($email, $list, $merge_fields = array(), $update_existing = true, $double_optin = false) + { + $data = array( + 'email_address' => $email, + 'status' => $double_optin ? 'pending' : 'subscribed' + ); + + // add support for tags + if ($tags = $this->getTags($merge_fields)) + { + $data['tags'] = $tags; + } + + if (is_array($merge_fields) && count($merge_fields)) + { + foreach ($merge_fields as $merge_field_key => $merge_field_value) + { + $value = is_array($merge_field_value) ? implode(',', $merge_field_value) : (string) $merge_field_value; + $data['merge_fields'][$merge_field_key] = $value; + } + } + + $interests = $this->validateInterestCategories($list, $merge_fields); + + if (!empty($interests)) + { + $data = array_merge($data, array('interests' => $interests)); + } + + if ($update_existing) + { + // Get subscriber information. + $subscriberHash = md5(strtolower($email)); + $member = $this->get('lists/' . $list . '/members/' . $subscriberHash); + + // Skip double opt-in if the subscriber exists and it's confirmed + if (isset($member['status']) && $member['status'] == 'subscribed') + { + $data['status'] = $member['status']; + } + + $this->put('lists/' . $list . '/members/' . $subscriberHash, $data); + + if ($tags) + { + $tags_ = []; + + foreach ($tags as $tag) + { + $tags_[] = [ + 'name' => $tag, + 'status' => 'active' + ]; + } + + $currentTags = $this->getMemberTags($list, $subscriberHash); + + if ($removeTags = array_diff($currentTags, $tags)) + { + foreach ($removeTags as $removeTag) + { + $tags_[] = [ + 'name' => $removeTag, + 'status' => 'inactive' + ]; + } + } + + $this->post('lists/' . $list . '/members/' . $subscriberHash . '/tags', ['tags' => $tags_]); + } + + } else + { + $this->post('lists/' . $list . '/members', $data); + } + + return true; + } + + /** + * @deprecated Use subscribeV2() + */ + private function getMemberTags($list, $subscriberHash) + { + $tags = $this->get('lists/' . $list . '/members/' . $subscriberHash . '/tags'); + + $return = []; + + if (isset($tags['tags'])) + { + foreach ($tags['tags'] as $tag) + { + $return[] = $tag['name']; + } + } + + return $return; + } + + /** + * Find and return all unique tags + * + * @param array $merge_fields + * + * @deprecated use subscribeV2() + * + * @return array + */ + private function getTags($merge_fields) + { + $tags = []; + + // ensure tags are added in the form + if (!isset($merge_fields['tags'])) + { + return $tags; + } + + $mergeFieldsTags = $merge_fields['tags']; + + // make string array + if (is_string($mergeFieldsTags)) + { + $tags = explode(',', $mergeFieldsTags); + } + + // ensure we have array to manipulate + if (is_array($mergeFieldsTags) || is_object($mergeFieldsTags)) + { + $tags = (array) $mergeFieldsTags; + } + + // remove empty values, keep uniques and reset keys + $tags = array_filter($tags); + $tags = array_unique($tags); + $tags = array_values($tags); + $tags = array_map('trim', $tags); + + return $tags; + } + + /** + * Returns all available MailChimp lists + * + * https://developer.mailchimp.com/documentation/mailchimp/reference/lists/#read-get_lists + * + * @return array + */ + public function getLists() + { + $data = $this->get('/lists'); + + if (!$this->success()) + { + return; + } + + if (!isset($data['lists']) || !is_array($data['lists'])) + { + return; + } + + $lists = []; + + foreach ($data['lists'] as $key => $list) + { + $lists[] = array( + 'id' => $list['id'], + 'name' => $list['name'] + ); + } + + return $lists; + } + + /** + * Gets the Interest Categories from MailChimp + * + * @param string $listID The List ID + * + * @deprecated Use subscribeV2() + * + * @return array + */ + public function getInterestCategories($listID) + { + if (!$listID) + { + return; + } + + $data = $this->get('/lists/' . $listID . '/interest-categories'); + + if (!$this->success()) + { + return; + } + + if (isset($data['total_items']) && $data['total_items'] == 0) + { + return; + } + + return $data['categories']; + } + + /** + * Gets the values accepted for the particular Interest Category + * + * @param string $listID The List ID + * @param string $interestCategoryID The Interest Category ID + * + * @deprecated Use subscribeV2() + * + * @return array + */ + public function getInterestCategoryValues($listID, $interestCategoryID) + { + if (!$interestCategoryID || !$listID) + { + return array(); + } + + $data = $this->get('/lists/' . $listID . '/interest-categories/' . $interestCategoryID . '/interests'); + + if (isset($data['total_items']) && $data['total_items'] == 0) + { + return array(); + } + + return $data['interests']; + } + + /** + * Filters the interests categories through the form fields + * and constructs the interests array for the subscribe method + * + * @param string $listID The List ID + * @param array $params The Form fields + * + * @deprecated Use subscribeV2() + * + * @return array + */ + public function validateInterestCategories($listID, $params) + { + if (!$params || !$listID) + { + return array(); + } + + $interestCategories = $this->getInterestCategories($listID); + + if (!$interestCategories) + { + return array(); + } + + $categories = array(); + + foreach ($interestCategories as $category) + { + if (array_key_exists($category['title'], $params)) + { + $categories[] = array('id' => $category['id'], 'title' => $category['title']); + } + } + + if (empty($categories)) + { + return array(); + } + + $interests = array(); + + foreach ($categories as $category) + { + $data = $this->getInterestCategoryValues($listID, $category['id']); + + if (isset($data['total_items']) && $data['total_items'] == 0) + { + continue; + } + + foreach ($data as $interest) + { + if (in_array($interest['name'], (array) $params[$category['title']])) + { + $interests[$interest['id']] = true; + } + else + { + $interests[$interest['id']] = false; + } + } + } + + return $interests; + } + + /** + * Find a subscriber in a list by email address + * + * @param string $list_id The MailChimp list ID + * @param string $email The email address + * + * @return mixed Object on success, false on failure + */ + public function getMemberByEmail($list_id, $email) + { + $subscriberHash = md5(strtolower($email)); + $result = $this->get('lists/' . $list_id . '/members/' . $subscriberHash); + + return $this->success() ? $result : false; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + if (isset($body['errors'])) + { + $error = $body['errors'][0]; + return $error['field'] . ': ' . $error['message']; + } + + if (isset($body['detail'])) + { + return $body['detail']; + } + } + + /** + * The get() method overridden so that it handles + * the default item paging of MailChimp which is 10 + * + * @param string $method URL of the API request method + * @param array $args Assoc array of arguments (usually your data) + * @return array|false Assoc array of API response, decoded from JSON + */ + public function get($method, $args = array()) + { + $data = $this->makeRequest('get', $method, $args); + + if ($data && isset($data['total_items']) && (int) $data['total_items'] > 10) + { + $args['count'] = $data['total_items']; + return $this->makeRequest('get', $method, $args); + } + + return $data; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/MailerLite.php b/plugins/system/nrframework/NRFramework/Integrations/MailerLite.php new file mode 100644 index 00000000..67ee826b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/MailerLite.php @@ -0,0 +1,144 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class MailerLite extends Integration +{ + /** + * Create a new instance + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options); + $this->setEndpoint('https://connect.mailerlite.com/api'); + $this->options->set('headers.Authorization', 'Bearer ' . $this->key); + } + + /** + * Subscribes a user to a MailerLite Account + * + * API Reference v3: + * https://developers.mailerlite.com/docs/subscribers.html#create-upsert-subscriber + * + * @param string $email The user's email + * @param array $fields All the custom fields + * @param string $groupIds The Group IDs + * @param string $subscriber_status The subscriber status (active, unsubscribed, unconfirmed, bounced, junk) + * @param boolean $update_existing Whether to update the existing contact (Only in v3) + * + * @return boolean + */ + public function subscribe($email, $fields, $groupIds = [], $subscriber_status = '', $update_existing = true) + { + // Abort if we don't want to update existing subscribers and the subscriber already exists + if (!$update_existing && $this->subscriberExists($email)) + { + throw new \Exception(Text::_('NR_YOU_ARE_ALREADY_A_SUBSCRIBER'), 1); + } + + $data = [ + 'email' => $email, + 'fields' => $fields + ]; + + if ($subscriber_status) + { + $data['status'] = $subscriber_status; + } + + if ($groupIds) + { + $data['groups'] = $groupIds; + } + + $this->post('subscribers', $data); + + return true; + } + + /** + * Check if a subscriber exists + * + * @param string $email The subscriber's email + * + * @return boolean + */ + private function subscriberExists($email = '') + { + if (!$email) + { + return false; + } + + $response = $this->get('subscribers/' . $email); + + return isset($response['data']['email']) && $response['data']['email'] == $email; + } + + /** + * Returns all groups + * + * API Reference v3: + * https://developers.mailerlite.com/docs/groups.html#list-all-groups + * + * @return array + */ + public function getGroups() + { + $data = [ + 'offset' => 0, + 'limit' => 50 + ]; + + $lists = []; + + $data = $this->get('groups', $data); + + // sanity check + if (!isset($data['data']) || !is_array($data['data'])) + { + return $lists; + } + + foreach ($data['data'] as $key => $list) + { + $lists[] = [ + 'id' => $list['id'], + 'name' => $list['name'] + ]; + } + + return $lists; + + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * API Reference: + * https://developers.mailerlite.com/docs/#validation-errors + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + return isset($body['message']) ? $body['message'] : 'An error has occurred.'; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Notion.php b/plugins/system/nrframework/NRFramework/Integrations/Notion.php new file mode 100644 index 00000000..bef13906 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Notion.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +defined('_JEXEC') or die; + +class Notion extends Integration +{ + protected $endpoint = 'https://api.notion.com/v1'; + + /** + * Create a new instance + * + * @param array $options The service's required options + */ + public function __construct($options) + { + parent::__construct(); + + $this->setKey($options); + + $this->options->set('headers.Authorization', 'Bearer ' . $this->key); + $this->options->set('headers.Notion-Version', '2022-06-28'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/ReCaptcha.php b/plugins/system/nrframework/NRFramework/Integrations/ReCaptcha.php new file mode 100644 index 00000000..61a2f8bd --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/ReCaptcha.php @@ -0,0 +1,103 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +/** + * The reCAPTCHA Wrapper + */ +class ReCaptcha extends Integration +{ + /** + * Service Endpoint + * + * @var string + */ + protected $endpoint = 'https://www.google.com/recaptcha/api/siteverify'; + + /** + * Create a new instance + * + * @param array $options + * + * @throws \Exception + */ + public function __construct($options = array()) + { + parent::__construct(); + + if (!array_key_exists('secret', $options)) + { + $this->setError('NR_RECAPTCHA_INVALID_SECRET_KEY'); + throw new \Exception($this->getLastError()); + } + + $this->setKey($options['secret']); + } + + /** + * Calls the reCAPTCHA siteverify API to verify whether the user passes reCAPTCHA test. + * + * @param string $response Response string from recaptcha verification. + * @param string $remoteip IP address of end user + * + * @return bool Returns true if the user passes reCAPTCHA test + */ + public function validate($response, $remoteip = null) + { + if (empty($response) || is_null($response)) + { + return $this->setError('NR_RECAPTCHA_PLEASE_VALIDATE'); + } + + $data = array( + 'secret' => $this->key, + 'response' => $response, + 'remoteip' => $remoteip ?: \NRFramework\User::getIP(), + ); + + $this->get('', $data); + + return true; + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + protected function determineSuccess() + { + $success = parent::determineSuccess(); + $body = $this->last_response->body; + + if ($body['success'] == false && array_key_exists('error-codes', $body) && count($body['error-codes']) > 0) + { + $success = $this->setError(implode(', ', $body['error-codes'])); + } + + return ($this->request_successful = $success); + } + + /** + * Set wrapper error text + * + * @param String $error The error message to display + */ + private function setError($error) + { + $this->last_error = Text::_('NR_RECAPTCHA') . ': ' . Text::_($error); + return false; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Salesforce.php b/plugins/system/nrframework/NRFramework/Integrations/Salesforce.php new file mode 100644 index 00000000..9b3a49ee --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Salesforce.php @@ -0,0 +1,114 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class SalesForce extends Integration +{ + /** + * Service API Endpoint + * + * @var string + */ + protected $endpoint = 'https://{{ENV}}.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8'; + + /** + * Encode data before sending the request + * + * @var boolean + */ + protected $encode = false; + + /** + * Create a new instance + * + * @param array $options + * + * @throws \Exception + */ + public function __construct($options = []) + { + parent::__construct(); + + $this->setKey($options); + $this->prepareEndpoint($options); + + $this->options->set('headers.Content-Type', 'application/x-www-form-urlencoded'); + } + + private function prepareEndpoint($options = []) + { + if (isset($options['test_mode']) && $options['test_mode']) + { + $this->setEndpoint(str_replace('{{ENV}}', 'test', $this->endpoint)); + } + else + { + $this->setEndpoint(str_replace('{{ENV}}', 'webto', $this->endpoint)); + } + } + + /** + * Subscribe user to SalesForce + * + * API References: + * https://developer.salesforce.com/page/Wordpress-to-lead + * + * @param string $email User's email address + * @param array $params All the form fields + * + * @return void + */ + public function subscribe($email, $params) + { + $data = array( + "email" => $email, + "oid" => $this->key + ); + + if (is_array($params) && count($params)) + { + $data = array_merge($data, $params); + } + + $this->post('', $data); + + return true; + } + + /** + * Determine if the Lead has been stored successfully in SalesForce + * + * @return string + */ + public function determineSuccess() + { + $status = $this->last_response->code; + + if ($status < 200 && $status > 299) + { + return false; + } + + $headers = $this->last_response->headers; + + if (isset($headers['Is-Processed']) && (strpos($headers['Is-Processed'], 'Exception') !== false)) + { + $this->last_error = Text::_('NR_SALESFORCE_ERROR'); + return false; + } + + return ($this->request_successful = true); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/SendInBlue.php b/plugins/system/nrframework/NRFramework/Integrations/SendInBlue.php new file mode 100644 index 00000000..94e9757c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/SendInBlue.php @@ -0,0 +1,114 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class SendInBlue extends Integration +{ + /** + * Create a new instance + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options['api']); + $this->setEndpoint('https://api.sendinblue.com/v2.0'); + $this->options->set('headers.api-key', $this->key); + } + + /** + * Subscribes a user to a SendinBlue Account + * + * API Reference: + * https://apidocs.sendinblue.com/user/#1 + * + * @param string $email The user's email + * @param array $params All the form fields + * @param string $listid The List ID + * + * @return boolean + */ + public function subscribe($email, $params, $listid = false) + { + $data = array( + 'email' => $email, + 'attributes' => $params, + ); + + if ($listid) + { + $data['listid'] = array($listid); + } + + $this->post('user/createdituser', $data); + + return true; + } + + /** + * Returns all Campaign lists + * + * https://apidocs.sendinblue.com/list/#1 + * + * @return array + */ + public function getLists() + { + $data = array( + 'page' => 1, + 'page_limit' => 50 + ); + + $lists = array(); + + $data = $this->get('/list', $data); + + if (!isset($data['data']['lists']) || !is_array($data['data']['lists']) || $data['data']['total_list_records'] == 0) + { + return $lists; + } + + foreach ($data['data']['lists'] as $key => $list) + { + $lists[] = array( + 'id' => $list['id'], + 'name' => $list['name'] + ); + } + + return $lists; + + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * API Reference: + * https://apidocs.sendinblue.com/response/ + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + $message = ''; + + if (isset($body['code']) && ($body['code'] == 'failure')) + { + $message = $body['message']; + } + + return $message; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/SendInBlue3.php b/plugins/system/nrframework/NRFramework/Integrations/SendInBlue3.php new file mode 100644 index 00000000..e36f6ba8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/SendInBlue3.php @@ -0,0 +1,118 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class SendInBlue3 extends Integration +{ + /** + * Create a new instance + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options['api']); + $this->setEndpoint('https://api.sendinblue.com/v3'); + $this->options->set('headers.api-key', $this->key); + } + + /** + * Subscribes a user to a SendinBlue Account + * + * API Reference v3: + * https://developers.sendinblue.com/reference#createcontact + * + * @param string $email The user's email + * @param array $params All the form fields + * @param string $listid The List ID + * @param boolean $update_existing Whether to update the existing contact (Only in v3) + * + * @return boolean + */ + public function subscribe($email, $params, $listid = false, $update_existing = true) + { + $data = [ + 'email' => $email, + 'attributes' => (object) $params, + 'updateEnabled' => $update_existing + ]; + + if ($listid) + { + $data['listIds'] = [(int) $listid]; + } + + $this->post('contacts', $data); + + return true; + } + + /** + * Returns all Campaign lists + * + * API Reference v3: + * https://developers.sendinblue.com/reference#getlists-1 + * + * @return array + */ + public function getLists() + { + $data = [ + 'page' => 1, + 'page_limit' => 50 + ]; + + $lists = []; + + $data = $this->get('contacts/lists', $data); + + // sanity check + if (!isset($data['lists']) || !is_array($data['lists']) || $data['count'] == 0) + { + return $lists; + } + + foreach ($data['lists'] as $key => $list) + { + $lists[] = [ + 'id' => $list['id'], + 'name' => $list['name'] + ]; + } + + return $lists; + + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * API Reference: + * https://developers.sendinblue.com/docs/how-it-works#error-codes + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + $message = ''; + + if (!isset($body['code'])) + { + return $message; + } + + return $body['message']; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Turnstile.php b/plugins/system/nrframework/NRFramework/Integrations/Turnstile.php new file mode 100644 index 00000000..5ad09509 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Turnstile.php @@ -0,0 +1,99 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +class Turnstile extends Integration +{ + /** + * Service Endpoint + * + * @var string + */ + protected $endpoint = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; + + /** + * Create a new instance + * + * @param array $options + * + * @throws \Exception + */ + public function __construct($options = []) + { + parent::__construct(); + + if (!array_key_exists('secret', $options)) + { + $this->setError('NR_RECAPTCHA_INVALID_SECRET_KEY'); + throw new \Exception($this->getLastError()); + } + + $this->setKey($options['secret']); + } + + /** + * Calls the Cloudflare Turnstile siteverify API to verify whether the user passes the test. + * + * @param string $response Response string from Cloudflare Turnstile verification. + * @param string $remoteip IP address of end user + * + * @return bool Returns true if the user passes the test + */ + public function validate($response, $remoteip = null) + { + if (empty($response) || is_null($response)) + { + return $this->setError('NR_RECAPTCHA_PLEASE_VALIDATE'); + } + + $data = [ + 'secret' => $this->key, + 'response' => $response, + ]; + + $this->post('', $data); + + return true; + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + protected function determineSuccess() + { + $success = parent::determineSuccess(); + $body = $this->last_response->body; + + if ($body['success'] == false && array_key_exists('error-codes', $body) && count($body['error-codes']) > 0) + { + $success = $this->setError(implode(', ', $body['error-codes'])); + } + + return ($this->request_successful = $success); + } + + /** + * Set wrapper error text + * + * @param String $error The error message to display + */ + private function setError($error) + { + $this->last_error = Text::_('NR_TURNSTILE') . ': ' . Text::_($error); + return false; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/Zoho.php b/plugins/system/nrframework/NRFramework/Integrations/Zoho.php new file mode 100644 index 00000000..1fe18507 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/Zoho.php @@ -0,0 +1,169 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class Zoho extends Integration +{ + /** + * Create a new instance + * + * @param array $options The service's required options + * @throws \Exception + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options['api']); + $this->endpoint = 'https://campaigns.zoho.com/api'; + } + + /** + * Subscribe user to ZoHo + * + * https://www.zoho.com/campaigns/help/api/contact-subscribe.html + * + * @param string $email User's email address + * @param string $list The ZoHo list unique ID + * @param Object $customFields Collection of custom fields + * + * @return void + */ + public function subscribe($email, $list, $customFields = array()) + { + + $contactinfo = json_encode(array_merge(array("Contact Email" => $email), $customFields)); + + $data = array( + "authtoken" => $this->key, + "scope" => "CampaignsAPI", + "version" => "1", + "resfmt" => "JSON", + "listkey" => $list, + "contactinfo" => $contactinfo + ); + + $this->get('json/listsubscribe', $data); + + return true; + } + + /** + * Returns all available ZoHo lists + * + * https://www.zoho.com/campaigns/help/api/get-mailing-lists.html + * + * @return array + */ + public function getLists() + { + if (!$this->key) + { + return; + } + + $data = array( + 'authtoken' => $this->key, + 'scope' => 'CampaignsAPI', + 'sort' => 'asc', + 'resfmt' => 'JSON', + 'range' => '1000' //ambiguously large range of total results to overwrite the default range which is 20 + ); + + $data = $this->get("getmailinglists", $data); + + if (!$this->success()) + { + return; + } + + $lists = array(); + + if (!isset($data["list_of_details"]) || !is_array($data["list_of_details"])) + { + return $lists; + } + + foreach ($data["list_of_details"] as $key => $list) + { + $lists[] = array( + "id" => $list["listkey"], + "name" => $list["listname"] + ); + } + + return $lists; + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + if (isset($body['message'])) + { + return $body['message']; + } + + return 'An unspecified error occured'; + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + protected function determineSuccess() + { + $status = $this->findHTTPStatus(); + + // check if the status is equal to the arbitrary success codes of ZoHo + if (in_array($status, array(0, 200, 6101, 6201))) + { + return ($this->request_successful = true); + } + + return false; + } + + /** + * Find the HTTP status code from the headers or API response body + * + * @return int HTTP status code + */ + protected function findHTTPStatus() + { + $status = $this->last_response->code; + $success = ($status >= 200 && $status <= 299) ? true : false; + + if (!$success) + { + return 418; + } + + // ZoHo sometimes uses "Code" instead of "code" + // also they don't use HTTP status codes + // instead they store their own status code inside the response body + $data = array_change_key_case($this->last_response->body); + + if (isset($data['code'])) + { + return (int) $data['code']; + } + + return 418; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Integrations/ZohoCRM.php b/plugins/system/nrframework/NRFramework/Integrations/ZohoCRM.php new file mode 100644 index 00000000..a5d55d9c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Integrations/ZohoCRM.php @@ -0,0 +1,156 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Integrations; + +// No direct access +defined('_JEXEC') or die; + +class ZohoCRM extends Integration +{ + /** + * Response Type + * + * @var string + */ + protected $response_type = 'xml'; + + /** + * Data Center API Endpoint + * + * @var string + */ + private $datacenter = 'crm.zoho.com'; + + /** + * Create a new instance + * + * @param array $options The service's required options + */ + public function __construct($options) + { + parent::__construct(); + $this->setKey($options['authenticationToken']); + + if (isset($options['datacenter']) && !is_null($options['datacenter']) && !empty($options['datacenter'])) + { + $this->datacenter = $options['datacenter']; + } + } + + /** + * Subscribe user to ZohoCRM + * + * https://www.zoho.eu/crm/help/api/insertrecords.html#Insert_records_into_Zoho_CRM_from_third-party_applications + * + * @param string $email User's email address + * @param array $fields Available form fields + * @param string $module Zoho module to be used + * @param boolean $update_existing Update existing users + * @param string $workflow Trigger the workflow rule while inserting record + * @param string $approve Approve records (Supports: Leads, Contacts, and Cases modules) + * + * @return void + */ + public function subscribe($email, $fields, $module = 'leads', $update_existing = true, $workflow = false, $approve = false) + { + $data = array( + 'authtoken' => $this->key, + 'scope' => 'crmapi', + 'xmlData' => $this->buildModuleXML($email, $fields, $module), + 'duplicateCheck' => $update_existing ? '2' : '1', + 'wfTrigger' => $workflow ? 'true' : 'false', + 'isApproval' => $approve ? 'true' : 'false', + 'version' => '4' + ); + + $this->endpoint = 'https://' . $this->datacenter . '/crm/private/xml/' . ucfirst($module) . '/insertRecords?' . http_build_query($data); + + $this->post(''); + } + + /** + * Build the XML for each module + * + * @param string $email User's email address + * @param array $fields Form fields + * @param string $module Module to be used + * + * @return string The XML + */ + private function buildModuleXML($email, $fields, $module) + { + $xml = new SimpleXMLElement('<' . ucfirst($module) . '/>'); + $row = $xml->addChild('row'); + $row->addAttribute('no', '1'); + + $xmlField = $row->addChild('FL', $email); + $xmlField->addAttribute('val', 'Email'); + + if (is_array($fields) && count($fields)) + { + foreach ($fields as $field_key => $field_value) + { + $field_value = is_array($field_value) ? implode(',', $field_value) : $field_value; + + $xmlField = $row->addChild('FL', $field_value); + $xmlField->addAttribute('val', $field_key); + } + } + + return $xml->asXML(); + } + + /** + * Get the last error returned by either the network transport, or by the API. + * + * @return string + */ + public function getLastError() + { + $body = $this->last_response->body; + + if (isset($body->error)) + { + return $body->error->message; + } + + if (isset($body->result->row->error)) + { + return $body->result->row->error->details; + } + + return 'Unknown error'; + } + + /** + * Check if the response was successful or a failure. If it failed, store the error. + * + * @return bool If the request was successful + */ + public function determineSuccess() + { + $status = $this->last_response->code; + $success = ($status >= 200 && $status <= 299) ? true : false; + + if (!$success) + { + return false; + } + + $body = $this->last_response->body; + + if (!isset($body->result->row->success)) + { + return false; + } + + return ($this->request_successful = true); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Library/Favorites.php b/plugins/system/nrframework/NRFramework/Library/Favorites.php new file mode 100644 index 00000000..ca88d8a9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Library/Favorites.php @@ -0,0 +1,138 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Library; + +defined('_JEXEC') or die; + +class Favorites +{ + /** + * Library + * + * @var Library + */ + protected $library = []; + + public function __construct($library = []) + { + $this->library = $library; + } + + /** + * Handles AJAX Library favorite toggle + * + * @return string + */ + public function tf_library_ajax_favorites_toggle() + { + $template_id = $this->library->getLibrarySetting('template_id'); + + if (empty($template_id)) + { + return false; + } + + $this->addOrRemoveFavorite($template_id); + + return $this->getFavorites(); + } + + /** + * Add or remove favorites + * + * @param int $template_id + * + * @return void + */ + private function addOrRemoveFavorite($template_id) + { + $favorites = $this->getFavorites(); + + if (array_key_exists($template_id, $favorites)) + { + $this->removeFromFavorites($template_id); + return; + } + + $this->addToFavorites($template_id); + } + + /** + * Add to favorites + * + * @param int $template_id + * + * @return void + */ + private function addToFavorites($template_id) + { + $favorites = $this->getFavorites(); + + if (array_key_exists($template_id, $favorites)) + { + return; + } + + $favorites[$template_id] = true; + + $this->saveFavorites($favorites); + } + + /** + * Save favorites to file + * + * @param string $content + * + * @return void + */ + private function saveFavorites($content) + { + // Create directory if not exist + if (!is_dir($this->library->getTemplatesPath())) + { + \NRFramework\File::createDirs($this->library->getTemplatesPath()); + } + + $file = $this->library->getTemplatesPath() . 'favorites.json'; + + return file_put_contents($file, json_encode($content)); + } + + /** + * Remove from favorites + * + * @param int $template_id + * + * @return void + */ + private function removeFromFavorites($template_id) + { + $favorites = $this->getFavorites(); + unset($favorites[$template_id]); + $this->saveFavorites($favorites); + } + + /** + * Get favorites + * + * @return array + */ + public function getFavorites() + { + $file = $this->library->getTemplatesPath() . 'favorites.json'; + + if (!file_exists($file)) + { + return []; + } + + return (array) json_decode(file_get_contents($file), true); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Library/Library.php b/plugins/system/nrframework/NRFramework/Library/Library.php new file mode 100644 index 00000000..424bacf3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Library/Library.php @@ -0,0 +1,440 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Library; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Layout\LayoutHelper; +use Joomla\CMS\Uri\Uri; + +class Library +{ + /** + * Library item info popup. + * + * @var string + */ + private $info_modal_id = 'tf-library-item-info-popup'; + + /** + * Library preview popup. + * + * @var string + */ + private $preview_modal_id = 'tf-library-preview-popup'; + + /** + * The library settings + * + * @var array + */ + public $library_settings = []; + + /** + * Favorites. + * + * @var Faovirtes + */ + public $favorites; + + /** + * Templates. + * + * @var Templates + */ + public $templates; + + public function __construct($library_settings = []) + { + $this->library_settings = $library_settings; + + $this->favorites = new Favorites($this); + $this->templates = new Templates($this); + } + + public function init() + { + $this->prepare(); + + // Enqueue media + $this->register_media(); + + // Add library popups + $this->add_library_popup(); + $this->add_library_item_info_popup(); + $this->add_library_preview_template_popup(); + } + + /** + * Prepares the Library. + * + * @return void + */ + private function prepare() + { + $this->library_settings['preview_url'] = TF_TEMPLATES_SITE_URL . '?template_preview=1&template=TEMPLATE_ID&project=' . $this->library_settings['project']; + + if (!$this->templates->hasFilters()) + { + $this->library_settings['class'] = 'no-sidebar'; + } + + $this->prepareModal(); + } + + /** + * Adds the toolbar to the modal's header. + * + * @return void + */ + public function prepareModal() + { + // Upgrade to Pro Button + if ($this->getLibrarySetting('project_license_type') === 'lite') + { + ?> + + + + + + + + + + + + addScriptDeclaration(' + document.addEventListener("DOMContentLoaded", function() { + /** + * Main Templates Library Popup + */ + let mainPopup = document.querySelector("#' . $this->library_settings['id'] . '"); + + /** + * Append Upgrade to Pro button to header + */ + let upgradeButton = document.querySelector(".tf-header-upgrade-button"); + if (upgradeButton) { + upgradeButton.removeAttribute("style"); + mainPopup.querySelector(".modal-header").append(upgradeButton); + } + + // Append actions + let modalToolbar = document.querySelector(".tfTemplatesLibraryModalToolbar") + modalToolbar.removeAttribute("style"); + mainPopup.querySelector(".modal-header").append(modalToolbar); + + // Add class to library popup + mainPopup.classList.add("tf-templates-library", "tf-templates-library-popup", "' . (defined('nrJ4') ? 'isJ4' : 'isJ3') . '"); + + /** + * Info Templates Library Popup + */ + let infoPopup = document.querySelector("#' . $this->info_modal_id . '"); + + // Append actions + modalToolbar = document.querySelector(".tfInfoTemplatesLibraryModalToolbar") + modalToolbar.removeAttribute("style"); + infoPopup.querySelector(".modal-header").append(modalToolbar); + + // Add class to info popup + infoPopup.classList.add("tf-templates-library-item-info", "tf-templates-library-popup", "' . (defined('nrJ4') ? 'isJ4' : 'isJ3') . '"); + + /** + * Preview Templates Library Popup + */ + let previewPopup = document.querySelector("#' . $this->preview_modal_id . '"); + + // Append toolbar on the left side of the header + modalToolbar = document.querySelector(".tfPreviewTemplatesLibraryModalToolbarLeft").cloneNode(true); + modalToolbar.removeAttribute("style"); + previewPopup.querySelector(".modal-header").insertBefore(modalToolbar, previewPopup.querySelector(".modal-header").firstChild); + + // Append responsive icons on the center of the header + modalToolbar = document.querySelector(".tfPreviewTemplatesLibraryModalToolbarCenter").cloneNode(true); + modalToolbar.removeAttribute("style"); + previewPopup.querySelector(".modal-header").append(modalToolbar); + + // Append actions + modalToolbar = document.querySelector(".tfPreviewTemplatesLibraryModalToolbar") + modalToolbar.removeAttribute("style"); + previewPopup.querySelector(".modal-header").append(modalToolbar); + + // Add class to preview popup + previewPopup.classList.add("tf-templates-library-popup-preview", "tf-templates-library-popup", "' . (defined('nrJ4') ? 'isJ4' : 'isJ3') . '"); + }); + '); + } + + /** + * Adds admin media + * + * @return void + */ + public function register_media() + { + // Templates Library CSS + HTMLHelper::stylesheet('plg_system_nrframework/tf_templates_library.css', ['relative' => true, 'version' => 'auto']); + + // Templates Library JS + HTMLHelper::script('plg_system_nrframework/tf_templates_library.js', ['relative' => true, 'version' => 'auto']); + + // Add Javascript options + $doc = Factory::getDocument(); + $options = $doc->getScriptOptions('tassos_framework'); + $options = is_array($options) ? $options : []; + $options = [ + 'project_name' => $this->library_settings['project_name'], + 'pro' => Text::_('NR_PRO'), + 'lite' => Text::_('NR_LITE'), + 'license_key' => Text::_('NR_LICENSE_KEY'), + 'license' => $this->library_settings['license_key'], + 'install_extension' => TEXT::_('NR_INSTALL_EXTENSION'), + 'update_extension' => TEXT::_('NR_UPDATE_EXTENSION'), + 'templates_library_ajax_url' => Uri::base() . '?option=com_ajax&format=raw&plugin=nrframework&task=TemplatesLibrary', + 'csrf_token' => Session::getFormToken() + ]; + $doc->addScriptOptions('tassos_framework', $options); + } + + /** + * Adds the popup at the footer of the page. Appears when you click the "New" / "Add New" button. + * + * @return void + */ + public function add_library_popup() + { + $payload = [ + 'title' => $this->library_settings['title'], + 'closeButton' => false, + 'backdrop' => 'static' + ]; + + $content = LayoutHelper::render('library/tmpl', $this->library_settings, JPATH_PLUGINS . '/system/nrframework/layouts'); + + echo HTMLHelper::_('bootstrap.renderModal', $this->library_settings['id'], $payload, $content); + } + + /** + * Adds the popup that displays the info for each template. + * + * @return void + */ + public function add_library_item_info_popup() + { + $info_payload = [ + 'category_label' => $this->library_settings['main_category_label'] + ]; + $content = LayoutHelper::render('library/info_popup', $info_payload, JPATH_PLUGINS . '/system/nrframework/layouts'); + + $payload = [ + 'title' => 'Template Title', + 'closeButton' => false, + 'backdrop' => 'static' + ]; + + echo HTMLHelper::_('bootstrap.renderModal', $this->info_modal_id, $payload, $content); + } + + /** + * Adds the popup at that allows us to preview a template. + * + * @return void + */ + public function add_library_preview_template_popup() + { + $content = LayoutHelper::render('library/preview', [], JPATH_PLUGINS . '/system/nrframework/layouts'); + + $payload = [ + 'title' => 'Template Title', + 'closeButton' => false, + 'backdrop' => 'static' + ]; + + echo HTMLHelper::_('bootstrap.renderModal', $this->preview_modal_id, $payload, $content); + } + + /** + * Return templates folder path + * + * @return string + */ + public function getTemplatesPath() + { + $component = isset($this->library_settings['component']) ? $this->library_settings['component'] : 'com_rstbox'; + return JPATH_ROOT . '/media/' . $component . '/templates/'; + } + + /** + * Returns the Framework Plugin URL. + * + * @return string + */ + public function getNRFrameworkPluginURL() + { + return Uri::base() . 'index.php?option=com_plugins&task=plugin.edit&extension_id=' . \NRFramework\Extension::getID('nrframework', 'plugin', 'system'); + } + + /** + * Returns a library settings value. + * + * @param string $key + * @param string $default + * + * @return string + */ + public function getLibrarySetting($key, $default = '') + { + return isset($this->library_settings[$key]) ? $this->library_settings[$key] : $default; + } + + /** + * Sets a library settings value. + * + * @param string $key + * @param mixed $value + * + * @return string + */ + public function setLibrarySetting($key, $value) + { + $this->library_settings[$key] = $value; + } + + /** + * Returns the refresh icon. + * + * @return string + */ + public function getRefreshIcon() + { + return ' + + + '; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Library/Templates.php b/plugins/system/nrframework/NRFramework/Library/Templates.php new file mode 100644 index 00000000..91786fd2 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Library/Templates.php @@ -0,0 +1,355 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Library; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use Joomla\CMS\Http\HttpFactory; +use Joomla\CMS\Layout\LayoutHelper; + +class Templates +{ + /** + * Library + * + * @var Library + */ + protected $library = []; + + /** + * The user download key. + * + * @var String + */ + private $download_key = null; + + public function __construct($library = []) + { + $this->library = $library; + $this->download_key = $this->library->getLibrarySetting('license_key'); + } + + /** + * Checks whether we have the template locally and retrives its layout. + * If no local template is found, then retrieves it from remote and returns its layout. + * + * @return string + */ + public function tf_library_ajax_get_templates() + { + return $this->getTemplates($this->getList()); + } + + /** + * Checks whether the given license is valid or not and updates the "license_key_status" property + * which defines whether the pro templates should contain an error letting the user know that their + * license is invalid. + * + * @return void + */ + private function checkAndUpdateLicenseStatus() + { + $license_status = \NRFramework\Helpers\License::getRemoteLicenseData($this->download_key); + $this->library->setLibrarySetting('license_key_status', !isset($license_status['error']) ? 'valid' : 'invalid'); + } + + /** + * Returns all available templates + * + * @param array $templates + * + * @return array + */ + private function getTemplates($templates = []) + { + if (isset($templates->error) && $templates->error) + { + return $templates; + } + + $this->checkAndUpdateLicenseStatus(); + + $layout_payload = [ + 'main_category_label' => $this->library->getLibrarySetting('main_category_label'), + 'project_name' => $this->library->getLibrarySetting('project_name'), + 'project_license_type' => $this->library->getLibrarySetting('project_license_type'), + 'project_version' => $this->library->getLibrarySetting('project_version'), + 'product_license_settings_url' => $this->library->getLibrarySetting('product_license_settings_url'), + 'template_use_url' => $this->library->getLibrarySetting('template_use_url'), + 'license_key' => $this->download_key, + 'license_key_status' => $this->library->getLibrarySetting('license_key_status'), + 'templates' => isset($templates->templates) ? $templates->templates : [], + 'favorites' => $this->library->favorites->getFavorites() + ]; + + $filters_payload = [ + 'filters' => $this->getTemplatesFilters(isset($templates->filters) ? $templates->filters : []) + ]; + + $layouts_path = JPATH_PLUGINS . '/system/nrframework/layouts'; + + return [ + 'templates' => LayoutHelper::render('library/items_list', $layout_payload, $layouts_path), + 'filters' => LayoutHelper::render('library/filters', $filters_payload, $layouts_path) + ]; + } + + /** + * Returns the filters payload. + * + * @param object $filters + * + * @return array + */ + private function getTemplatesFilters($filters) + { + // Main filters + $data = []; + + $categories = isset($filters->categories) ? $filters->categories : []; + if ($categories) + { + $data['category'] = [ + 'label' => $this->library->getLibrarySetting('main_category_label', Text::_('NR_CATEGORIES_PLURAL')), + 'items' => $categories + ]; + } + + $goals = isset($filters->goals) ? $filters->goals : []; + if ($goals) + { + $data['goal'] = [ + 'label' => Text::_('NR_GOALS'), + 'items' => $goals + ]; + } + + // Add compatibility filter (Free/Pro filtering) only in the Lite version + if ($this->library->getLibrarySetting('project_license_type') === 'lite') + { + $compatibility = isset($filters->compatibility) ? $filters->compatibility : []; + if ($compatibility) + { + $data['compatibility'] = [ + 'label' => Text::_('NR_COMPATIBILITY'), + 'items' => $compatibility + ]; + } + } + + return $data; + } + + public function hasFilters() + { + if (!$localTemplates = $this->getLocalTemplates()) + { + return; + } + + if (!isset($localTemplates->filters)) + { + return; + } + + $categories = isset($localTemplates->filters->categories) ? $localTemplates->filters->categories : []; + $goals = isset($localTemplates->filters->goals) ? $localTemplates->filters->goals : []; + $isFree = $this->library->getLibrarySetting('project_license_type') === 'lite'; + + return $categories || $goals || $isFree; + } + + /** + * Retrieve remote templates, store them locally and return new layout. + * + * @return string + */ + public function tf_library_ajax_refresh_templates() + { + return $this->getTemplates($this->getRemoteTemplatesAndStore()); + } + + /** + * Insert template. + * + * @return void + */ + public function tf_library_ajax_insert_template() + { + $template_id = $this->library->getLibrarySetting('template_id'); + + // Get remote template + $templates_url = str_replace('{{PROJECT}}', $this->library->getLibrarySetting('project'), TF_TEMPLATE_GET_URL); + $templates_url = str_replace('{{DOWNLOAD_KEY}}', $this->download_key, $templates_url); + $templates_url = str_replace('{{TEMPLATE}}', $template_id, $templates_url); + + $response = HttpFactory::getHttp()->get($templates_url); + + if (!$body = json_decode($response->body, true)) + { + return [ + 'error' => true, + 'message' => 'Cannot insert template.' + ]; + } + + // An error has occurred + if (isset($body['error']) && $body['error']) + { + return [ + 'error' => true, + 'message' => $body['response'] + ]; + } + + // Prepare template + $template = $body['response']['template']; + // Set ID used to check if we are adding a valid template within the extension's item edit page + $template['id'] = $body['response']['id']; + + // Save template locally so we can fetch its contents on redirect + file_put_contents($this->library->getTemplatesPath() . 'template.json', json_encode($template)); + + return [ + 'error' => false, + 'message' => 'Inserting template.', + 'redirect' => $this->library->getLibrarySetting('template_use_url') . $template_id + ]; + } + + /** + * Save templates locally + * + * @param array $body + * + * @return void + */ + private function saveLocalTemplate($body) + { + // Create directory if not exist + if (!is_dir($this->library->getTemplatesPath())) + { + \NRFramework\File::createDirs($this->library->getTemplatesPath()); + } + + $path = $this->library->getTemplatesPath() . 'templates.json'; + + file_put_contents($path, json_encode($body)); + } + + /** + * Returns the local templates + * + * @return array + */ + private function getLocalTemplates() + { + $path = $this->library->getTemplatesPath() . 'templates.json'; + + if (!file_exists($path)) + { + return false; + } + + // If templates are old, fetch remote list + if ($this->templatesRequireUpdate()) + { + return false; + } + + return json_decode(file_get_contents($path)); + } + + /** + * Checks whether the local templates list is older than X days. + * + * @return bool + */ + private function templatesRequireUpdate() + { + $path = $this->library->getTemplatesPath() . 'templates.json'; + + $days_old = 7; + + /** + * If its older than X days, then request remote list + */ + // Get the modification time of the templates file + $modTime = @filemtime($path); + + // Current time + $now = time(); + + // Minimum time difference + $threshold = $days_old * 24 * 3600; + + // Do we need an update? + return ($now - $modTime) >= $threshold; + } + + /** + * Returns the remote templates + * + * @return array + */ + private function getRemoteTemplates() + { + // Get remote templates + $templates_url = str_replace('{{PROJECT}}', $this->library->getLibrarySetting('project'), TF_TEMPLATES_GET_URL); + + $response = HttpFactory::getHttp()->get($templates_url); + + if (!$response = $response->body) + { + return; + } + + if (!$response = json_decode($response)) + { + $error = new \stdClass(); + $error->error = true; + $error->message = sprintf(Text::_('NR_TEMPLATES_CANNOT_BE_RETRIEVED'), $this->library->getRefreshIcon()); + return $error; + } + + return $response; + } + + /** + * Gets the remote templates and stores them locally + * + * @return array + */ + private function getRemoteTemplatesAndStore() + { + $templates = $this->getRemoteTemplates(); + + if (isset($templates->error) && $templates->error) + { + return $templates; + } + + $this->saveLocalTemplate($templates); + + return $templates; + } + + /** + * Get templates list + * + * @return array + */ + private function getList() + { + // try to find local templates with fallback remote templates + return $this->getLocalTemplates() ?: $this->getRemoteTemplatesAndStore(); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Library/index.php b/plugins/system/nrframework/NRFramework/Library/index.php new file mode 100644 index 00000000..e69de29b diff --git a/plugins/system/nrframework/NRFramework/Mimes.php b/plugins/system/nrframework/NRFramework/Mimes.php new file mode 100644 index 00000000..00b88e8c --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Mimes.php @@ -0,0 +1,757 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + * @credits https://github.com/codeigniter4/CodeIgniter4/blob/develop/app/Config/Mimes.php +*/ + +namespace NRFramework; + +// No direct access +defined('_JEXEC') or die; + +class Mimes +{ + /** + * Map of extensions to mime types. + * + * @var array + */ + public static $mimes = [ + 'hqx' => [ + 'application/mac-binhex40', + 'application/mac-binhex', + 'application/x-binhex40', + 'application/x-mac-binhex40', + ], + 'cpt' => 'application/mac-compactpro', + 'csv' => [ + 'text/csv', + 'text/x-comma-separated-values', + 'text/comma-separated-values', + 'application/vnd.ms-excel', + 'application/x-csv', + 'text/x-csv', + 'application/csv', + 'application/excel', + 'application/vnd.msexcel', + 'text/plain', + ], + 'bin' => [ + 'application/macbinary', + 'application/mac-binary', + 'application/octet-stream', + 'application/x-binary', + 'application/x-macbinary', + ], + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => [ + 'application/octet-stream', + 'application/x-msdownload', + ], + 'class' => 'application/octet-stream', + 'psd' => [ + 'application/x-photoshop', + 'image/vnd.adobe.photoshop', + ], + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => [ + 'application/pdf', + 'application/force-download', + 'application/x-download', + ], + 'ai' => [ + 'application/pdf', + 'application/postscript', + ], + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => [ + 'application/vnd.ms-excel', + 'application/msexcel', + 'application/x-msexcel', + 'application/x-ms-excel', + 'application/x-excel', + 'application/x-dos_ms_excel', + 'application/xls', + 'application/x-xls', + 'application/excel', + 'application/download', + 'application/vnd.ms-office', + 'application/msword', + ], + 'ppt' => [ + 'application/vnd.ms-powerpoint', + 'application/powerpoint', + 'application/vnd.ms-office', + 'application/msword', + ], + 'pptx' => [ + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/x-zip', + 'application/zip', + ], + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'gzip' => 'application/x-gzip', + 'php' => [ + 'application/x-php', + 'application/x-httpd-php', + 'application/php', + 'text/php', + 'text/x-php', + 'application/x-httpd-php-source', + ], + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => [ + 'application/x-javascript', + 'text/plain', + ], + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => [ + 'application/x-tar', + 'application/x-gzip-compressed', + ], + 'z' => 'application/x-compress', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => [ + 'application/x-zip', + 'application/zip', + 'application/x-zip-compressed', + 'application/s-compressed', + 'multipart/x-zip', + ], + 'rar' => [ + 'application/vnd.rar', + 'application/x-rar', + 'application/rar', + 'application/x-rar-compressed', + ], + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => [ + 'audio/mpeg', + 'audio/mpg', + 'audio/mpeg3', + 'audio/mp3', + ], + 'aif' => [ + 'audio/x-aiff', + 'audio/aiff', + ], + 'aiff' => [ + 'audio/x-aiff', + 'audio/aiff', + ], + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => [ + 'audio/x-wav', + 'audio/wave', + 'audio/wav', + ], + 'bmp' => [ + 'image/bmp', + 'image/x-bmp', + 'image/x-bitmap', + 'image/x-xbitmap', + 'image/x-win-bitmap', + 'image/x-windows-bmp', + 'image/ms-bmp', + 'image/x-ms-bmp', + 'application/bmp', + 'application/x-bmp', + 'application/x-win-bitmap', + ], + 'gif' => 'image/gif', + 'jpg' => [ + 'image/jpeg', + 'image/pjpeg', + ], + 'jpeg' => [ + 'image/jpeg', + 'image/pjpeg', + ], + 'jpe' => [ + 'image/jpeg', + 'image/pjpeg', + ], + 'jp2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'j2k' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpf' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpg2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpx' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpm' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'mj2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'mjp2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'png' => [ + 'image/png', + 'image/x-png', + ], + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'css' => [ + 'text/css', + 'text/plain', + ], + 'html' => [ + 'text/html', + 'text/plain', + ], + 'htm' => [ + 'text/html', + 'text/plain', + ], + 'shtml' => [ + 'text/html', + 'text/plain', + ], + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => [ + 'text/plain', + 'text/x-log', + ], + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => [ + 'application/xml', + 'text/xml', + 'text/plain', + ], + 'xsl' => [ + 'application/xml', + 'text/xsl', + 'text/xml', + ], + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => [ + 'video/x-msvideo', + 'video/msvideo', + 'video/avi', + 'application/x-troff-msvideo', + ], + 'movie' => 'video/x-sgi-movie', + 'doc' => [ + 'application/msword', + 'application/vnd.ms-office', + ], + 'docx' => [ + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/zip', + 'application/msword', + 'application/x-zip', + ], + 'dot' => [ + 'application/msword', + 'application/vnd.ms-office', + ], + 'dotx' => [ + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/zip', + 'application/msword', + ], + 'xlsx' => [ + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/zip', + 'application/vnd.ms-excel', + 'application/msword', + 'application/x-zip', + ], + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'word' => [ + 'application/msword', + 'application/octet-stream', + ], + 'xl' => 'application/excel', + 'eml' => 'message/rfc822', + 'json' => [ + 'application/json', + 'text/json', + ], + 'pem' => [ + 'application/x-x509-user-cert', + 'application/x-pem-file', + 'application/octet-stream', + ], + 'p10' => [ + 'application/x-pkcs10', + 'application/pkcs10', + ], + 'p12' => 'application/x-pkcs12', + 'p7a' => 'application/x-pkcs7-signature', + 'p7c' => [ + 'application/pkcs7-mime', + 'application/x-pkcs7-mime', + ], + 'p7m' => [ + 'application/pkcs7-mime', + 'application/x-pkcs7-mime', + ], + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'crt' => [ + 'application/x-x509-ca-cert', + 'application/x-x509-user-cert', + 'application/pkix-cert', + ], + 'crl' => [ + 'application/pkix-crl', + 'application/pkcs-crl', + ], + 'der' => 'application/x-x509-ca-cert', + 'kdb' => 'application/octet-stream', + 'pgp' => 'application/pgp', + 'gpg' => 'application/gpg-keys', + 'sst' => 'application/octet-stream', + 'csr' => 'application/octet-stream', + 'rsa' => 'application/x-pkcs7', + 'cer' => [ + 'application/pkix-cert', + 'application/x-x509-ca-cert', + ], + '3g2' => 'video/3gpp2', + '3gp' => [ + 'video/3gp', + 'video/3gpp', + ], + 'mp4' => 'video/mp4', + 'm4a' => 'audio/x-m4a', + 'f4v' => [ + 'video/mp4', + 'video/x-f4v', + ], + 'flv' => 'video/x-flv', + 'webm' => 'video/webm', + 'aac' => 'audio/x-acc', + 'm4u' => 'application/vnd.mpegurl', + 'm3u' => 'text/plain', + 'xspf' => 'application/xspf+xml', + 'vlc' => 'application/videolan', + 'wmv' => [ + 'video/x-ms-wmv', + 'video/x-ms-asf', + ], + 'au' => 'audio/x-au', + 'ac3' => 'audio/ac3', + 'flac' => [ + 'audio/x-flac', + 'audio/flac' + ], + 'ogg' => [ + 'audio/ogg', + 'video/ogg', + 'application/ogg', + ], + 'kra' => [ + 'application/x-krita', + 'application/zip', + ], + 'krz' => [ + 'application/x-krita', + 'application/zip', + ], + 'kmz' => [ + 'application/vnd.google-earth.kmz', + 'application/zip', + 'application/x-zip', + ], + 'kml' => [ + 'application/vnd.google-earth.kml+xml', + 'application/xml', + 'text/xml', + ], + 'ics' => 'text/calendar', + 'ical' => 'text/calendar', + 'zsh' => 'text/x-scriptzsh', + '7z' => [ + 'application/x-compressed', + 'application/x-zip-compressed', + 'application/x-7z-compressed', + 'application/zip', + 'multipart/x-zip', + ], + '7zip' => [ + 'application/x-compressed', + 'application/x-zip-compressed', + 'application/x-7z-compressed', + 'application/zip', + 'multipart/x-zip', + ], + 'cdr' => [ + 'application/cdr', + 'application/coreldraw', + 'application/x-cdr', + 'application/x-coreldraw', + 'image/cdr', + 'image/x-cdr', + 'zz-application/zz-winassoc-cdr', + ], + 'wma' => [ + 'audio/x-ms-wma', + 'video/x-ms-asf', + ], + 'jar' => [ + 'application/java-archive', + 'application/x-java-application', + 'application/x-jar', + 'application/x-compressed', + ], + 'svg' => [ + 'image/svg+xml', + 'image/svg', + 'application/xml', + 'text/xml', + ], + 'vcf' => 'text/x-vcard', + 'srt' => [ + 'text/srt', + 'text/plain', + ], + 'vtt' => [ + 'text/vtt', + 'text/plain', + ], + 'ico' => [ + 'image/x-icon', + 'image/x-ico', + 'image/vnd.microsoft.icon', + ], + 'stl' => [ + 'application/sla', + 'application/vnd.ms-pki.stl', + 'application/x-navistyle', + ], + ]; + + /** + * Attempts to determine the best mime type for the given file extension. + * + * @param string $extension + * + * @return string|null The mime type found, or none if unable to determine. + */ + public static function getTypesFromExtension($extension) + { + $extension = trim(strtolower($extension), '. '); + + if (!array_key_exists($extension, static::$mimes)) + { + return null; + } + + return (array) static::$mimes[$extension]; + } + + /** + * Attempts to determine the best file extension for a given mime type. + * + * @param string $type + * @param string|null $proposedExtension - default extension (in case there is more than one with the same mime type) + * + * @return string|null The extension determined, or null if unable to match. + */ + public static function guessExtensionFromType($type, $proposedExtension = null) + { + $type = trim(strtolower($type), '. '); + + $proposedExtension = trim(strtolower($proposedExtension)); + + if ($proposedExtension !== '') + { + if (array_key_exists($proposedExtension, static::$mimes) && in_array($type, is_string(static::$mimes[$proposedExtension]) ? [static::$mimes[$proposedExtension]] : static::$mimes[$proposedExtension], true)) + { + // The detected mime type matches with the proposed extension. + return $proposedExtension; + } + + // An extension was proposed, but the media type does not match the mime type list. + return null; + } + + // Reverse check the mime type list if no extension was proposed. + // This search is order sensitive! + foreach (static::$mimes as $ext => $types) + { + if ((is_string($types) && $types === $type) || (is_array($types) && in_array($type, $types, true))) + { + return $ext; + } + } + + return null; + } + + /** + * Test whether the given mime type is in the allowed file types. + * + * @param mixed $allowed_types Can be a list of comma separated types or an array of types. Types can be either an extension (.jpg) or a mime type (application/zip) + * @param string $mime The mime type to check + * + * @return mixed Null on failure, true on success + */ + public static function check($allowed_types, $detected_mime) + { + if (!$allowed_types || !$detected_mime) + { + return false; + } + + $allowed_types = self::toSafeArray($allowed_types); + + foreach ($allowed_types as $allowed_type) + { + // Check whether we have a mime type or a file extension. A Mime type is supposed to have a forward slash character. + // If we have a file extension (.jpg, .zip), convert it to a Mime type. + $allowed_mime_types = strpos($allowed_type, '/') === false ? self::getTypesFromExtension($allowed_type) : $allowed_type; + + if (self::typeIsInTypes($detected_mime, $allowed_mime_types)) + { + return true; + } + } + } + + /** + * Validates if the provided file extension is allowed based on a list of permitted types. + * + * This method checks if the given file extension matches any of the allowed types specified. + * The allowed types can be either file extensions or MIME types. If a MIME type is provided, + * it is converted into its associated file extensions for validation. + * + * @param string $fileExtension The file extension to validate, which will be normalized to lowercase. + * @param array|string $allowedTypes An array or a comma-separated string of allowed file types (extensions or MIME types). + * + * @return bool Returns true if the file extension is valid; otherwise, false. + */ + public static function validateFileExtension($fileExtension, $allowedTypes) + { + $fileExtension = strtolower(ltrim(trim($fileExtension), '.')); + $allowedTypes = self::toSafeArray($allowedTypes); + + foreach ($allowedTypes as $allowedType) + { + // Trim any whitespace + $allowedType = trim($allowedType); + + // Check if the allowed type is a MIME type + if (strpos($allowedType, '/') !== false) + { + // It's a MIME type, convert to associated extensions + $extensions = self::getExtensionsByMimeType($allowedType); + + // Validate against each associated extension + foreach ($extensions as $extension) + { + if ($extension === $fileExtension) + { + return true; + } + } + } else + { + // It's a file extension, normalize and compare + $allowedExtension = ltrim(strtolower($allowedType), '.'); + + if ($allowedExtension === $fileExtension) + { + return true; + } + } + } + + return false; // No valid extension found + } + + /** + * Retrieves an array of file extensions that are associated with a given MIME type. + * + * This method loops through a predefined list of MIME types and their associated file extensions. + * If the specified MIME type matches any of the entries, the corresponding file extensions are collected. + * + * @param string $mimeType The MIME type for which to find associated file extensions. + * + * @return array An array of unique file extensions associated with the provided MIME type. + * Returns an empty array if no matching extensions are found or if the MIME type is empty. + */ + public static function getExtensionsByMimeType($mimeType) + { + // Initialize an array to hold the matching extensions + $matchingExtensions = []; + + if (!$mimeType) + { + return $matchingExtensions; + } + + // Loop through the mimes array + foreach (self::$mimes as $extension => $types) + { + // Ensure $types is an array + $types = (array) $types; + + // Loop through each type + foreach ($types as $type) + { + // Use preg_match to handle wildcard types + if (preg_match('#' . str_replace(['*'], ['.*'], $mimeType) . '#', $type)) + { + $matchingExtensions[] = strtolower($extension); + } + } + } + + return array_unique(array_filter($matchingExtensions)); + } + + /** + * Test whether the given detected mime type is in allowed mime types + * + * @param string $detected_type The mime type to check Eg: application/zip + * @param array $allowed_types A list of allowed mime types Eg: ['application/zip', 'images/jpg'] + * + * @return bool True on success + */ + public static function typeIsInTypes($detected_type, $allowed_types) + { + if (!$detected_type || !$allowed_types) + { + return; + } + + $allowed_types = self::toSafeArray($allowed_types); + $detected_type = strtolower($detected_type); + + foreach ($allowed_types as $allowed_type) + { + // Special case: Allow to use wildcard in mime types like: image/* - This requires to convert the asterisk character to regex pattern. + $allowed_type = str_replace('*', '.*', $allowed_type); + + if (preg_match('#' . $allowed_type . '#', $detected_type)) + { + return true; + } + } + } + + /** + * Detect the filename's Mime type + * + * @param string $file The path to the file to be checked + * + * @return mixed the mime type detected false on error + */ + public static function detectFileType($file) + { + // If we can't detect anything mime is false + $mime = false; + + try + { + if (function_exists('mime_content_type')) + { + $mime = mime_content_type($file); + } + elseif (function_exists('finfo_open')) + { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $file); + finfo_close($finfo); + } + } + catch (\Exception $e) + { + } + + return $mime; + } + + private static function toSafeArray($subject) + { + if (!is_array($subject)) + { + $subject = explode(',', $subject); + } + + $subject = array_map('trim', $subject); + $subject = array_map('strtolower', $subject); + $subject = array_unique($subject); + $subject = array_filter($subject); + + return $subject; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Helper.php b/plugins/system/nrframework/NRFramework/Notices/Helper.php new file mode 100644 index 00000000..048f3d89 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Helper.php @@ -0,0 +1,54 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + * @credits https://github.com/codeigniter4/CodeIgniter4/blob/develop/app/Config/Mimes.php +*/ + +namespace NRFramework\Notices; + +// No direct access +defined('_JEXEC') or die; + +use \NRFramework\Extension; + +class Helper +{ + /** + * Returns the extension details for given element. + * + * @param array $data + * @param string $element + * + * @return array + */ + public static function getExtensionDetails($data, $element) + { + // Return bundle only if its active + if (isset($data['bundle']) && $data['bundle']['active']) + { + return $data['bundle']; + } + + $alias = Extension::getExtensionDataFileAlias($element); + + // If no license data found for this extension + if (!isset($data[$alias])) + { + // Return the expired bundle information if it exists + if (isset($data['bundle'])) + { + return $data['bundle']; + } + + // No bundle exists, return nothing + return; + } + + // Return the extension's license data details + return $data[$alias]; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices.php b/plugins/system/nrframework/NRFramework/Notices/Notices.php new file mode 100644 index 00000000..77f28e20 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices.php @@ -0,0 +1,323 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + * @credits https://github.com/codeigniter4/CodeIgniter4/blob/develop/app/Config/Mimes.php +*/ + +namespace NRFramework\Notices; + +// No direct access +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Layout\LayoutHelper; +use Joomla\CMS\Factory; +use \NRFramework\Extension; + +class Notices +{ + /** + * The payload. + * + * @var array + */ + private $payload; + + /** + * The extension's ext_element we are showing notices. + * + * Example: acf + * + * @var string + */ + private $ext_element; + + /** + * The extension's main XML file location folder. + * + * Example: plg_system_acf, com_rstbox + * + * @var string + */ + private $ext_xml; + + /** + * The extension type. + * + * Example: plugin, component, module, etc... + * + * @var string + */ + private $ext_type = 'component'; + + /** + * The notices to exclude. + * + * @var array + */ + private $exclude = []; + + /** + * Define how old (in days) the file that holds all extensions data needs to be set as expired, + * so we can fetch new data. + * + * @var int + */ + private $extensions_data_file_days_old = 1; + + /** + * Download Key. + * + * @var String + */ + protected $download_key = null; + + /** + * The license data for the given download key. + * + * @var array + */ + protected $license_data = []; + + /** + * Notices Instance. + * + * @var Notices + */ + private static $instance; + + public function __construct($payload = []) + { + $this->payload = $payload; + + $this->ext_element = isset($this->payload['ext_element']) ? $this->payload['ext_element'] : ''; + $this->ext_xml = isset($this->payload['ext_xml']) ? $this->payload['ext_xml'] : ''; + $this->ext_type = isset($this->payload['ext_type']) ? $this->payload['ext_type'] : $this->ext_type; + $this->exclude = isset($this->payload['exclude']) ? $this->payload['exclude'] : []; + + $this->download_key = \NRFramework\Functions::getDownloadKey(); + } + + /** + * Returns class instance + * + * @param array $payload + * + * @return object + */ + public static function getInstance($payload = []) + { + if (is_null(self::$instance)) + { + self::$instance = new self($payload); + } + + return self::$instance; + } + + /** + * Show all available notices. + * + * @return void + */ + public function show() + { + // Show only for Super Users + if (!$this->isSuperUser()) + { + return; + } + + HTMLHelper::stylesheet('plg_system_nrframework/notices.css', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_system_nrframework/notices.js', ['relative' => true, 'version' => 'auto']); + + $payload = [ + 'ext_element' => $this->ext_element, + 'ext_xml' => $this->ext_xml, + 'ext_type' => $this->ext_type, + 'exclude' => $this->exclude + ]; + + echo LayoutHelper::render('notices/tmpl', $payload, dirname(dirname(__DIR__)) . '/layouts'); + } + + /** + * Check if the current user is a Super User. + * + * @return bool + */ + private function isSuperUser() + { + return Factory::getUser()->authorise('core.admin'); + } + + /** + * Returns the base notices. + * + * @param array $notices + * + * @return void + */ + private function getBaseNotices() + { + $base_notices = [ + 'Outdated', + 'DownloadKey', + 'Geolocation', + 'UpgradeToPro', + 'UpgradeToBundle' + ]; + + // Exclude notices we should not display + if (count($this->exclude)) + { + foreach ($base_notices as $key => $notice) + { + if (!in_array($notice, $this->exclude)) + { + continue; + } + + unset($base_notices[$key]); + } + } + + $notices = []; + + // Initialize notices + foreach ($base_notices as $key => $notice) + { + $class = '\NRFramework\Notices\Notices\\' . $notice; + + // Skip empty notice + if (!$html = (new $class($this->payload))->render()) + { + continue; + } + + $notices[strtolower($notice)] = $html; + } + + return $notices; + } + + /** + * Returns which license-related notices to show. + * + * Notices: + * - Extension expires in date + * - Extension expired at date + * + * @return array + */ + private function getLicensesBasedNoticesToShow() + { + // If no data found for this extension, abort + if (!$extension_data = \NRFramework\Notices\Helper::getExtensionDetails($this->license_data, $this->ext_element)) + { + return false; + } + + if (!array_key_exists('active', $extension_data)) + { + return; + } + + $notices = []; + + // Active subscription and we have a expiration date + if ($extension_data['active'] && array_key_exists('expires_in', $extension_data) && $extension_data['expires_in']) + { + $notices[] = (new Notices\Expiring(array_merge($this->payload, [ + 'expires_in' => $extension_data['expires_in'], + 'plan' => $extension_data['plan'] + ])))->render(); + } + + /** + * We should not have an active subscription and the "expired_at" date must be set. + * + * If "active" is true and an "expired_at" date is set, it means we have a Bundle plan. + */ + if (!$extension_data['active'] && array_key_exists('expired_at', $extension_data) && $extension_data['expired_at']) + { + $notices[] = (new Notices\Expired(array_merge($this->payload, [ + 'expired_at' => $extension_data['expired_at'], + 'plan' => $extension_data['plan'] + ])))->render(); + } + + if (!$notices) + { + return; + } + + return implode('', $notices); + } + + /** + * Returns the based notices: + * + * Notices: + * - Base notices + * - Outdated + * - Download Key + * - Geolocation + * - Upgrade To Pro + * - Upgrade To Bundle + * - Update notice + * - Extension expires in date + * - Extension expired at date + * - Rate (If none of the license-related notices appear) + * + * @return string + */ + public function getNotices() + { + // Check and Update the local licenses data + $this->checkAndUpdateExtensionsData(); + + $notices = $this->getBaseNotices(); + + // Show Update Notice + if ($update_html = (new Notices\Update($this->payload))->render()) + { + $notices['update'] = $update_html; + } + + if ($license_notices = $this->getLicensesBasedNoticesToShow()) + { + $notices['license'] = $license_notices; + } + else if ($rate_html = (new Notices\Rate($this->payload))->render()) + { + $notices['rate'] = $rate_html; + } + + return $notices; + } + + /** + * Checks whether the current extensions data has expired and updates the data file. + * + * Also checks and sets the installation date of the extension. + * + * @return bool + */ + public function checkAndUpdateExtensionsData() + { + // Sets licenses information + $this->license_data = \NRFramework\Helpers\License::getRemoteLicenseData($this->download_key); + + // Add the license data to the payload as well + $this->payload['license_data'] = $this->license_data; + + // Set installation date + Extension::setInstallationDate($this->ext_element, gmdate('Y-m-d H:i:s')); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/DownloadKey.php b/plugins/system/nrframework/NRFramework/Notices/Notices/DownloadKey.php new file mode 100644 index 00000000..f4d03f91 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/DownloadKey.php @@ -0,0 +1,112 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Functions; + +class DownloadKey extends Notice +{ + protected $notice_payload = [ + 'type' => 'error', + 'class' => 'download-key', + 'dismissible' => false, + 'download_key' => null, + 'state' => null + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + $this->payload['download_key'] = Functions::getDownloadKey(); + $this->payload['state'] = isset($this->payload['license_data']['state']) ? $this->payload['license_data']['state'] : null; + } + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + $text = !empty($this->payload['download_key']) || ($this->payload['state'] && in_array($this->payload['state'], ['invalid_key'])) ? Text::_('NR_IS_INVALID') : Text::_('NR_IS_MISSING'); + return sprintf(Text::_('NR_DOWNLOAD_KEY_TEXT'), $text); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + $text = !empty($this->payload['download_key']) || ($this->payload['state'] && in_array($this->payload['state'], ['invalid_key'])) ? Text::_('NR_A_VALID') : Text::_('NR_YOUR'); + return sprintf(Text::_('NR_DOWNLOAD_KEY_MISSING_DESC'), $this->extension_name, $text); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = 'https://www.tassos.gr/kb/general/how-to-activate-your-pro-version'; + + return ' + ' . Text::_('JAPPLY') . ' + + + + + ' . Text::_('JHELP') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // Ensure customer is using the Pro version + if (!\NRFramework\Extension::isPro($this->payload['ext_xml'])) + { + return false; + } + + // If user is Pro but has no license details, show it + if (!$details = \NRFramework\Notices\Helper::getExtensionDetails($this->payload['license_data'], $this->payload['ext_element'])) + { + return true; + } + + // If state exists and key is invalid/or no subscriptions exist, return true + if ($this->payload['state'] && in_array($this->payload['state'], ['missing_key', 'invalid_key'])) + { + return true; + } + + if (!empty($this->getDownloadKey())) + { + return false; + } + + return true; + } + + private function getDownloadKey() + { + return isset($this->payload['download_key']) ? $this->payload['download_key'] : ''; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Expired.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Expired.php new file mode 100644 index 00000000..1d0e5150 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Expired.php @@ -0,0 +1,91 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Functions; +use \NRFramework\Extension; + +class Expired extends Notice +{ + protected $notice_payload = [ + 'type' => 'error', + 'class' => 'expired', + 'expired_at' => '', + 'plan' => '' + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + $this->payload['tooltip'] = Text::_('NR_NOTICE_EXPIRED_TOOLTIP'); + $this->payload['expired_at'] = isset($payload['expired_at']) ? $payload['expired_at'] : false; + $this->payload['plan'] = isset($payload['plan']) ? $payload['plan'] : false; + } + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return sprintf(Text::_('NR_SUBSCRIPTION_EXPIRED'), $this->extension_name); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + $title = strtolower($this->payload['plan']) === 'bundle' ? $this->payload['plan'] : $this->extension_name . ' ' . $this->payload['plan']; + + return sprintf(Text::_('NR_SUBSCRIPTION_EXPIRED_DESC'), $title, Functions::applySiteTimezoneToDate($this->payload['expired_at'], 'd M o')); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = 'https://www.tassos.gr/subscriptions'; + + return '' . sprintf(Text::_('NR_RENEW_X_PERCENT_OFF'), 20) . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, it's already hidden + if ($this->factory->getCookie('tfNoticeHideExpiredNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + // The date the extension expired. + if (!$this->payload['expired_at']) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Expiring.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Expiring.php new file mode 100644 index 00000000..616dbee4 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Expiring.php @@ -0,0 +1,103 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Functions; + +class Expiring extends Notice +{ + protected $notice_payload = [ + 'type' => 'warning', + 'class' => 'expiring', + 'expires_in' => '', + 'plan' => '' + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + $this->payload['tooltip'] = Text::_('NR_NOTICE_EXPIRING_TOOLTIP'); + $this->payload['expires_in'] = isset($this->payload['expires_in']) ? $this->payload['expires_in'] : false; + $this->payload['plan'] = isset($payload['plan']) ? $payload['plan'] : false; + } + + /** + * Define the remaining days the subscription must have to display the expiring subscription notice. + * + * @var int + */ + private $expiring_notice_days = 30; + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return sprintf(Text::_('NR_SUBSCRIPTION_EXPIRING'), $this->extension_name); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + $title = strtolower($this->payload['plan']) === 'bundle' ? $this->payload['plan'] : $this->extension_name . ' ' . $this->payload['plan']; + + return sprintf(Text::_('NR_SUBSCRIPTION_EXPIRING_DESC'), $title, Functions::applySiteTimezoneToDate($this->payload['expires_in'], 'd M o')); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = 'https://www.tassos.gr/subscriptions'; + + return '' . sprintf(Text::_('NR_RENEW_X_PERCENT_OFF'), 30) . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, it's already hidden + if ($this->factory->getCookie('tfNoticeHideExpiringNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + // The date the extension expires. + if (!$this->payload['expires_in']) + { + return false; + } + + // The days difference criteria must be met + if ($this->getDaysDifference(strtotime($this->payload['expires_in']), time()) > $this->expiring_notice_days) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Geolocation.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Geolocation.php new file mode 100644 index 00000000..27a6aa4b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Geolocation.php @@ -0,0 +1,85 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use \NRFramework\Extension; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Session\Session; + +class Geolocation extends Notice +{ + protected $notice_payload = [ + 'type' => 'warning', + 'class' => 'geolocation' + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + \NRFramework\Functions::loadLanguage('plg_system_tgeoip'); + } + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return Text::_('PLG_SYSTEM_TGEOIP_MAINTENANCE'); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + return sprintf(Text::_('NR_NOTICE_GEO_MAINTENANCE_DESC'), $this->extension_name); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = Uri::base() . 'index.php?option=com_ajax&format=raw&plugin=tgeoip&task=update-red&' . Session::getFormToken() . '=1&return=' . base64_encode($this->payload['current_url']); + + return '' . Text::_('NR_UPDATE_NOW') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, its been hidden + if ($this->factory->getCookie('tfNoticeHideGeolocationNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + if (!Extension::geoPluginNeedsUpdate()) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Notice.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Notice.php new file mode 100644 index 00000000..ece68c1e --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Notice.php @@ -0,0 +1,220 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use \NRFramework\Extension; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Layout\LayoutHelper; + +class Notice +{ + /** + * The notice payload. + * + * @var array + */ + protected $notice_payload = []; + + /** + * The payload. + * + * @var array + */ + protected $payload = [ + /** + * The extension's element we are showing notices. + * + * Example: com_rstbox, plg_system_acf + */ + 'ext_element' => '', + + /** + * The extension's main XML file location folder. + */ + 'ext_xml' => '', + + /** + * The extension type. + * + * Example: component, plugin, module, etc... + */ + 'ext_type' => 'component', + + /** + * The notice type. + */ + 'type' => '', + + /** + * The notice icon. + * + * Inner part of the SVG icon. + */ + 'icon' => '', + + /** + * An array containing classes attached to the notice wrapper HTML Element. + */ + 'class' => '', + + /** + * Whether the notice is dismissible. + */ + 'dismissible' => true, + + /** + * The notice title. + */ + 'title' => '', + + /** + * The notice description. + */ + 'description' => '', + + /** + * The tooltip text explaining this action. + */ + 'tooltip' => '', + + /** + * The notice actions. + */ + 'actions' => '' + ]; + + /** + * The extension name. + * + * @var String + */ + protected $extension_name; + + /** + * Factory. + * + * @var Factory + */ + protected $factory; + + public function __construct($payload = []) + { + $this->payload = array_merge($this->payload, $this->notice_payload, $payload); + + $this->factory = new \NRFramework\Factory(); + + $this->extension_name = Extension::getExtensionName($this->payload['ext_element']); + } + + /** + * Renders notice. + * + * @return string + */ + public function render() + { + if (!$this->canRun()) + { + return; + } + + $this->prepare(); + + return LayoutHelper::render('notices/notice', $this->payload, dirname(dirname(dirname(__DIR__))) . '/layouts'); + } + + /** + * Prepares the notice. + * + * @return void + */ + private function prepare() + { + // Set title + if (method_exists($this, 'getTitle')) + { + $this->payload['title'] = $this->getTitle(); + } + + // Set description + if (method_exists($this, 'getDescription')) + { + $this->payload['description'] = $this->getDescription(); + } + + // Set actions + if (method_exists($this, 'getActions')) + { + $this->payload['actions'] = $this->getActions(); + } + + if (isset($this->payload['type']) && !empty($this->payload['type'])) + { + // Set type of notice + $this->payload['class'] .= ' ' . $this->payload['type']; + + // Set icon + switch ($this->payload['type']) + { + case 'warning': + $icon = ''; + break; + case 'error': + $icon = ''; + break; + case 'info': + $icon = ''; + break; + case 'success': + $icon = ''; + break; + } + + $this->payload['icon'] = $icon; + } + + // Set whether dismissible + if ($this->payload['dismissible']) + { + $this->payload['class'] .= ' alert-dismissible'; + } + } + + /** + * Whether the notice can run. + * + * @return bool + */ + protected function canRun() + { + // If no title or description is given, do not run + if (empty($this->payload['title']) && empty($this->payload['description'])) + { + return false; + } + + return true; + } + + /** + * Returns the date difference between today and a given date in the future. + * + * @param string $date1 + * @param string $date2 + * + * @return string + */ + protected function getDaysDifference($date1, $date2) + { + return (int) round(($date1 - $date2) / (60 * 60 * 24)); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Outdated.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Outdated.php new file mode 100644 index 00000000..0a75bba5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Outdated.php @@ -0,0 +1,84 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Functions; +use \NRFramework\Extension; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Session\Session; + +class Outdated extends Notice +{ + /** + * How old the extension needs to be to be defined as "outdated". + * + * @var int + */ + private $oudated_notice_days_old = 120; + + protected $notice_payload = [ + 'type' => 'warning', + 'class' => 'outdated' + ]; + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return sprintf(Text::_('NR_EXTENSION_IS_OUTDATED'), $this->extension_name); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + return sprintf(Text::_('NR_OUTDATED_EXTENSION'), $this->extension_name, $this->oudated_notice_days_old); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + return '' . Text::_('NR_UPDATE_NOW') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, its been hidden + if ($this->factory->getCookie('tfNoticeHideOutdatedNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + if (!Extension::isOutdated($this->payload['ext_element'], $this->oudated_notice_days_old)) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Rate.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Rate.php new file mode 100644 index 00000000..1708bfb5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Rate.php @@ -0,0 +1,90 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Extension; + +class Rate extends Notice +{ + /** + * Define how old (in days) the extension needs to be since the installation date + * in order to display this notice. + * + * @var int + */ + private $rate_notice_days_old = 10; + + protected $notice_payload = [ + 'type' => 'info', + 'class' => 'rate' + ]; + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return sprintf(Text::_('NR_RATE'), $this->extension_name); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + return sprintf(Text::_('NR_RATE_NOTICE_EXTENSION_DESC'), $this->extension_name); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + return '' . Text::_('NR_I_ALREADY_DID') . ' + ' . Text::_('NR_WRITE_A_REVIEW') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, it's already hidden + if ($this->factory->getCookie('tfNoticeHideRateNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + // Get extension installation date + if (!$install_date = Extension::getInstallationDate($this->payload['ext_element'])) + { + return false; + } + + // If the extension is not old enough, do not show the rate notice + if ($this->getDaysDifference(time(), strtotime($install_date)) < $this->rate_notice_days_old) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/Update.php b/plugins/system/nrframework/NRFramework/Notices/Notices/Update.php new file mode 100644 index 00000000..334ea9d5 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/Update.php @@ -0,0 +1,90 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Extension; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Session\Session; + +class Update extends Notice +{ + protected $notice_payload = [ + 'type' => 'success', + 'class' => 'update', + 'current_version' => '', + 'latest_version' => '' + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + $this->payload['current_version'] = Extension::getVersion($this->payload['ext_xml']); + $this->payload['latest_version'] = Extension::getLatestVersion($this->payload['ext_xml']); + } + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return sprintf(Text::_('NR_EXTENSION_NEW_VERSION_IS_AVAILABLE'), $this->extension_name . ' v' . $this->payload['latest_version']); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + return sprintf(Text::_('NR_EXTENSION_NOTICE_DESC'), $this->extension_name); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = Extension::getProductURL($this->payload['ext_xml']) . '/changelog'; + + return '' . sprintf(Text::_('NR_YOUR_USING_VERSION'), $this->payload['current_version']) . ' + ' . Text::_('NR_VIEW_CHANGELOG') . ' + ' . Text::_('NR_UPDATE_NOW') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, its been hidden + if ($this->factory->getCookie('tfNoticeHideUpdateNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + if (!$this->payload['latest_version']) + { + return false; + } + + return version_compare($this->payload['latest_version'], $this->payload['current_version'], '>'); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/UpgradeToBundle.php b/plugins/system/nrframework/NRFramework/Notices/Notices/UpgradeToBundle.php new file mode 100644 index 00000000..c1279190 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/UpgradeToBundle.php @@ -0,0 +1,120 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Extension; + +class UpgradeToBundle extends Notice +{ + /** + * Define how old (in days) the extension needs to be since the installation date + * in order to display this notice. + * + * @var int + */ + private $upgrade_to_bundle_notice_days_old = 60; + + protected $notice_payload = [ + 'type' => 'success', + 'class' => 'upgradeToBundle' + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + $this->payload['tooltip'] = Text::_('NR_NOTICE_UPGRADE_TO_BUNDLE_TOOLTIP'); + } + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return Text::_('NR_UPGRADE_TO_BUNDLE'); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + return Text::_('NR_UPGRADE_TO_BUNDLE_NOTICE_DESC'); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = 'https://www.tassos.gr/bundle'; + + return '' . Text::_('NR_UPGRADE_NOW') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, its been hidden + if ($this->factory->getCookie('tfNoticeHideUpgradeToBundleNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + // Get license details for this extension + if ($details = \NRFramework\Notices\Helper::getExtensionDetails($this->payload['license_data'], $this->payload['ext_element'])) + { + // If we already have an active bundle plan, abort + if (isset($details['active']) && isset($details['plan']) && $details['active'] && strtolower($details['plan']) === 'bundle') + { + return false; + } + } + + // The user must have at least 2 installed tassos.gr extensions + if (Extension::getTotalInstalledExtensions() < 2) + { + return false; + } + + // User must have at least 1 paid subscription + if (Extension::getUserTotalPaidPlans($this->payload['license_data']) < 1) + { + return false; + } + + // Get extension installation date + if (!$install_date = Extension::getInstallationDate($this->payload['ext_element'])) + { + return false; + } + + // If the extension is not old enough, do not show the rate notice + if ($this->getDaysDifference(time(), strtotime($install_date)) < $this->upgrade_to_bundle_notice_days_old) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Notices/Notices/UpgradeToPro.php b/plugins/system/nrframework/NRFramework/Notices/Notices/UpgradeToPro.php new file mode 100644 index 00000000..d2958d35 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Notices/Notices/UpgradeToPro.php @@ -0,0 +1,104 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later + */ + +namespace NRFramework\Notices\Notices; + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; +use \NRFramework\Extension; + +class UpgradeToPro extends Notice +{ + /** + * Define how old (in days) the extension needs to be since the installation date + * in order to display this notice. + * + * @var int + */ + private $upgrade_to_pro_notice_days_old = 30; + + protected $notice_payload = [ + 'type' => 'success', + 'class' => 'upgradeToPro' + ]; + + public function __construct($payload = []) + { + parent::__construct($payload); + + $this->payload['tooltip'] = Text::_('NR_NOTICE_UPGRADE_TO_PRO_TOOLTIP'); + } + + /** + * Notice title. + * + * @return string + */ + protected function getTitle() + { + return sprintf(Text::_('NR_UPGRADE_TO_PRO_X_OFF'), 20); + } + + /** + * Notice description. + * + * @return string + */ + protected function getDescription() + { + return sprintf(Text::_('NR_UPGRADE_TO_PRO_NOTICE_DESC'), $this->extension_name); + } + + /** + * Notice actions. + * + * @return string + */ + protected function getActions() + { + $url = Extension::getTassosExtensionUpgradeURL($this->payload['ext_xml'], false); + + return '' . Text::_('NR_UPGRADE_NOW') . ''; + } + + /** + * Whether the notice can run. + * + * @return string + */ + protected function canRun() + { + // If cookie exists, its been hidden + if ($this->factory->getCookie('tfNoticeHideUpgradeToProNotice_' . $this->payload['ext_element']) === 'true') + { + return false; + } + + // If its already Pro, abort + if (Extension::isPro($this->payload['ext_xml'])) + { + return false; + } + + // Get extension installation date + if (!$install_date = Extension::getInstallationDate($this->payload['ext_element'])) + { + return false; + } + + // If the extension is not old enough, do not show the rate notice + if ($this->getDaysDifference(time(), strtotime($install_date)) < $this->upgrade_to_pro_notice_days_old) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/ConditionLexer.php b/plugins/system/nrframework/NRFramework/Parser/ConditionLexer.php new file mode 100644 index 00000000..254c69f8 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/ConditionLexer.php @@ -0,0 +1,612 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use NRFramework\Parser\Lexer; + +/** + * ConditionLexer + * + * Tokens: + * ------- + * and : 'AND' + * or : 'OR' + * quotedval : quotes ~(quotes)* quotes + * literal : ~(whitespace | quotes)+ + * ident : ('a'..'z' | 'A'..'Z' | '_' | '\-' | '\.')+ + * quotes : '\'' | '\"' + * comma : ',' + * l_paren : '(' + * r_paren : ')' + * + * negate_op : '!' + * equals : '=' | 'equals' + * contains : '*=' | 'contains' + * contains_any : 'containsAny' + * contains_all : 'containsAll' + * contains_only : 'containsOnly' + * ends_with : '$=' | 'endsWith' + * starts_with : '^=' | 'startsWith' + * lt : '<' | 'lt' | 'lowerThan' + * lte : '<=' | 'lte' | 'lowerThanEqual' + * gt : '>' | 'gt' | 'greaterThan' + * gte : '>=' | 'gte' | 'greaterThanEqual' + * empty : 'empty' + * + * param : '--' . ident + * whitespace : ' ' | '\r' | '\n' | '\t' + */ +class ConditionLexer extends Lexer +{ + /** + * ConditionLexer constructor + * + * @param string $input + */ + public function __construct($input) + { + parent::__construct($input); + // single char tokens + $this->tokens->addType('comma'); + $this->tokens->addType('quote'); + $this->tokens->addType('dquote'); + $this->tokens->addType('l_paren'); + $this->tokens->addType('r_paren'); + // operators + $this->tokens->addType('negate_op'); + $this->tokens->addType('equals'); + $this->tokens->addType('contains'); + $this->tokens->addType('contains_all'); + $this->tokens->addType('contains_any'); + $this->tokens->addType('contains_only'); + $this->tokens->addType('ends_with'); + $this->tokens->addType('starts_with'); + + $this->tokens->addType('lt'); + $this->tokens->addType('gt'); + $this->tokens->addType('lte'); + $this->tokens->addType('gte'); + $this->tokens->addType('empty'); + // logical operators + $this->tokens->addType('and'); + $this->tokens->addType('or'); + // values/literals/identifiers/parameters + $this->tokens->addType('quotedvalue'); + $this->tokens->addType('literal'); + $this->tokens->addType('ident'); + $this->tokens->addType('param'); + } + + /** + * Returns the next token from the input string + * + * @return NRFramework\Parser\Token + * @throws Exception + */ + public function nextToken() + { + while ($this->cur !== Lexer::EOF) + { + + if (preg_match('/\s+/', $this->cur)) + { + $this->whitespace(); + continue; + } + + switch ($this->cur) + { + // match tokens from single char predictions + case ',': + return $this->comma(); + case "'": + return $this->quotedValue("'"); + case '"': + return $this->quotedValue('"'); + case '=': + return $this->equals(); + case '!': + return $this->negate_op(); + case '*': + return $this->contains(); + case '$': + return $this->ends_with(); + case '^': + return $this->starts_with(); + case '<': + return $this->lt_or_lte(); + case '>': + return $this->gt_or_gte(); + case '(': + return $this->l_paren(); + case ')': + return $this->r_paren(); + case '-': + $this->mark(); + $next_chars = $this->consume(2); + if ($next_chars === '--') + { + $this->reset(); + return $this->param(); + } + $this->reset(); + + // match other tokens + default: + if (!$this->isValidChar()) + { + throw new Exceptions\SyntaxErrorException('Invalid character: ' . $this->cur); + } + $token = null; + + // try to match literal operators + $token = $this->literal_ops(); + if($token) + { + return $token; + } + + // try to match boolean operators + $token = $this->_and(); + if($token) + { + return $token; + } + + $token = $this->_or(); + if($token) + { + return $token; + } + + // if we get here the token is certainly a literal + $pos = $this->index; + $token = $this->literal(); + if ($token) + { + // check if the literal also qualifies to be an identifier + if ($this->isValidIdentifier($token->text)) + { + $token = $this->tokens->create('ident', $token->text, $pos); + } + return $token; + } + return null; + } + } + return $this->tokens->create('EOF', '', -1); + } + + /** + * Checks if a string qualifies to be an identifier + * + * @return bool + */ + protected function isValidIdentifier($text) + { + $ident_regex = '/(^[a-zA-Z\_]{1}$)|(^[a-zA-Z\_](?=([\w\-\.]*))([\w\-\.]*))/'; + return preg_match($ident_regex, $text); + } + + /** + * Check if the current character is valid for + * some matching rules (and, or, literal, ident) + * + * @return boolean + */ + protected function isValidChar() + { + $r = '/[^\s\'\",=\!\(\)\~\*\<\>\$\^]/'; + + return preg_match($r, $this->cur); + } + + /** + * literal : ~(whitespace | quotes)+ //one or more chars except whitespace and quotes + * + * @return Token|void + */ + protected function literal() + { + $pos = $this->index; + $buf = ''; + do + { + if (!$this->isValidChar()) + { + break; + } + $buf .= $this->cur; + $this->consume(); + } + while ($this->cur !== Lexer::EOF); + + if (strlen($buf) > 0) + { + return $this->tokens->create('literal', $buf, $pos); + } + } + + /** + * and : 'AND' + * + * @return Token|void + */ + protected function _and() + { + $pos = $this->index; + $this->mark(); + $buf = ''; + $buf .= $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if (preg_match('/and/', strtolower($buf))) + { + return $this->tokens->create('and', trim($buf), $pos); + } + + $this->reset(); + } + /** + * or : 'OR' + * + * @return Token|void + */ + public function _or() + { + $pos = $this->index; + $this->mark(); + $buf = ''; + $buf .= $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if (preg_match('/or/', strtolower($buf))) + { + return $this->tokens->create('or', trim($buf), $pos); + } + + $this->reset(); + } + + /** + * quotedval : quotes ~(quotes)* quotes + * + * @return Token|void + * @throws Exception + */ + protected function quotedValue($q) + { + $pos = $this->index; + $otherQuote = $q === '"' ? "'" : '"'; + $quote_queue = []; + $buf = ''; + + $quote_queue[] = $q; + $this->consume(); + while (!empty($quote_queue)) + { + if ($this->cur === Lexer::EOF) + { + throw new Exceptions\SyntaxErrorException('Missing quote at: ' . $buf); + } + + if ($this->cur === end($quote_queue)) + { + array_pop($quote_queue); + // if it's not the opening quote + if (!empty($quote_queue)) + { + $buf .= $this->cur; + } + } + else if ($this->cur === $otherQuote) + { + array_push($quote_queue, $otherQuote); + $buf .= $otherQuote; + } + else + { + $buf .= $this->cur; + } + $this->consume(); + } + return $this->tokens->create('quotedvalue', $buf, $pos); + } + + /** + * param : '--' . ident + * + * @return Token|void + */ + protected function param() + { + $pos = $this->index; + $this->mark(); + $buf = ''; + $buf .= $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '--') + { + $buf = ''; + do + { + if (!$this->isValidChar()) + { + break; + } + $buf .= $this->cur; + $this->consume(); + } + while ($this->cur !== Lexer::EOF); + + if (strlen($buf) > 0 && $this->isValidIdentifier($buf)) + { + return $this->tokens->create('param', $buf, $pos); + } + } + + $this->reset(); + } + + /** + * equals : '=' + * + * @return Token|void + */ + protected function equals() + { + $pos = $this->index; + $this->consume(); + return $this->tokens->create('equals', "=", $pos); + } + + protected function negate_op() + { + $pos = $this->index; + $this->consume(); + return $this->tokens->create('negate_op', "!", $pos); + } + + /** + * comma : ',' + * + * @return Token + */ + protected function comma() + { + $pos = $this->index; + $this->consume(); + return $this->tokens->create('comma', ",", $pos); + } + + /** + * l_paren : '(' + */ + protected function l_paren() + { + $pos = $this->index; + $this->consume(); + return $this->tokens->create('l_paren', '(', $pos); + } + + /** + * r_paren : ')' + */ + protected function r_paren() + { + $pos = $this->index; + $this->consume(); + return $this->tokens->create('r_paren', ')', $pos); + } + + /** + * contains: '*=' + * + * @return Token|void + */ + protected function contains() + { + $pos = $this->index; + $this->mark(); + $buf = $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '*=') + { + return $this->tokens->create('contains', "*=", $pos); + } + + $this->reset(); + } + + /** + * contains_word: '~=' + * + * @return Token|void + */ + protected function contains_word() + { + $pos = $this->index; + $this->mark(); + $buf = $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '~=') + { + return $this->tokens->create('contains_word', "~=", $pos); + } + + $this->reset(); + } + + + /** + * ends_with: '$=' + * + * @return Token|void + */ + protected function ends_with() + { + $pos = $this->index; + $this->mark(); + $buf = $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '$=') + { + return $this->tokens->create('ends_with', "$=", $pos); + } + + $this->reset(); + } + + /** + * starts_with: '$=' + * + * @return Token|void + */ + protected function starts_with() + { + $pos = $this->index; + $this->mark(); + $buf = $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '^=') + { + return $this->tokens->create('starts_with', "^=", $pos); + } + + $this->reset(); + } + + /** + * lt_or_lte: '<' | '<=' + * + * @return Token|void + */ + protected function lt_or_lte() + { + $pos = $this->index; + $this->mark(); + $buf = $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '<=') + { + return $this->tokens->create('lte', "<=", $pos); + } + else + { + $this->reset(); + $this->consume(); + return $this->tokens->create('lt', '<', $pos); + } + + $this->reset(); + } + + /** + * gt_or_gte: '>' | '>=' + * + * @return Token|void + */ + protected function gt_or_gte() + { + $pos = $this->index; + $this->mark(); + $buf = $this->cur; + $this->consume(); + $buf .= $this->cur; + $this->consume(); + + if ($buf === '>=') + { + return $this->tokens->create('gte', ">=", $pos); + } + else + { + $this->reset(); + $this->consume(); + return $this->tokens->create('gt', '>', $pos); + } + + $this->reset(); + } + + /** + * Literal Operators predictor + * + * @return Token|null + */ + protected function literal_ops() + { + $pos = $this->index; + $this->mark(); + $lit = $this->literal(); + + if ($lit) + { + switch (strtolower($lit->text)) + { + case 'equals': + return $this->tokens->create('equals', $lit->text, $pos); + case 'startswith': + return $this->tokens->create('starts_with', $lit->text, $pos); + case 'endswith': + return $this->tokens->create('ends_with', $lit->text, $pos); + case 'contains': + return $this->tokens->create('contains', $lit->text, $pos); + case 'containsall': + return $this->tokens->create('contains_all', $lit->text, $pos); + case 'containsany': + return $this->tokens->create('contains_any', $lit->text, $pos); + case 'containsonly': + return $this->tokens->create('contains_only', $lit->text, $pos); + case 'lt': + case 'lowerthan': + return $this->tokens->create('lt', $lit->text, $pos); + case 'lte': + case 'lowerthanequal': + return $this->tokens->create('lte', $lit->text, $pos); + case 'gt': + case 'greaterthan': + return $this->tokens->create('gt', $lit->text, $pos); + case 'gte': + case 'greaterthantequal': + return $this->tokens->create('gte', $lit->text, $pos); + case 'empty': + return $this->tokens->create('empty', $lit->text, $pos); + } + } + $this->reset(); + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/ConditionParser.php b/plugins/system/nrframework/NRFramework/Parser/ConditionParser.php new file mode 100644 index 00000000..ad5a3104 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/ConditionParser.php @@ -0,0 +1,358 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use NRFramework\Parser\Parser; +use NRFramework\Parser\ConditionLexer; +/** + * ConditionParser + * LL(1) recursive-decent parser + * Uses NRFramework\Parser\ConditionLexer as input source + * + * Grammar: + * -------- + * expr : condition (logic_op condition)* (option)* + * condition : {negate_op} alias (parameter)* | (alias|l_func) ({negate_op}? operator (values)? (parameter)* + * alias : {ident} + * values : value ({comma} value)* + * value : {quotedval} | ({literal} | {ident})+ + * func : {ident} {l_paren} values {r_paren} + * l_func : func + * r_func : func + * parameter : {param} ({equals} value)? + * option : {ident} ({equals} value)? + * logic_op : {and} | {or} + * operator : {equals} | {starts_with} | {ends_with} | {empty} | {contains} | {contains_any} | {contains_all}| {contains_only} | {lt} | {lte} | {gt} | {gte} + */ +class ConditionParser extends Parser +{ + /** + * Constructor + * + * @param ConditionLexer $input + */ + public function __construct(ConditionLexer $input) + { + parent::__construct($input, 2); + } + + /** + * value : {quotedval} | ({literal} | {ident})+ + * + * @return string + * @throws Exception + */ + public function value() + { + if ($this->lookahead[0]->type === 'quotedvalue') + { + $text = $this->lookahead[0]->text; + $this->match('quotedvalue'); + return $text; + } + else if ($this->lookahead[0]->type !== 'ident' && $this->lookahead[0]->type !== 'literal') + { + throw new \Exception("Syntax error in ConditionParser::value(); expecting 'ident' or 'literal'; found {$this->lookahead[0]}"); + } + + $text = $this->lookahead[0]->text; + $this->consume(); + + while ($this->lookahead[0]->type === 'ident' || $this->lookahead[0]->type === 'literal') + { + $text .= ' ' . $this->lookahead[0]->text; + $this->consume(); + } + + return $text; + } + + /** + * values : value ({comma} value)* + * + * @return array + */ + public function values() + { + $vals = []; + $vals[] = $this->value(); + + while ($this->lookahead[0]->type === 'comma') + { + $this->consume(); + $vals[] = $this->value(); + } + return $vals; + } + + /** + * func : {ident} {l_paren} values {r_paren} + * + */ + public function func() + { + $func_name = $this->lookahead[0]->text; + $this->match('ident'); + $this->match('l_paren'); + + if ($this->lookahead[0]->type === 'quotedvalue' || + $this->lookahead[0]->type === 'ident' || + $this->lookahead[0]->type === 'literal') + { + $func_args = $this->values(); + } + + $this->match('r_paren'); + + return ['func_name' => $func_name, 'func_args' => $func_args ?? []]; + } + + /** + * parameter : {param} ({equals} value)? + * + * @return string + */ + public function param() + { + $param = $this->lookahead[0]->text; + $value = true; + $this->match('param'); + + // If this is the 'context' parameter make sure that it appears as the last token + // if ($param === 'context') + // { + // $this->consume(); // consume the 'equals' operator + // $value = $this->value(); // expect a value + // if ($this->lookahead[0]->type !== 'EOF') + // { + // throw new \Exception("Syntax error in ConditionParser::param(); the 'context' parameter can only appear as the last token"); + // } + // } + // else + if ($this->isOperator($this->lookahead[0]->type)) + { + if ($this->lookahead[0]->type === 'equals') + { + $this->consume(); // consume the 'equals' operator + $value = $this->value(); // expect a value + } + else + { + // only the 'equals' operator is supported for the 'param' rule. + throw new \Exception("Syntax error in ConditionParser::param(); expecting 'equals', found {$this->lookahead[0]}"); + } + } + + return ['param' => $param, 'value' => $value]; + } + + /** + * alias : {ident} + * + * @return string + */ + public function alias() + { + $sel = $this->lookahead[0]->text; + $this->match('ident'); + return $sel; + } + + + /** + * condition : {negate_op} alias (parameter)* | alias ({negate_op}? operator values)? (parameter)* + * + * @return object + */ + public function condition() + { + $result = []; + $operator = ''; + $params = []; + $negate_op = false; + + if ($this->lookahead[0]->type === 'negate_op') + { + $this->match('negate_op'); + $operator = 'empty'; + $result['alias'] = $this->alias(); + } + else + { + if($this->lookahead[0]->type === 'ident' && $this->lookahead[1]->type === 'l_paren') + { + $l_func = $this->func(); + $result['l_func_name'] = $l_func['func_name']; + $result['l_func_args'] = $l_func['func_args']; + } + else + { + $result['alias'] = $this->alias(); + } + + if ($this->lookahead[0]->type === 'negate_op') + { + $this->match('negate_op'); + $negate_op = true; + // expect an operator after '!' + if (!$this->isOperator($this->lookahead[0]->type)) + { + throw new Exceptions\SyntaxErrorException("Expecting an 'operator' after '!', found {$this->lookahead[0]}"); + } + } + if ($this->isOperator($this->lookahead[0]->type)) + { + $operator = $this->operator(); + if($this->lookahead[0]->type === 'ident' && $this->lookahead[1]->type === 'l_paren') + { + $r_func = $this->func(); + $result['r_func_name'] = $r_func['func_name']; + $result['r_func_args'] = $r_func['func_args']; + } + else if ( + $this->lookahead[0]->type === 'quotedvalue' || + $this->lookahead[0]->type === 'ident' || + $this->lookahead[0]->type === 'literal' + ) + { + + + $values = $this->values(); + if (count($values) === 1) + { + $values = $values[0]; + } + $result['values'] = $values; + } + } + } + + while ($this->lookahead[0]->type === 'param') + { + $params[] = $this->param(); + } + + if (!$operator) { + $operator = 'empty'; + $negate_op = true; + } + + // + $_params = []; + foreach($params as $p) + { + $_params[$p['param']] = $p['value']; + } + + $result['operator'] = $operator; + $result['negate_op'] = $negate_op; + $result['params'] = $_params; + return $result; + } + + /** + * operator : {equals} | {starts_with} | {ends_with} | {empty} | {contains} | {contains_any} | {contains_all}| {contains_only} | {lt} | {lte} | {gt} | {gte} + * + * @return string + * @throws Exception + */ + public function operator() + { + if (!$this->isOperator($this->lookahead[0]->type)) + { + throw new Exceptions\SyntaxErrorException("Expecting an 'operator', found " . $this->lookahead[0]); + } + + $op = $this->lookahead[0]->type; + $this->consume(); + return $op; + } + + /** + * expr : condition ({logic_op} condition)* (option)* + * + * @return array The condition expression results + */ + public function expr() + { + $logic_op = 'and'; + $res = [ + 'conditions' => [$this->condition()], + 'logic_op' => 'and', + 'context' => null, + 'global_params' => [] + ]; + + if ($this->lookahead[0]->type === 'or') + { + $logic_op = 'or'; + } + + while ($this->lookahead[0]->type !== 'EOF') + { + $this->match($logic_op); + $res['conditions'][] = $this->condition(); + } + + $res['logic_op'] = $logic_op; + + // check the last parsed condition for global parameters + $globalParams = [ + 'debug', + 'dateformat', + 'context', + 'nopreparecontent', + 'excludebots' + ]; + + $last_params = $res['conditions'][count($res['conditions'])-1]['params']; + foreach(array_keys($last_params) as $param_key) + { + if (in_array(strtolower($param_key), $globalParams)) + { + $res['global_params'][strtolower($param_key)] = $last_params[$param_key]; + unset($res['conditions'][count($res['conditions'])-1]['params'][$param_key]); + } + } + // foreach ($last_params as $idx => $param) + // { + // if (in_array($param['param'], $globalParams)) + // { + // $res['global_params'][$param['param']] = $param['value']; + // unset($res['conditions'][count($res['conditions'])-1]['params'][$idx]); + // } + // } + return $res; + } + + /** + * Helper method that checks if the given Token is an operator. + */ + protected function isOperator($token_type) + { + + return in_array($token_type, [ + 'equals', + 'starts_with', + 'ends_with', + 'contains', + 'contains_any', + 'contains_all', + 'contains_only', + 'lt', + 'lte', + 'gt', + 'gte', + 'empty' + ]); + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/ConditionsEvaluator.php b/plugins/system/nrframework/NRFramework/Parser/ConditionsEvaluator.php new file mode 100644 index 00000000..d82d82d3 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/ConditionsEvaluator.php @@ -0,0 +1,804 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use DateTime; +use DateTimeZone; +use Exception; +use Joomla\CMS\Factory; + +class ConditionsEvaluator +{ + /** + * Payload associative array + * + * @var array + */ + protected $payload; + + /** + * Parsed conditions + * + * @var array + */ + protected $conditions; + + /** + * Framework Condition aliases + * + * @var array + */ + protected $condition_aliases; + + /** + * Debug flag + * + * @var bool + */ + protected $debug; + + /** + * @param array $conditions The parsed conditions + * @param array $payload Shortcode parser payload + */ + public function __construct($conditions, $payload = null, $debug = false) + { + $this->conditions = $conditions; + $this->payload = $payload; + $this->debug = $debug; + + $this->generateConditionAliasesMap(); + } + + /** + * @return array + */ + public function evaluate() : array + { + $results = []; + $caseSensitive = false; + + foreach($this->conditions as $condition) + { + // case sensitivity param + if (array_key_exists('caseSensitive', $condition['params'])) + { + $caseSensitive = strtolower($condition['params']['caseSensitive']) != 'false'; + } + + $result = [ + 'operator' => $condition['operator'], + 'params' => $condition['params'] + ]; + $l_value = null; + $r_value = null; + + if(array_key_exists('r_func_name', $condition)) + { + $r_value = $this->applyFunction($condition['r_func_name'], $condition['r_func_args']); + $result['r_func_name'] = $condition['r_func_name']; + $result['r_func_args'] = $condition['r_func_args']; + $result['r_func_val'] = $r_value; + } + else + { + $r_value = $condition['values'] ?? null; + } + + if (array_key_exists('alias', $condition) && $this->isPayloadCondition($condition['alias'])) + { + $l_value = $this->payload[$condition['alias']]; + $result = array_merge($result, $this->evaluatePayloadCondition($l_value, $r_value, $condition['operator'], $caseSensitive)); + $result['pass'] = $condition['negate_op'] ? !$result['pass'] : $result['pass']; + $result['actual_value'] = $this->payload[$condition['alias']]; + } + else if (array_key_exists('alias', $condition) && $this->isFrameworkCondition($condition['alias'])) + { + $result = array_merge($result, $this->evaluateFrameworkCondition($condition, $r_value)); + } + else if (array_key_exists('l_func_name', $condition)) + { + $l_value = $this->applyFunction($condition['l_func_name'], $condition['l_func_args']); + $result['l_func_name'] = $condition['l_func_name']; + $result['l_func_args'] = $condition['l_func_args']; + $result['l_func_val'] = $l_value; + + $result = array_merge($result, $this->evaluatePayloadCondition($l_value, $r_value, $condition['operator'], $caseSensitive)); + $result['pass'] = $condition['negate_op'] ? !$result['pass'] : $result['pass']; + } + // not a payload or framework condition with the 'empty' op + else if ($condition['operator'] === 'empty') + { + $result['pass'] = !$condition['negate_op']; + } + // + else + { + // Unknown condition + throw new Exceptions\InvalidConditionException($condition['alias']); + } + + $results[] = $result; + } + return $results; + } + + /** + * + */ + public function applyFunction($func_name, $args) + { + $arg_values = []; + foreach($args as $arg) + { + if ($this->isPayloadCondition($arg)) + { + $arg_values[] = $this->payload[$arg]; + } + else if ($this->isFrameworkCondition($arg)) + { + $conditions_helper = \NRFramework\Conditions\ConditionsHelper::getInstance(); + $framework_condition = $conditions_helper->getCondition($this->condition_aliases[strtolower($arg)]); + // Some framework condition don't implement the 'value()' method. + if (method_exists($framework_condition, 'value')) + { + $arg_values[] = $framework_condition->value(); + } + else + { + throw new Exceptions\ConditionValueException($arg); + } + } + else + { + $arg_values[] = $arg; + } + } + + switch(strtolower($func_name)) + { + case 'count': + return $this->funcCount($arg_values); + case 'today': + return $this->funcToday(); + case 'now': + return $this->funcNow(); + case 'date': + return $this->funcDate($arg_values); + case 'datediff': + return $this->funcDateDiff($arg_values); + default: + throw new Exceptions\UnknownFunctionException($func_name); + + } + } + + /** + * + */ + public function funcCount($args) + { + if (count($args) !== 1) + { + throw new Exception("count() accepts 1 argument. " . count($args) . " were given."); + } + + if (is_array($args[0])) + { + + return count($args[0]); + } + else if (is_string($args[0])) + { + return mb_strlen($args[0]); + } + else + { + throw new Exception("count() accepts only strings and arrays."); + } + + } + + /** + * + */ + public function funcToday() + { + return (new DateTime('today'))->format('Y-m-d'); + } + + /** + * + */ + public function funcNow() + { + return new DateTime('now'); + } + + /** + * + */ + public function funcDate($args) + { + if (count($args) < 1 || count($args) > 3) + { + throw new Exception("date() accepts between 1 and 3 arguments. " . count($args) . " were given."); + } + + if ($args[0] instanceof \DateTime || $args[0] instanceof \DateTimeImmutable) + { + return $args[0]; + } + + $date = $args[0]; + $format = null; + + if (count($args) > 1) + { + $format = $args[1] === 'null' ? null : $args[1]; + } + + $timezone = new \DateTimeZone($args[2] ?? Factory::getApplication()->get('offset','UTC')); + + if ($format) + { + return \DateTime::createFromFormat('!'.$format, $date, $timezone); + } + + return new \DateTime($date, $timezone); + } + + /** + * + */ + public function funcDateDiff($args) + { + if (count($args) != 2) + { + throw new Exception("dateDiff() accepts 2 arguments. " . count($args) . " were given."); + } + + $date1 = $this->convertToDateTime($args[0]); + $date2 = $this->convertToDateTime($args[1]); + + return abs($date1->diff($date2)->days); + } + + /** + * @var array $condition + * + * @return array Evaluation result + */ + protected function evaluatePayloadCondition($l_value, $r_value, $operator, $caseSensitive = false) : array + { + if (!$caseSensitive) + { + $l_value = $this->_lowercaseValues($l_value); + $r_value = $this->_lowercaseValues($r_value); + } + + $result = []; + switch($operator) + { + case 'equals': + $result = $this->evaluateEquals($l_value, $r_value); + break; + case 'starts_with': + $result = $this->evaluateStartsWith($l_value, $r_value); + break; + case 'ends_with': + $result = $this->evaluateEndsWith($l_value, $r_value); + break; + case 'contains': + $result = $this->evaluateContains($l_value, $r_value); + break; + case 'contains_any': + $result = $this->evaluateContainsAny($l_value, $r_value); + break; + case 'contains_all': + $result = $this->evaluateContainsAll($l_value, $r_value); + break; + case 'contains_only': + $result = $this->evaluateContainsOnly($l_value, $r_value); + break; + case 'lt': + $result = $this->evaluateLessThan($l_value, $r_value); + break; + case 'lte': + $result = $this->evaluateLessThanEquals($l_value, $r_value); + break; + case 'gt': + $result = $this->evaluateGreaterThan($l_value, $r_value); + break; + case 'gte': + $result = $this->evaluateGreaterThanEquals($l_value, $r_value); + break; + case 'empty': + $result = $this->evaluateEmpty($l_value); + break; + default: + throw new Exceptions\UnknownOperatorException($operator); + } + return $result; + } + + /** + * @var array $condition + * + * @return array Evaluation result + */ + public function evaluateFrameworkCondition($condition, $r_value) + { + $operator = $condition['operator']; + + // Certain framework operators only work on single values. + // Force fail if the parsed condition contains more than one value. + if (in_array($operator, [ + 'contains', + 'lt', 'lte', + 'gt', 'gte', + 'starts_with', + 'ends_with' + ])) + { + if (is_array($r_value) && !empty($r_value)) + { + throw new Exceptions\UnsupportedValueOperandException($operator, false); + } + } + + // + $conditions_helper = \NRFramework\Conditions\ConditionsHelper::getInstance(); + $result = ['actual_value' => null]; + + // Transform 'caseSensitive' parameter to 'ignoreCase' + if (array_key_exists('caseSensitive', $condition['params'])) + { + $condition['params']['ignoreCase'] = !$condition['params']['caseSensitive']; + } + + // Instantiate the framework condition + $framework_condition = $conditions_helper->getCondition( + $this->condition_aliases[strtolower($condition['alias'])], + $r_value, + $operator, + $condition['params'] + ); + + // Try to grab the actual condition's value if 'debug' is enabled. + if ($this->debug) + { + // Some framework conditions don't implement the 'value()' method. + if (method_exists($framework_condition, 'value')) + { + $result['actual_value'] = $framework_condition->value(); + } + } + + // Special handling for Date/Time framework conditions + if (in_array(strtolower($condition['alias']), ['date', 'time', 'datetime'])) + { + $pass = $this->evaluatePayloadCondition($framework_condition->value(), $r_value, $operator)['pass']; + } + // Check if the condition passes using the 'passOne()' helper method + else + { + $pass = $conditions_helper->passOne( + $this->condition_aliases[strtolower($condition['alias'])], + $r_value, + $operator, + $condition['params'] + ); + } + $result['pass'] = $condition['negate_op'] ? !$pass : $pass; + + return $result; + } + + /** + * Generates an array mapping Condition aliases to Condition class names + */ + protected function generateConditionAliasesMap() + { + $conditions_namespace = 'NRFramework\\Conditions\\Conditions\\'; + $dir_iterator = new \RecursiveDirectoryIterator(JPATH_PLUGINS . "/system/nrframework/NRFramework/Conditions/Conditions/"); + $iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::SELF_FIRST); + foreach ($iterator as $file) + { + $condition_class = str_replace(JPATH_PLUGINS . "/system/nrframework/NRFramework/Conditions/Conditions/", '', $file); + $condition_class = str_replace('.php', '', $condition_class); + $condition_class = str_replace('/', '\\', $condition_class); + if (class_exists($conditions_namespace . $condition_class)) + { + $this->condition_aliases[strtolower($file->getBasename('.php'))] = $condition_class; + if (property_exists($conditions_namespace . $condition_class, 'shortcode_aliases')) + { + foreach(($conditions_namespace . $condition_class)::$shortcode_aliases as $alias) + { + $this->condition_aliases[$alias] = $condition_class; + } + } + } + } + } + + /** + * + */ + protected function convertToDateTime($date, $format = null, $tz = null) + { + if ($tz == null) + { + $tz = Factory::getApplication()->getCfg('offset','UTC'); + } + + if ($date instanceof \DateTime || $date instanceof \DateTimeImmutable) + { + return $date; + } + + try + { + if ($format) + { + return DateTime::createFromFormat($format, $date, $tz); + } + + return new DateTime($date, new DateTimeZone($tz)); + } + catch (\Throwable $t) + { + return null; + } + } + + /** + * + */ + protected function isPayloadCondition($alias) + { + return $this->payload && array_key_exists($alias, $this->payload); + } + + /** + * + */ + protected function isFrameworkCondition($alias) + { + return array_key_exists(strtolower($alias), $this->condition_aliases); + } + + /** + * @return array Evaluation result + */ + protected function evaluateEquals($l_value, $r_value) : array + { + // are we comparing arrays? + if (is_array($l_value)) + { + return $this->evaluateContainsAny($l_value, $r_value); + } + + if (is_numeric($r_value)) + { + return ['pass' => $l_value == $r_value]; + } + + + // check if we are comparing dates + $l_date = $this->convertToDateTime($l_value); + $r_date = $this->convertToDateTime($r_value); + if($l_date && $r_date) + { + if (is_string($r_value) && !preg_match("/\d{1,2}:\d{1,2}(:\d{1,2})?/", $r_value)) + { + $l_date->setTime(0,0); + $r_date->setTime(0,0); + } + return [ + 'l_eval' => $l_date, + 'r_eval' => $r_date, + 'pass' => $l_date == $r_date + ]; + } + + // generic equality test + return ['pass' => $l_value == $r_value]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateStartsWith($l_value, $r_value) : array + { + if (!is_string($l_value)) + { + throw new Exceptions\UnsupportedOperatorException('startsWith', $l_value, false); + } + + if (!is_string($r_value)) + { + throw new Exceptions\UnsupportedValueOperandException('startsWith', false); + } + + return ['pass' => $this->_starts_with($l_value, $r_value)]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateEndsWith($l_value, $r_value) : array + { + if (!is_string($l_value)) + { + throw new Exceptions\UnsupportedOperatorException('endsWith', $l_value, false); + } + + if (!is_string($r_value)) + { + throw new Exceptions\UnsupportedValueOperandException('endsWith', false); + } + + return ['pass' => $this->_ends_with($l_value, $r_value)]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateContains($l_value, $r_value) : array + { + if (!is_string($l_value)) + { + throw new Exceptions\UnsupportedOperatorException('contains', $l_value, false); + } + + if (!is_string($r_value)) + { + throw new Exceptions\UnsupportedValueOperandException('contains', false); + } + + return ['pass' => strlen($l_value) > 0 && strpos($l_value, $r_value) !== false]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateContainsAny($l_value, $r_value) : array + { + if (!is_array($l_value)) + { + throw new Exceptions\UnsupportedOperatorException('containsAny', $l_value, true); + } + + $r_value = (array) $r_value; + return ['pass' => !empty(array_intersect($l_value, $r_value))]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateContainsAll($l_value, $r_value) : array + { + if (!is_array($l_value)) + { + throw new Exceptions\UnsupportedOperatorException('containsAll', $l_value, true); + } + + $r_value = (array) $r_value; + return ['pass' => count(array_intersect($l_value, $r_value)) == count($r_value)]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateContainsOnly($l_value, $r_value) : array + { + if (!is_array($l_value)) + { + throw new Exceptions\UnsupportedOperatorException('containsOnly', $l_value, true); + } + + $r_value = (array) $r_value; + return ['pass' => count(array_diff($l_value, $r_value)) == 0]; + } + + /** + * @return array Evaluation result + */ + protected function evaluateLessThan($l_value, $r_value) : array + { + if (is_numeric($r_value)) + { + return ['pass' => $l_value < $r_value]; + } + + // check if we are comparing dates + $l_date = $this->convertToDateTime($l_value); + $r_date = $this->convertToDateTime($r_value); + if($l_date && $r_date) + { + if (is_string($r_value) && !preg_match("/\d{1,2}:\d{1,2}(:\d{1,2})?/", $r_value)) + { + $l_date->setTime(0,0); + $r_date->setTime(0,0); + } + return [ + 'l_eval' => $l_date, + 'r_eval' => $r_date, + 'pass' => $l_date < $r_date + ]; + } + + throw new Exceptions\SyntaxErrorException("The 'lessThan' operator accepts only numeric values and dates."); + } + + /** + * @return array Evaluation result + */ + protected function evaluateLessThanEquals($l_value, $r_value) : array + { + if (is_numeric($r_value)) + { + return ['pass' => $l_value <= $r_value]; + } + + // check if we are comparing dates + $l_date = $this->convertToDateTime($l_value); + $r_date = $this->convertToDateTime($r_value); + if($l_date && $r_date) + { + if (is_string($r_value) && !preg_match("/\d{1,2}:\d{1,2}(:\d{1,2})?/", $r_value)) + { + $l_date->setTime(0,0); + $r_date->setTime(0,0); + } + return [ + 'l_eval' => $l_date, + 'r_eval' => $r_date, + 'pass' => $l_date <= $r_date + ]; + } + + throw new Exceptions\SyntaxErrorException("The 'lessThanEquals' operator accepts only numeric values and dates."); + } + + /** + * @return array Evaluation result + */ + protected function evaluateGreaterThan($l_value, $r_value) : array + { + if (is_numeric($r_value)) + { + return ['pass' => $l_value > $r_value]; + } + + // check if we are comparing dates + $l_date = $this->convertToDateTime($l_value); + $r_date = $this->convertToDateTime($r_value); + if($l_date && $r_date) + { + if (is_string($r_value) && !preg_match("/\d{1,2}:\d{1,2}(:\d{1,2})?/", $r_value)) + { + $l_date->setTime(0,0); + $r_date->setTime(0,0); + } + return [ + 'l_eval' => $l_date, + 'r_eval' => $r_date, + 'pass' => $l_date > $r_date + ]; + } + + throw new Exceptions\SyntaxErrorException("The 'greaterThan' operator accepts only numeric values and dates."); + } + + /** + * @return array Evaluation result + */ + protected function evaluateGreaterThanEquals($l_value, $r_value) : array + { + if (is_numeric($r_value)) + { + return ['pass' => $l_value >= $r_value]; + } + + // check if we are comparing dates + $l_date = $this->convertToDateTime($l_value); + $r_date = $this->convertToDateTime($r_value); + if($l_date && $r_date) + { + if (is_string($r_value) && !preg_match("/\d{1,2}:\d{1,2}(:\d{1,2})?/", $r_value)) + { + $l_date->setTime(0,0); + $r_date->setTime(0,0); + } + return [ + 'l_eval' => $l_date, + 'r_eval' => $r_date, + 'pass' => $l_date >= $r_date + ]; + } + + throw new Exceptions\SyntaxErrorException("The 'greaterThanEquals' operator accepts only numeric values and dates."); + } + + /** + * @return array Evaluation result + */ + protected function evaluateEmpty($payload_value) : array + { + // $payload_value = $this->payload[$payload_key]; + + if (is_array($payload_value)) + { + return ['pass' => empty($payload_value)]; + } + else if(is_string($payload_value)) + { + $payload_value = trim($payload_value); + return ['pass' => empty($payload_value) || $payload_value == 'false']; + } + else if(is_bool($payload_value)) + { + return ['pass' => !$payload_value]; + } + + return ['pass' => is_null($payload_value)]; + } + + /** + * @return bool + */ + protected function _starts_with($haystack, $needle) + { + return strlen($needle) > 0 && strncmp($haystack, $needle, strlen($needle)) === 0; + } + + /** + * @return bool + */ + protected function _ends_with($haystack, $needle) + { + return strlen($needle) > 0 && substr($haystack, -strlen($needle)) === (string)$needle; + } + + /** + * @return bool + */ + protected function _contains($haystack, $needle) + { + return strlen($needle) > 0 && strpos($haystack, $needle) !== false; + } + + /** + * @return string|array + */ + protected function _lowercaseValues($value) + { + if (is_array($value)) + { + foreach($value as $idx => $val) + { + if (is_string($val)) + { + $value[$idx] = strtolower($val); + } + } + } + else if(is_string($value)) + { + $value = strtolower($value); + } + + return $value; + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/ConditionValueException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/ConditionValueException.php new file mode 100644 index 00000000..18178569 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/ConditionValueException.php @@ -0,0 +1,20 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class ConditionValueException extends \Exception +{ + public function __construct($condition_name) + { + parent::__construct("008 - Condition Value Error: The Condition '" . $condition_name . "' does not return a value."); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/InvalidConditionException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/InvalidConditionException.php new file mode 100644 index 00000000..eb33e008 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/InvalidConditionException.php @@ -0,0 +1,20 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class InvalidConditionException extends \Exception +{ + public function __construct($condition_name) + { + parent::__construct("002 - Invalid Condition: The condition '" . $condition_name . "' does not exist."); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/SyntaxErrorException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/SyntaxErrorException.php new file mode 100644 index 00000000..1bacef2b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/SyntaxErrorException.php @@ -0,0 +1,20 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class SyntaxErrorException extends \Exception +{ + public function __construct($message) + { + parent::__construct("001 - Syntax Error: " . $message); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnknownFunctionException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnknownFunctionException.php new file mode 100644 index 00000000..048597d9 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnknownFunctionException.php @@ -0,0 +1,20 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class UnknownFunctionException extends \Exception +{ + public function __construct($func_name) + { + parent::__construct("007 - Unknown Function: " . $func_name); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnknownOperatorException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnknownOperatorException.php new file mode 100644 index 00000000..bcb1d91b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnknownOperatorException.php @@ -0,0 +1,20 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class UnknownOperatorException extends \Exception +{ + public function __construct($operator) + { + parent::__construct("003 - Unknown Comparison Operator: " . $operator); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnsupportedOperatorException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnsupportedOperatorException.php new file mode 100644 index 00000000..e1cfd374 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnsupportedOperatorException.php @@ -0,0 +1,21 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class UnsupportedOperatorException extends \Exception +{ + public function __construct($operator, $condition_name, $accepts_multi_values) + { + $message = 'The Comparison Operator "' . $operator . '" can only be used with Condition Operands that return ' . ($accepts_multi_values ? 'multiple values.' : 'single values.'); + parent::__construct("005 - Unsupported Comparison Operator: " . $message); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnsupportedValueOperandException.php b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnsupportedValueOperandException.php new file mode 100644 index 00000000..4604fc9b --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Exceptions/UnsupportedValueOperandException.php @@ -0,0 +1,21 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser\Exceptions; + +defined('_JEXEC') or die; + +class UnsupportedValueOperandException extends \Exception +{ + public function __construct($operator, $accepts_multi_values) + { + $message = 'The Comparison Operator "' . $operator . '" can only be used with ' . ($accepts_multi_values ? 'multiple values.' : 'single values.'); + parent::__construct("006 - Unsupported Value Operand: " . $message); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/NRFramework/Parser/Lexer.php b/plugins/system/nrframework/NRFramework/Parser/Lexer.php new file mode 100644 index 00000000..e4f3dd52 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Lexer.php @@ -0,0 +1,215 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use NRFramework\Parser\Tokens; + +/** + * Lexer base class + * + * TODO: Rename to Tokenizer?? + */ +abstract class Lexer +{ + /** + * EOF character + */ + const EOF = -1; + + /** + * Tokens instance + * + * @var NRFramework\Parser\Tokens + */ + protected $tokens = null; // Tokens instance + + /** + * Input string + * + * @var string + */ + protected $input; + + /** + * Input string length + */ + protected $length; + + /** + * The index of the current character + * in the input string + * + * @var integer + */ + protected $index = 0; + + /** + * Current character in input string + * + * @var string + */ + protected $cur; + + /** + * A Mark(position) inside the input string. + * Used when matching ahead of the 'current' character + * + * @var integer + */ + protected $mark = 0; + + /** + * Holds the Lexer's state + * + * @var object + */ + protected $state; + + /** + * Lexer constructor + * + * @param string $input + */ + public function __construct($input) + { + $this->input = $input; + $this->length = strlen($input); + $this->cur = $this->length >= 1 ? $this->input[0] : Lexer::EOF; + $this->tokens = new Tokens(); + + // inititalize state + $this->state = new \StdClass(); + $this->state->skip_whitespace = true; + $this->state->tokenize_content = true; + } + + /** + * Returns the next token from the input string. + * + * @return NRFramework\Parser\Token + */ + abstract function nextToken(); + + /** + * Moves n characters ahead in the input string. + * Returns all n characters. + * Detects "end of file". + * + * @param integer $n Number of characters to advance + * @return string The n previous characters + */ + public function consume($n = 1) + { + $prev = ''; + for ($i=0; $i < $n; $i++) + { + $prev .= $this->cur; + if ( ($this->index + 1) >= $this->length) + { + $this->cur = Lexer::EOF; + break; + } + else + { + $this->index++; + $this->cur = $this->input[$this->index]; + } + } + + return $prev; + } + + /** + * Sets the skip_whitespce state + * + * @param boolean $skip + * @return void + */ + public function setSkipWhitespaceState($skip = true) + { + $this->state->skip_whitespace = $skip; + } + + /** + * Sets the tokenize_content state + * + * @param bool + * @return void + */ + public function setTokenizeContentState($state = true) + { + $this->state->tokenize_content = $state; + } + + /** + * Gets the tokenize_content state + * + * @param bool + * @return bool + */ + public function getTokenizeContentState() + { + return $this->state->tokenize_content; + } + + /** + * Marks the current index + * + * @return void + */ + public function mark() + { + $this->mark = $this->index; + } + + /** + * Reset index to previously marked position (or at the start of the stream if not marked) + * + * @return void + */ + public function reset() + { + $this->index = $this->mark; + $this->cur = $this->input[$this->index]; + $this->mark = 0; + } + + /** + * Get the token types array from the Tokens instance + * + * @return void + */ + public function getTokensTypes() + { + return $this->tokens->getTypes(); + } + + /** + * Returns the current position in the input stream + * + * @return integer + */ + public function getStreamPosition() + { + return $this->index; + } + + /** + * whitespace : (' '|'\t'|'\n'|'\r') + * Ignores any whitespace while advancing + * @return null + */ + protected function whitespace() + { + while (preg_match('/\s+/', $this->cur)) $this->consume(); + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/Parser.php b/plugins/system/nrframework/NRFramework/Parser/Parser.php new file mode 100644 index 00000000..da43d651 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/Parser.php @@ -0,0 +1,152 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use NRFramework\Parser\Lexer; +use NRFramework\Parser\RingBuffer; + +/** + * Parser base class + * LL(k) recursive-decent parser with backtracking support + */ +abstract class Parser +{ + /** + * Lexer instance (feeds the parser with tokens) + * + * @var NRFramework\Parser\Lexer + */ + protected $input = null; + + /** + * Ring buffer of the next k tokens + * from the input stream + * + * @var RingBuffer + */ + protected $lookahead = null; + + /** + * k: Number of lookahead tokens + * + * @var int + */ + protected $k; + + /** + * Array(stack) containing the current + * contents of the lookahead buffer when + * marking the position of the stream + * + * @var array + */ + protected $lookahead_history = null; + + /** + * Lexer constructor + * + * @param Lexer $input + * @param integer $k, number of lookahead tokens + */ + public function __construct(Lexer $input, $k = 1) + { + if (!is_integer($k) || ($k < 1)) + { + throw new \InvalidArgumentException('Parser: $k must be greater than 0!'); + } + $this->k = $k; + $this->input = $input; + $this->lookahead_history = []; + + // initialize lookahead buffer + $this->resetBuffer(); + } + + /** + * Checks the type of the next token. + * Advances the position in the token stream. + * + * @param string $type + * @return void + * + * @throws Exception + */ + public function match($type) + { + if ($this->lookahead[0]->type === $type) + { + $this->consume(); + return; + } + + throw new Exceptions\SyntaxErrorException('Expecting token ' . $type . ', found ' . $this->lookahead[0]); + } + + /** + * Retrieves the next token from the input stream + * and add it to the buffer. + * + * @return void + */ + public function consume() + { + $this->lookahead[] = $this->input->nextToken(); + } + + /** + * Marks the position in the token stream + * + * @return void + */ + public function mark() + { + array_push($this->lookahead_history, $this->lookahead); + $this->input->mark(); + } + + /** + * Reset to a previously marked position + * in the token stream + * + * @return void + */ + public function reset() + { + $this->input->reset(); + + // reset lookahead buffer if not marked + if (empty($this->lookahead_history)) + { + $this->resetBuffer(); + } + // normal reset + else + { + $this->lookahead = array_pop($this->lookahead_history); + } + } + + /** + * Resets and refills the lookahead buffer starting + * from the current position in the token stream + * + * @return void + */ + protected function resetBuffer() + { + $this->lookahead = new RingBuffer($this->k); + for ($i=0; $i < $this->k; $i++) + { + $this->consume(); + } + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/RingBuffer.php b/plugins/system/nrframework/NRFramework/Parser/RingBuffer.php new file mode 100644 index 00000000..bc9f67aa --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/RingBuffer.php @@ -0,0 +1,227 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +/** + * RingBuffer + * + * A circular buffer of fixed length. + * This class essentially implements a fixed-size FIFO stack but with + * "convenient" accessor methods compared to manually handling a vanilla PHP array. + * + * Used by NRFramework\Parser\Parser and NRFramework\Parser\Lexer. + */ +class RingBuffer implements \Countable, \ArrayAccess, \Iterator +{ + /** + * Iterator position + * @var int + */ + protected $iterator_position = 0; + + /** + * Position of the next element + * @var integer + */ + protected $position = 0; + + /** + * Contents buffer + * @var \SplFixedArray + */ + protected $buffer; + + /** + * Size of the ring buffer + * @var int + */ + protected $size; + + /** + * RingBuffer constructor + * + * Handles arguments through 'func_get_args' (gotta love PHP) + * + * @param int $size Size of the ring buffer + * @param array $val Initial values + */ + public function __construct() + { + //argument checks + $argv = func_get_args(); + $argc = count($argv); + + switch($argc) + { + case 1: + // array + if (is_array($argv[0])) + { + $this ->size = count($argv[0]); + $this->buffer = \SplFixedArray::fromArray($argv[0]); + } + // size + else if (is_numeric($argv[0])) + { + if ($argv[0] < 1) + { + throw new \InvalidArgumentException('RingBuffer ctor: size must be greater than zero'); + } + $size = (integer)$argv[0]; + $this->buffer = \SplFixedArray::fromArray(array_fill(0, $size, null)); + $this->size = $size; + } + else + { + throw new \InvalidArgumentException("RingBuffer ctor: arguments must be an array ,a numeric size or both"); + } + break; + case 2: + if(is_array($argv[0]) && is_numeric($argv[1])) + { + if ($argv[1] < 1) + { + throw new \InvalidArgumentException('RingBuffer ctor: size must be greater than zero'); + } + $arr_size = count($argv[0]); + $size = (integer)$argv[1]; + if ($arr_size == $size) + { + $this->buffer = \SplFixedArray::fromArray($argv[0]); + $this->size = $size; + } + else if ($arr_size > $size) + { + $this->buffer = \SplFixedArray::fromArray(array_slice($argv[0], 0, $size)); + $this->size = $size ; + } + else // $arr_size < $size + { + $this->buffer = \SplFixedArray::fromArray(array_merge($argv[0], array_fill(0, $size - $arr_size, null))); + $this->size = $size ; + $this->position = $arr_size ; + } + } + else + { + throw new \InvalidArgumentException("RingBuffer ctor: arguments must be an array ,a numeric size or both"); + } + break; + default: + throw new \InvalidArgumentException('RingBuffer ctor: no arguments given'); + } + } + + /** + * Returns the internal buffer as an array + * + * @return \SplFixedArray + */ + public function buffer() + { + return $this->buffer; + } + + /** + * 'Countable' interface methods + */ + + /** + * Returns the size of the buffer + * + * @return int + */ + public function count() : int + { + return $this->size; + } + + /** + * 'ArrayAccess' interface methods + */ + + protected function offsetOf($offset) + { + return ($this->position + $offset) % $this->size; + } + + + public function offsetExists($offset) : bool + { + return ($offset >= 0) && ($offset < $this->size); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + // if (!$this->offsetExists($offset)) + if (($offset < 1) && ($offset >= $this->size)) + { + throw new \OutOfBoundsException("RingBuffer: invalid offset $offset."); + } + return $this->buffer[($this->position + $offset) % $this->size]; + } + + public function offsetUnset($offset) : void + { + if (!$this->offsetExists($offset)) + { + throw new \OutOfBoundsException("RingBuffer: invalid offset $offset."); + } + $this->buffer[$this->offsetOf($offset)] = null; + } + + public function offsetSet($offset, $value) : void + { + if ($offset === null) + { + $this->buffer[$this->position] = $value; + $this->position = ($this->position + 1)%$this->size; + } + else if ($this->offsetExists($offset)) + { + $this->buffer[$this->offsetOf($offset)] = $value; + } + else + { + throw new \OutOfBoundsException("RingBuffer: invalid offset $offset."); + } + } + + /** + * 'Iterator' interface methods + */ + public function rewind() : void + { + $this->iterator_position = 0; + } + + public function current() : mixed + { + return $this->buffer[$this->offsetOf($this->iterator_position)]; + } + + public function key() : mixed + { + return $this->iterator_position; + } + + public function next() : void + { + $this->iterator_position++; + } + + public function valid() : bool + { + return ($this->iterator_position >= 0) && ($this->iterator_position < $this->size); + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/ShortcodeLexer.php b/plugins/system/nrframework/NRFramework/Parser/ShortcodeLexer.php new file mode 100644 index 00000000..fde1b9a1 --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/ShortcodeLexer.php @@ -0,0 +1,359 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use NRFramework\Parser\Lexer; + +/** + * ShortcodeLexer + * + * Tokenizes a string using the following grammar. + * Acts as the input "stream" for NRFramework\Parser\ShortcodeParser + * + * Tokens: + * ------- + * sc_open : shortcode tag opening character(s), default: { + * sc_close : shortcode tag closing character(s), default: } + * if_keyword : if keyword, default: 'if' + * endif_keyword : endif keyword, default: '/if' + * text : any character sequence + * text_preserved : any character sequence with quoted values preserved + * whitespace : ' ' | '\r' | '\n' | '\t' + */ +class ShortcodeLexer extends Lexer +{ + /** + * Shortcode opening character(s) (default: {) + * + * @var string + */ + protected $sc_open_char; + + /** + * Shortcode closing character(s) (default: }) + * + * @var string + */ + protected $sc_close_char; + + /** + * if keyword (default: 'if') + * + * @var string + */ + protected $if_keyword; + + /** + * endif keyword (default: '/if') + * + * @var string + */ + protected $endif_keyword; + + /** + * ShortcodeLexer constructor + * + * @param string $input + * @param object $options + */ + public function __construct($input, $options = null) + { + parent::__construct($input); + + $this->tokens->addType('sc_open'); + $this->tokens->addType('sc_close'); + $this->tokens->addType('if_keyword'); + $this->tokens->addType('endif_keyword'); + $this->tokens->addType('char'); + + $this->sc_open_char = $options->tag_open_char ?? '{'; + $this->sc_close_char = $options->tag_close_char ?? '}'; + $this->if_keyword = $options->if_keyword ?? 'if'; + $this->endif_keyword = '/' . $this->if_keyword; + } + + /** + * Returns the next token from the input string + * + * @return RestrictContent\Parser\Token + */ + public function nextToken() + { + static $if_flag = false; + + while ($this->cur !== Lexer::EOF) + { + if ($this->state->skip_whitespace && preg_match('/\s+/', $this->cur)) + { + $this->whitespace(); + continue; + } + + if ($this->predictScOpen()) + { + $this->setTokenizeContentState(false); + $this->setSkipWhitespaceState(true); + return $this->sc_open(); + } + else if ($this->predictScClose()) + { + $this->setTokenizeContentState(true); + $this->setSkipWhitespaceState(false); + return $this->sc_close(); + } + // check for if/endif + else if ($this->predictIf(false)) + { + return $this->_if(); + } + else if ($this->predictEndif(false)) + { + return $this->_endif(); + } + // check for text + else { + $preserve_quoted_values = !$this->getTokenizeContentState(); + $token = $this->text($preserve_quoted_values); + return $token; + } + } + // return EOF token at the end of stream + return $this->tokens->create('EOF', '', -1); + } + + /** + * Predicts an upcoming 'if' keyword from the input stream + * + * @param bool $reset Reset to the marked position when the keyword is found + * @return bool + */ + protected function predictIf($reset = true) + { + $this->mark(); + $tmp = $this->consume(2); + if ($tmp === $this->if_keyword) + { + if ($reset) + { + $this->reset(); + } + return true; + } + + $this->reset(); + return false; + } + + /** + * Predicts an upcoming 'endif' keyword from the input stream + * + * @param bool $reset Reset to the marked position when the keyword is found + * @return bool + */ + protected function predictEndif($reset = true) + { + $this->mark(); + $tmp = $this->consume(3); + if ($tmp === $this->endif_keyword) + { + if ($reset) + { + $this->reset(); + } + return true; + } + + $this->reset(); + return false; + } + + /** + * Predicts any upcoming keyword + * + * @return bool + */ + protected function predictKeywords() + { + return $this->predictIf() || $this->predictEndif(); + } + + /** + * Predicts any upcoming special character + * + * @return bool + */ + protected function predictSpecialChars() + { + return $this->predictScOpen() || $this->predictScClose(); + } + + /** + * Predicts upcoming shortcode opening character(s), default: { + * + * @return bool + */ + protected function predictScOpen() + { + $sc_length = \strlen($this->sc_open_char); + $res = false; + $this->mark(); + $tmp = $this->consume($sc_length); + if ($tmp === $this->sc_open_char) + { + $res = true; + } + + $this->reset(); + return $res; + } + + /** + * Predicts upcoming shortcode closing character(s), default: { + * + * @return bool + */ + protected function predictScClose() + { + $sc_length = \strlen($this->sc_close_char); + $res = false; + $this->mark(); + $tmp = $this->consume($sc_length); + if ($tmp === $this->sc_close_char) + { + $res = true; + } + + $this->reset(); + return $res; + } + + /** + * sc_open : shortcode tag opening character, default: { + * + * @return Token + */ + protected function sc_open() + { + $pos = $this->index; + $length = \strlen($this->sc_open_char); + $this->consume($length); + return $this->tokens->create('sc_open', $this->sc_open_char, $pos); + } + + /** + * sc_close : shortcode tag closeing character, default: } + * + * @return Token + */ + protected function sc_close() + { + $pos = $this->index; + $length = \strlen($this->sc_close_char); + $this->consume($length); + return $this->tokens->create('sc_close', $this->sc_close_char, $pos); + } + + /** + * if_keyword, default: 'if' + * + * @return Token + */ + protected function _if() + { + return $this->tokens->create('if_keyword', $this->if_keyword, $this->index - \strlen($this->if_keyword)); + } + + /** + * endif_keyword, default: '/if' + * + * @return Token + */ + protected function _endif() + { + return $this->tokens->create('endif_keyword', $this->endif_keyword, $this->index - \strlen($this->endif_keyword)); + } + + + /** + * text : any character sequence + * + * @param bool $preserve Preserve keywords and special characters inside quotes + * @return Token + */ + protected function text($preserve) + { + if ($preserve) + { + return $this->text_preserved(); + } + + $pos = $this->index; + $buf = ''; + while ($this->cur !== Lexer::EOF) + { + if ($this->predictKeywords() || $this->predictSpecialChars()) + { + return $this->tokens->create('text', $buf, $pos); + } + $buf .= $this->cur; + $this->consume(); + } + + return $this->tokens->create('EOF', '', -1); + } + + /** + * text_preserved : any character sequence with quoted values preserved + * + * @return Token + */ + protected function text_preserved() + { + $quote_queue = []; + $buf = ''; + $pos = $this->index; + + while ($this->cur !== Lexer::EOF) + { + // manage quote parsing + if ($this->cur == '"' || $this->cur == "'") + { + if ($this->cur == end($quote_queue)) + { + // remove last added quote + array_pop($quote_queue); + } + else + { + // add quote to the queue + array_push($quote_queue, $this->cur); + } + } + // End parsing when any keyword or special character is found + // handles quoted values + if ($this->predictKeywords() || $this->predictSpecialChars()) + { + // return the expression's text if no quotes are open + if (empty($quote_queue)) + { + return $this->tokens->create('text_preserved', trim($buf), $pos); + } + } + + // add current character to buffer + $buf .= $this->cur; + $this->consume(); + } + + return $this->tokens->create('EOF', '', -1); + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/ShortcodeParser.php b/plugins/system/nrframework/NRFramework/Parser/ShortcodeParser.php new file mode 100644 index 00000000..19f6041d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/ShortcodeParser.php @@ -0,0 +1,250 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +defined('_JEXEC') or die; + +use NRFramework\Parser\Parser; +use NRFramework\Parser\ShortcodeLexer; + +/** + * ShortcodeParser + * LL(k = 3) recursive-decent parser + * Uses ShortcodeLexer as the input/token source + * + * Parses the following grammar: + * ------------------------------ + * expr := shortcode* <-- Top-level expression (i.e. 0 or more shortcodes) + * shortcode := ifexpr content endifexpr + * ifexpr := {sc_open} {if_keyword} condition {sc_close} + * endifexpr := {sc_open} {end_ifkeyword} {sc_close} + * content := any text until endifexpr + * condition := any text with preserved quoted values until {sc_close} + */ +class ShortcodeParser extends Parser +{ + /** + * shortcode opening character (e.g.: '{') + * @var string + */ + protected $sc_open; + + /** + * shortcode closing character (e.g.: '}') + * @var string + */ + protected $sc_close; + + /** + * Log parsing errors? + * + * @var boolean + */ + protected $log_errors; + + /** + * The shortcode's position in the input text + * Used only for error logging + * + * @var int + */ + protected $shortcode_position; + + /** + * Constructor + * + * @param ShortcodeLexer $input + * @param Object $options + */ + public function __construct(ShortcodeLexer $input, $options = null) + { + // k = 3, look 3 tokens ahead at most + parent::__construct($input, 3); + $this->sc_open = $options->tag_open_char ?? '{'; + $this->sc_close = $options->tag_close_char ?? '}'; + $this->log_errors = $options->log_errors ?? false; + $this->shortcode_position = $options->shortcode_position ?? 0; + } + + /** + * Returns the correct content for replacement + * If $pass = null will return an array with [content, else-content] + * + * Call this method when the conditions have been parsed and + * the result is known + * + * @param string $content + * @param bool $pass + * @return string + */ + public function getReplacement($content, $pass) + { + //construct the else-tag, e.g. {else} + $elseTag = $this->sc_open . 'else' . $this->sc_close; + + // split content on the else-tag + $replacement = $content; + $elseReplacement = ''; + + if (strpos($content, $elseTag) !== false) + { + list($replacement, $elseReplacement) = explode($elseTag, $content, 2); + } + + return $pass === null ? + [$replacement, $elseReplacement] : + ($pass ? $replacement : $elseReplacement); + } + + /** + * Top-level parsing method + * + * Rule: + * expr := shortcode* + * + * @return array + */ + public function expr() + { + $shortcodes = []; + while ($this->lookahead[0]->type != 'EOF') + { + $position = $this->lookahead[0]->position; + try + { + if ($this->lookahead[0]->type == 'sc_open' && + $this->lookahead[1]->type == 'if_keyword') + { + $shortcodes[] = $this->shortcode(); + } + else + { + // this token is not part of a shortcode, keep going... + $this->consume(); + } + } + catch (\Exception $error) + { + // something went horribly wrong while parsing a shortcode + // log the error and continue + $msg = $error->getMessage(); + $near_text = $this->lookahead[0]->position + $this->shortcode_position; + $shortcodes[] = (object) [ + 'position' => $position, + 'parser_error' => $msg, + 'near_text' => $near_text + ]; + $this->consume(); + } + } + return $shortcodes; + } + + /** + * Rule + * + * shortcode := ifexpr content endifexpr + * + * @return object + */ + protected function shortcode() + { + $start = $this->lookahead[0]->position + $this->shortcode_position; + $conditions = $this->ifexpr(); + $content = $this->content(); + $length = $this->lookahead[2]->position + $this->shortcode_position - $start + 1; + $this->endifexpr(); + + return (object) [ + 'start' => $start, + 'length' => $length, + 'conditions' => $conditions, + 'content' => $content + ]; + } + + /** + * Rule: + * ifexpr : {sc_open} {if_keyword} condition {sc_close} + * + * @return string + */ + protected function ifexpr() + { + $this->match('sc_open'); + $this->match('if_keyword'); + $condition_text = $this->condition(); + $this->match('sc_close'); + + return $condition_text; + } + + /** + * Rule: + * endifexpr := {sc_open} {end_ifkeyword} {sc_close} + * + * @return void + */ + protected function endifexpr() + { + $this->match('sc_open'); + $this->match('endif_keyword'); + $this->match('sc_close'); + } + + /** + * Rule: + * condition := any text with preserved quoted values until {sc_close} + * + * @return string + * @throws Exception + */ + protected function condition() + { + $buf = ''; + while ($this->lookahead[0]->type !== 'EOF') + { + if ($this->lookahead[0]->type === 'sc_close') + { + return htmlspecialchars_decode($buf); + } + $buf .= $this->lookahead[0]->text; + $this->consume(); + } + + throw new Exceptions\SyntaxErrorException('Invalid condition expression.'); + } + + /** + * Rule: + * content := any text until an endif expression + * + * @return string + * @throws Exception + */ + protected function content() + { + $buf = ''; + while ($this->lookahead[0]->type !== 'EOF') + { + if ($this->lookahead[0]->type === 'sc_open' && + $this->lookahead[1]->type === 'endif_keyword' && + $this->lookahead[2]->type === 'sc_close') + { + return $buf; + } + + $buf .= $this->lookahead[0]->text; + $this->consume(); + } + + throw new Exceptions\SyntaxErrorException('Missing shortcode tag character.'); + } +} diff --git a/plugins/system/nrframework/NRFramework/Parser/ShortcodeParserHelper.php b/plugins/system/nrframework/NRFramework/Parser/ShortcodeParserHelper.php new file mode 100644 index 00000000..5e80c38d --- /dev/null +++ b/plugins/system/nrframework/NRFramework/Parser/ShortcodeParserHelper.php @@ -0,0 +1,359 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +namespace NRFramework\Parser; + +use DateTime; +use DateTimeImmutable; + +defined('_JEXEC') or die; + +class ShortcodeParserHelper { + /** + * Input text buffer + * + * @var string + */ + protected $text; + + /** + * ShortcodeParser options + * + * @var object + */ + protected $parser_options; + + /** + * Parsing payload, associative array + * + * @var array + */ + protected $payload; + + /** + * Parsing context + * + * @var string + */ + protected $context; + + /** + * List of areas in the content that should not be parsed for Smart Tags. + * + * @var array + */ + private $protectedAreas = []; + + /** + * @param string $text Text buffer (stored as a reference) + * @param object $parser_options ShortcodeParser options + * @param array|null $payload Parser payload + * @param string|null $context Parser context + */ + public function __construct(&$text, $payload = null, $parser_options = null, $context = null) + { + $this->text =& $text; + $this->payload = $payload; + $this->context = $context; + + if (!$parser_options) + { + $this->parser_options = new \stdClass(); + $this->parser_options->tag_open_char = '{'; + $this->parser_options->tag_close_char = '}'; + $this->parser_options->if_keyword = 'if'; + $this->parser_options->log_errors = 'false'; + } + else + { + $this->parser_options = $parser_options; + } + } + + /** + * The text being parsed may contain sensitive information and areas where parsing of shortcodes, such as + getDocument(); + $doc->addScriptOptions('media-picker', [ + 'images' => array_map( + 'trim', + explode( + ',', + ComponentHelper::getParams('com_media')->get( + 'image_extensions', + 'bmp,gif,jpg,jpeg,png' + ) + ) + ) + ]); + + $wam = $doc->getWebAssetManager(); + $wam->useScript('webcomponent.media-select'); + + Text::script('JFIELD_MEDIA_LAZY_LABEL'); + Text::script('JFIELD_MEDIA_ALT_LABEL'); + Text::script('JFIELD_MEDIA_ALT_CHECK_LABEL'); + Text::script('JFIELD_MEDIA_ALT_CHECK_DESC_LABEL'); + Text::script('JFIELD_MEDIA_CLASS_LABEL'); + Text::script('JFIELD_MEDIA_FIGURE_CLASS_LABEL'); + Text::script('JFIELD_MEDIA_FIGURE_CAPTION_LABEL'); + Text::script('JFIELD_MEDIA_LAZY_LABEL'); + Text::script('JFIELD_MEDIA_SUMMARY_LABEL'); + } +} + +// Use admin gallery manager path if browsing via backend +$gallery_manager_path = Factory::getApplication()->isClient('administrator') ? 'administrator/' : ''; + +// Javascript files should always load as they are used to populate the Gallery Manager via Dropzone +HTMLHelper::script('plg_system_nrframework/dropzone.min.js', ['relative' => true, 'version' => 'auto']); +HTMLHelper::script('plg_system_nrframework/widgets/gallery/manager_init.js', ['relative' => true, 'version' => 'auto']); +HTMLHelper::script('plg_system_nrframework/widgets/gallery/manager.js', ['relative' => true, 'version' => 'auto']); + +if ($load_stylesheet) +{ + HTMLHelper::stylesheet('plg_system_nrframework/widgets/gallerymanager.css', ['relative' => true, 'version' => 'auto']); +} + +$tags = isset($tags) ? $tags : []; + +$ai_icon = ''; +?> + + + \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/gallerymanager/edit.php b/plugins/system/nrframework/layouts/widgets/gallerymanager/edit.php new file mode 100644 index 00000000..4856ec2d --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/gallerymanager/edit.php @@ -0,0 +1,63 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +extract($displayData); +?> + \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/gallerymanager2/default.php b/plugins/system/nrframework/layouts/widgets/gallerymanager2/default.php new file mode 100644 index 00000000..a6a56202 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/gallerymanager2/default.php @@ -0,0 +1,320 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +extract($displayData); + +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Layout\LayoutHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; + +if (!$disabled) +{ + HTMLHelper::_('bootstrap.modal'); + + if (strpos($css_class, 'ordering-default') !== false) + { + HTMLHelper::script('plg_system_nrframework/vendor/sortable.min.js', ['relative' => true, 'version' => 'auto']); + } + + // Required in the front-end for the media manager to work + if (!defined('nrJ4')) + { + HTMLHelper::_('behavior.modal'); + // Front-end editing: The below script is required for front-end media library selection to work as its missing from parent window when called + if (Factory::getApplication()->isClient('site')) + { + ?> + + getDocument(); + $doc->addScriptOptions('media-picker', [ + 'images' => array_map( + 'trim', + explode( + ',', + ComponentHelper::getParams('com_media')->get( + 'image_extensions', + 'bmp,gif,jpg,jpeg,png' + ) + ) + ) + ]); + + $wam = $doc->getWebAssetManager(); + $wam->useScript('webcomponent.media-select'); + + Text::script('JFIELD_MEDIA_LAZY_LABEL'); + Text::script('JFIELD_MEDIA_ALT_LABEL'); + Text::script('JFIELD_MEDIA_ALT_CHECK_LABEL'); + Text::script('JFIELD_MEDIA_ALT_CHECK_DESC_LABEL'); + Text::script('JFIELD_MEDIA_CLASS_LABEL'); + Text::script('JFIELD_MEDIA_FIGURE_CLASS_LABEL'); + Text::script('JFIELD_MEDIA_FIGURE_CAPTION_LABEL'); + Text::script('JFIELD_MEDIA_LAZY_LABEL'); + Text::script('JFIELD_MEDIA_SUMMARY_LABEL'); + } +} + +// Use admin gallery manager path if browsing via backend +$gallery_manager_path = Factory::getApplication()->isClient('administrator') ? 'administrator/' : ''; + +// Javascript files should always load as they are used to populate the Gallery Manager via Dropzone +HTMLHelper::script('plg_system_nrframework/dropzone.min.js', ['relative' => true, 'version' => 'auto']); +HTMLHelper::script('plg_system_nrframework/widgets/gallery/manager_init.js', ['relative' => true, 'version' => 'auto']); +HTMLHelper::script('plg_system_nrframework/widgets/gallery/manager.js', ['relative' => true, 'version' => 'auto']); + +if ($load_stylesheet) +{ + HTMLHelper::stylesheet('plg_system_nrframework/widgets/gallerymanager.css', ['relative' => true, 'version' => 'auto']); +} + +$tags = isset($tags) ? $tags : []; + +$ai_icon = ''; +?> + + + \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/gallerymanager2/edit.php b/plugins/system/nrframework/layouts/widgets/gallerymanager2/edit.php new file mode 100644 index 00000000..b548aade --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/gallerymanager2/edit.php @@ -0,0 +1,63 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +extract($displayData); +?> + \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/gallerymanager2/media_library.php b/plugins/system/nrframework/layouts/widgets/gallerymanager2/media_library.php new file mode 100644 index 00000000..ad1ae600 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/gallerymanager2/media_library.php @@ -0,0 +1,24 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; +use Joomla\CMS\Uri\Uri; + +extract($displayData); +?> + + \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/googlemap/default.php b/plugins/system/nrframework/layouts/widgets/googlemap/default.php new file mode 100644 index 00000000..5b583d13 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/googlemap/default.php @@ -0,0 +1,28 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +extract($displayData); + +$options = isset($options) ? $options : $displayData; + +if ($options['load_css_vars'] && !empty($options['custom_css'])) +{ + Factory::getDocument()->addStyleDeclaration($options['custom_css']); +} +?> +
+
+
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapaddress/default.php b/plugins/system/nrframework/layouts/widgets/mapaddress/default.php new file mode 100644 index 00000000..89e0f599 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapaddress/default.php @@ -0,0 +1,63 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +extract($displayData); + +// Skip if all address details are empty +if (empty(array_filter($address))) +{ + return; +} + +$address_details_html = null; + +switch ($layout_type) +{ + // Default Layout + case 'default': + $address_details_html = \NRFramework\Helpers\Widgets\MapAddress::getDefaultAddressDetailsLayout($address, $showAddressDetails); + break; + + // Custom Layout + case 'custom': + if (!empty(trim($custom_layout))) + { + $st = new \NRFramework\SmartTags(); + $st->add($address, 'address.'); + + // Add labels + foreach ($address as $key => $value) + { + $st->add([$key . '.label' => Text::_('NR_' . strtoupper($key))], 'address.'); + } + + // Add map + if ($map) + { + $st->add(['address.map' => $map]); + } + + $address_details_html = nl2br(\Joomla\CMS\HTML\HTMLHelper::_('content.prepare', $st->replace($custom_layout))); + } + break; +} + +if (!$address_details_html) +{ + return; +} + +echo $map_location === 'above' ? $map_html . $address_details_html : $address_details_html . $map_html; \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapaddresseditor/default.php b/plugins/system/nrframework/layouts/widgets/mapaddresseditor/default.php new file mode 100644 index 00000000..cb7a71bf --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapaddresseditor/default.php @@ -0,0 +1,72 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Language\Text; + +extract($displayData); + +$mapWrapperClass = ''; +if (!$map) +{ + $mapWrapperClass .= ' no-map'; +} +?> +
+ + + + + +
+ +
+
+ $show) + { + if ($key === 'address') + { + continue; + } + + $lang_key = 'NR_' . strtoupper($key); + $placeholder = $lang_key; + + if ($key === 'address') + { + $placeholder .= '_ADDRESS_HINT'; + } + + $input_type = $show ? 'text' : 'hidden'; + $visibility_class = $show ? 'visible' : 'hidden'; + ?> +
+
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/add_marker_modal.php b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/add_marker_modal.php new file mode 100644 index 00000000..7f6052ab --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/add_marker_modal.php @@ -0,0 +1,43 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Form\Form; + +$form_source = new SimpleXMLElement(' +
+
+ + + +
+
+'); + +$form = Form::getInstance($options['name'] . '[add_marker]', $form_source->asXML(), ['control' => $options['name'] . '[add_marker]']); + +echo HTMLHelper::_('bootstrap.renderModal', 'tfMapEditorMarkerAddModal', [ + 'title' => Text::_('NR_ADD_MARKER'), + 'modalWidth' => '40', + 'footer' => '' +], $form->renderFieldset('add_marker_modal')); \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/default.php b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/default.php new file mode 100644 index 00000000..881476e5 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/default.php @@ -0,0 +1,138 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; + +extract($displayData); + +$options = isset($options) ? $options : $displayData; + +if ($options['load_css_vars'] && !empty($options['custom_css'])) +{ + Factory::getDocument()->addStyleDeclaration($options['custom_css']); +} +?> +
+ + + + + +
+ + +
+ + '; + require 'settings.php'; + echo '
'; + } + if ($options['show_markers_list']) + { + ?> +
+ +
+ $marker) + { + ?> +
+
+ . +
+
+ +
+ +
+ + + + + +
+ +
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/edit_marker_modal.php b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/edit_marker_modal.php new file mode 100644 index 00000000..d1350ec9 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/edit_marker_modal.php @@ -0,0 +1,60 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Form\Form; + +$form_source = new SimpleXMLElement(' +
+
+ + + + + + + +
+
+'); + +$form = Form::getInstance($options['name'], $form_source->asXML(), ['control' => $options['name']]); + +echo HTMLHelper::_('bootstrap.renderModal', 'tfMapEditorMarkerEditModal', [ + 'title' => Text::_('NR_EDIT_MARKER'), + 'modalWidth' => '50', + 'footer' => '' +], $form->renderFieldset('edit_marker_modal')); \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/settings.php b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/settings.php new file mode 100644 index 00000000..355b2427 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapaddresseditorview/settings.php @@ -0,0 +1,40 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Form\Form; + +require_once JPATH_SITE . '/plugins/system/nrframework/fields/tfaddresslookup.php'; + +$form_source = new SimpleXMLElement(' +
+
+ +
+
+'); + +$form = Form::getInstance($options['name'], $form_source->asXML(), ['control' => $options['name']]); +$form->bind([ + 'address' => [ + 'coordinates' => $options['value'], + 'address' => !empty($options['address']) ? $options['address'] : $options['value'] + ] +]); + +echo $form->renderFieldset('mapeditor_field_settings'); \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/mapeditor/default.php b/plugins/system/nrframework/layouts/widgets/mapeditor/default.php new file mode 100644 index 00000000..0a0ec7bc --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/mapeditor/default.php @@ -0,0 +1,32 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; + +extract($displayData); + +$options = isset($options) ? $options : $displayData; + +if ($options['load_css_vars'] && !empty($options['custom_css'])) +{ + Factory::getDocument()->addStyleDeclaration($options['custom_css']); +} +?> +
+
+ + /> + +
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/openstreetmap/default.php b/plugins/system/nrframework/layouts/widgets/openstreetmap/default.php new file mode 100644 index 00000000..ebf15f15 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/openstreetmap/default.php @@ -0,0 +1,28 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +extract($displayData); + +$options = isset($options) ? $options : $displayData; + +if ($options['load_css_vars'] && !empty($options['custom_css'])) +{ + Factory::getDocument()->addStyleDeclaration($options['custom_css']); +} +?> +
+
+
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/rangeslider/default.php b/plugins/system/nrframework/layouts/widgets/rangeslider/default.php new file mode 100644 index 00000000..6593a28b --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/rangeslider/default.php @@ -0,0 +1,76 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Factory; + +extract($displayData); + +if (!$readonly && !$disabled) +{ + HTMLHelper::script('plg_system_nrframework/widgets/slider.js', ['relative' => true, 'version' => 'auto']); +} + +if ($load_stylesheet) +{ + HTMLHelper::stylesheet('plg_system_nrframework/widgets/slider.css', ['relative' => true, 'version' => 'auto']); +} + +if ($load_css_vars) +{ + Factory::getDocument()->addStyleDeclaration(' + .nrf-slider-wrapper.' . $id . ' { + --base-color: ' . $base_color . '; + --progress-color: ' . $color . '; + --input-bg-color: ' . $input_bg_color . '; + --input-border-color: ' . $input_border_color . '; + --thumb-shadow-color: ' . $color . '26' . '; + } + '); +} +?> +
+ + data-base-color="" + data-progress-color="" + style="background: linear-gradient(to right, 0%, %, %, 100%)" + + + disabled + + + aria-label="" + + /> + + readonly + + /> +
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/rating/default.php b/plugins/system/nrframework/layouts/widgets/rating/default.php new file mode 100644 index 00000000..cb104c3a --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/rating/default.php @@ -0,0 +1,41 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Factory; + +extract($displayData); + +if (!$readonly && !$disabled) +{ + HTMLHelper::script('plg_system_nrframework/widgets/rating.js', ['relative' => true, 'version' => 'auto']); +} + +if ($load_stylesheet) +{ + HTMLHelper::stylesheet('plg_system_nrframework/widgets/rating.css', ['relative' => true, 'version' => 'auto']); +} + +if ($load_css_vars) +{ + Factory::getDocument()->addStyleDeclaration(' + .nrf-rating-wrapper.' . $id . ' { + --rating-selected-color: ' . $selected_color . '; + --rating-unselected-color: ' . $unselected_color . '; + --rating-size: ' . $size . 'px; + } + '); +} + +echo $this->sublayout($half_ratings ? 'half' : 'full', $displayData); \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/rating/default/full.php b/plugins/system/nrframework/layouts/widgets/rating/default/full.php new file mode 100644 index 00000000..d6b9f85c --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/rating/default/full.php @@ -0,0 +1,50 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +extract($displayData); + +?> +
+ 1 ? 's' : '')); + + if ($value && $i <= $value) + { + $label_class = 'iconFilled'; + } + ?> + + checked + + + disabled + + /> + + +
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/rating/default/half.php b/plugins/system/nrframework/layouts/widgets/rating/default/half.php new file mode 100644 index 00000000..a5809203 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/rating/default/half.php @@ -0,0 +1,81 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +extract($displayData); + +$css_class .= ' ' . $size; +?> +
+ 1 ? 's' : '')); + ?> + + checked + + + disabled + + /> + + +
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/selfhostedvideo/default.php b/plugins/system/nrframework/layouts/widgets/selfhostedvideo/default.php new file mode 100644 index 00000000..f8ae3bef --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/selfhostedvideo/default.php @@ -0,0 +1,46 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Language\Text; + +extract($displayData); + +$options = isset($options) ? $options : $displayData; + +if (!$options['video'] || !is_array($options['video'])) +{ + return; +} + +$attributes = array_filter(array( + isset($options['controls']) && $options['controls'] ? 'controls' : '', + isset($options['loop']) && $options['loop'] ? 'loop' : '', + isset($options['mute']) && $options['mute'] ? 'muted' : '', + isset($options['autoplay']) && $options['autoplay'] ? 'autoplay playsinline' : '' +)); + +$type_ext = $options['video']['ext'] === 'mov' ? 'mp4' : $options['video']['ext']; +?> +
+
> + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/signature/default.php b/plugins/system/nrframework/layouts/widgets/signature/default.php new file mode 100644 index 00000000..5283ffdb --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/signature/default.php @@ -0,0 +1,53 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Factory; + +extract($displayData); + +HTMLHelper::script('plg_system_nrframework/vendor/signature.min.js', ['relative' => true, 'version' => 'auto']); +HTMLHelper::script('plg_system_nrframework/widgets/signature.js', ['relative' => true, 'version' => 'auto']); + +if ($load_stylesheet) +{ + HTMLHelper::stylesheet('plg_system_nrframework/widgets/signature.css', ['relative' => true, 'version' => 'auto']); +} + +if ($load_css_vars) +{ + Factory::getDocument()->addStyleDeclaration(' + .nrf-widget.signature.' . $id . ' { + --width: ' . $width . '; + --height: ' . $height . '; + --input-border-width: ' . $border_width . '; + --input-border-color: ' . $border_color . '; + --input-border-radius: ' . $border_radius . '; + --input-background-color: ' . $background_color . '; + --line-color: ' . (is_null($line_color) ? $border_color : $line_color) . '; + } + '); +} +?> +
+
+
+ + +
+ + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/slideshow/default.php b/plugins/system/nrframework/layouts/widgets/slideshow/default.php new file mode 100644 index 00000000..657b974f --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/slideshow/default.php @@ -0,0 +1,112 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Layout\FileLayout; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Factory; + +extract($displayData); + +if (!$items || !is_array($items) || !count($items)) +{ + return; +} + +if ($load_stylesheet) +{ + HTMLHelper::script('plg_system_nrframework/vendor/swiper.min.js', ['relative' => true, 'version' => 'auto']); + HTMLHelper::script('plg_system_nrframework/widgets/slideshow.js', ['relative' => true, 'version' => 'auto']); + + HTMLHelper::stylesheet('plg_system_nrframework/vendor/swiper.min.css', ['relative' => true, 'version' => 'auto']); + HTMLHelper::stylesheet('plg_system_nrframework/widgets/slideshow.css', ['relative' => true, 'version' => 'auto']); +} + +if ($load_css_vars) +{ + Factory::getDocument()->addStyleDeclaration(' + .nrf-widget.tf-slideshow-wrapper.' . $id . ', + .nrf-widget.tf-slideshow-thumbs-wrapper.' . $id . ' { + --swiper-theme-color: ' . $theme_color . '; + } + '); +} +?> + + + +
+
+ $item) + { + ?>
<?php echo strip_tags($item['alt']); ?>
+
+ +
+
+ +
+ + +render($displayData); +} +?> \ No newline at end of file diff --git a/plugins/system/nrframework/layouts/widgets/video/default.php b/plugins/system/nrframework/layouts/widgets/video/default.php new file mode 100644 index 00000000..fe701cb3 --- /dev/null +++ b/plugins/system/nrframework/layouts/widgets/video/default.php @@ -0,0 +1,51 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +extract($displayData); + +$options = isset($options) ? $options : $displayData; + +if (!$options['value']) +{ + return; +} + +if ($options['load_css_vars']) +{ + $atts = []; + + // Add cover image + if (isset($options['coverImageType']) && isset($options['coverImage']) && in_array($options['coverImageType'], ['auto', 'custom']) && !empty($options['coverImage'])) + { + $atts['video-cover-image'] = $options['coverImage']; + } + + $css = \NRFramework\Helpers\CSS::cssVarsToString($atts, '.nrf-widget.tf-video.' . $options['id']); + + Factory::getDocument()->addStyleDeclaration($css); +} +?> +
+
+
> +
+
+ + +
+ +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/nrframework.php b/plugins/system/nrframework/nrframework.php new file mode 100644 index 00000000..007c3af2 --- /dev/null +++ b/plugins/system/nrframework/nrframework.php @@ -0,0 +1,495 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined( '_JEXEC' ) or die( 'Restricted access' ); + +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\String\StringHelper; +use NRFramework\HTML; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Registry\Registry; + +// Initialize Tassos Library +require_once __DIR__ . '/autoload.php'; + +class plgSystemNRFramework extends CMSPlugin +{ + /** + * Auto load plugin language + * + * @var boolean + */ + protected $autoloadLanguage = true; + + /** + * The Joomla Application object + * + * @var object + */ + protected $app; + + /** + * Update UpdateSites after the user has entered a Download Key + * + * @param string $context The component context + * @param string $table + * @param boolean $isNew + * + * @return void + */ + public function onExtensionAfterSave($context, $table, $isNew) + { + // Run only on Tassos Framework edit form + if ( + $this->app->isClient('site') + || $context != 'com_plugins.plugin' + || $table->element != 'nrframework' + || !isset($table->params) + ) + { + return; + } + + // Set Download Key & fix Update Sites + $upds = new NRFramework\Updatesites(); + $upds->update(); + } + + /** + * Handling of PRO for extensions + * Throws a notice message if the Download Key is missing before downloading the package + * + * @param string &$url Update Site URL + * @param array &$headers + */ + public function onInstallerBeforePackageDownload(&$url, &$headers) + { + $uri = Uri::getInstance($url); + $host = $uri->getHost(); + + // This is not a Tassos.gr extension + if (strpos($host, 'tassos.gr') === false) + { + return true; + } + + // If it's a Free version. No need to check for the Download Key. + if (strpos($url, 'free') !== false) + { + return true; + } + + // This is a Pro version. Let's validate the Download Key. + $download_id = $this->params->get('key', ''); + + // Append it to the URL + if (!empty($download_id)) + { + $uri->setVar('dlid', $download_id); + $url = $uri->toString(); + return true; + } + + $this->app->enqueueMessage('To be able to update the Pro version of this extension via the Joomla updater, you will need enter your Download Key in the settings of the Tassos Framework System Plugin'); + return true; + } + + /** + * Listens to AJAX requests on ?option=com_ajax&format=raw&plugin=nrframework + * + * @return void + */ + public function onAjaxNrframework() + { + Session::checkToken('request') or jexit(Text::_('JINVALID_TOKEN')); + + // Check if we have a valid task + $task = $this->app->input->get('task', null); + + $non_admin_tasks = [ + 'include' + ]; + + // Only in backend + if (!in_array(strtolower($task), $non_admin_tasks) && !$this->app->isClient('administrator')) + { + return; + } + + // Check if we have a valid method task + $taskMethod = 'ajaxTask' . $task; + + if (!method_exists($this, $taskMethod)) + { + die('Task not found'); + } + + $this->$taskMethod(); + } + + /** + * Handles the Widgets AJAX requests. + * + * @return void + */ + public function onAjaxWidgets() + { + Session::checkToken('request') or jexit(Text::_('JINVALID_TOKEN')); + + $widget = $this->app->input->get('widget', null); + + $class = '\NRFramework\Widgets\\' . $widget; + + if (!class_exists($class)) + { + return; + } + + $task = $this->app->input->get('task'); + + (new $class)->onAjax($task); + } + + private function ajaxTaskInclude() + { + $input = $this->app->input; + + $file = $input->get('file'); + $path = JPATH_SITE . '/' . $input->get('path', '', 'RAW'); + $class = $input->get('class'); + + $file_to_include = $path . $file . '.php'; + + if (!file_exists($file_to_include)) + { + die('FILE_ERROR'); + } + + @include_once $file_to_include; + + if (!class_exists($class)) + { + die('CLASS_ERROR'); + } + + if (!method_exists($class, 'onAJAX')) + { + die('METHOD_ERROR'); + } + + (new $class())->onAJAX($input->getArray()); + } + + /** + * Notices AJAX requests. + * + * @return void + */ + private function ajaxTaskNotices() + { + if (!Session::checkToken('request')) + { + echo json_encode([ + 'error' => true, + 'message' => Text::_('JINVALID_TOKEN') + ]); + die(); + } + + $input = $this->app->input; + + $action = $input->get('action', null); + + $allowed_actions = [ + 'downloadkey', + 'ajaxnotices' + ]; + + if (!in_array($action, $allowed_actions)) + { + echo json_encode([ + 'error' => true, + 'response' => 'Invalid action.' + ]); + die(); + } + + $error = false; + $response = ''; + + switch ($action) + { + case 'downloadkey': + // Get Download Key + if (!$download_key = $input->get('download_key', null, 'string')) + { + echo json_encode([ + 'error' => true, + 'response' => 'Missing download key.' + ]); + die(); + } + + // Try and update the Download Key + if (!\NRFramework\Functions::updateDownloadKey($download_key)) + { + echo json_encode([ + 'error' => true, + 'response' => 'Cannot update download key.' + ]); + die(); + } + + $response = Text::_('NR_DOWNLOAD_KEY_UPDATED'); + break; + case 'ajaxnotices': + // Get element + if (!$ext_element = $input->get('ext_element', null, 'string')) + { + echo json_encode([ + 'error' => true, + 'response' => 'Missing extension element.' + ]); + die(); + } + + // Get xml + if (!$ext_xml = $input->get('ext_xml', null, 'string')) + { + echo json_encode([ + 'error' => true, + 'response' => 'Missing extension xml.' + ]); + die(); + } + + // Get type + if (!$ext_type = $input->get('ext_type', null, 'string')) + { + echo json_encode([ + 'error' => true, + 'response' => 'Missing extension type.' + ]); + die(); + } + + // Current URL + if (!$current_url = $input->get('current_url', null, 'string')) + { + echo json_encode([ + 'error' => true, + 'response' => 'Missing current URL.' + ]); + die(); + } + + // Get excluded notices + $exclude = $input->get('exclude', null, 'string'); + $exclude = array_filter(explode(',', $exclude)); + + $notices = \NRFramework\Notices\Notices::getInstance([ + 'ext_element' => $ext_element, + 'ext_xml' => $ext_xml, + 'ext_type' => $ext_type, + 'exclude' => $exclude, + 'current_url' => $current_url + ])->getNotices(); + + echo json_encode([ + 'error' => false, + 'notices' => $notices + ]); + die(); + break; + } + + echo json_encode([ + 'error' => $error, + 'response' => $response + ]); + } + + /** + * Templates Library AJAX requests. + * + * @return void + */ + private function ajaxTaskTemplatesLibrary() + { + if (!Session::checkToken('request')) + { + echo json_encode([ + 'error' => true, + 'message' => Text::_('JINVALID_TOKEN') + ]); + die(); + } + + $action = $this->app->input->get('action', null); + + $input = new Registry(json_decode(file_get_contents('php://input'))); + + $template_id = $input->get('template_id', ''); + + $allowed_actions = [ + 'get_templates', + 'refresh_templates', + 'insert_template', + 'favorites_toggle' + ]; + + if (!in_array($action, $allowed_actions)) + { + echo json_encode([ + 'error' => true, + 'message' => 'Cannot validate request.' + ]); + die(); + } + + if (!$options = json_decode($input->get('options', []), true)) + { + echo json_encode([ + 'error' => true, + 'message' => 'Cannot validate request.' + ]); + die(); + } + + $class = ''; + $method = 'tf_library_ajax_' . $action; + + switch ($action) { + case 'get_templates': + case 'refresh_templates': + case 'insert_template': + $class = 'templates'; + + if ($action === 'insert_template') + { + // Ensure a template ID is given + if (empty($template_id)) + { + echo json_encode([ + 'error' => true, + 'message' => 'Cannot process request.' + ]); + die(); + } + + $options['template_id'] = $template_id; + } + + break; + case 'favorites_toggle': + $class = 'favorites'; + + // Ensure a template ID is given + if (empty($template_id)) + { + echo json_encode([ + 'error' => true, + 'message' => 'Cannot process request.' + ]); + die(); + } + + $options['template_id'] = $template_id; + + break; + } + + $library = new \NRFramework\Library\Library($options); + + echo json_encode($library->$class->$method()); + } + + /** + * Conditional Builder AJAX requests. + * + * @return void + */ + private function ajaxTaskConditionBuilder() + { + if (!Session::checkToken('request')) + { + echo json_encode([ + 'error' => true, + 'message' => Text::_('JINVALID_TOKEN') + ]); + die(); + } + + $input = new Registry(json_decode(file_get_contents('php://input'))); + + switch ($input->get('subtask', null)) + { + // Adding a condition item or group + case 'add': + $conditionItemGroup = $input->get('conditionItemGroup', null); + $groupKey = intval($input->get('groupKey')); + $conditionKey = intval($input->get('conditionKey')); + $include_rules = $input->get('include_rules', null); + $exclude_rules = $input->get('exclude_rules', null); + $exclude_rules_pro = $input->get('exclude_rules_pro', null) === '1'; + + $conditionItem = NRFramework\Conditions\ConditionBuilder::add($conditionItemGroup, $groupKey, $conditionKey, null, $include_rules, $exclude_rules, $exclude_rules_pro); + + // Adding a single condition item + if (!$input->get('addingNewGroup', false)) { + echo $conditionItem; + break; + } + + $payload = [ + 'name' => $conditionItemGroup, + 'groupKey' => $groupKey, + 'groupConditions' => ['enabled' => 1], + 'include_rules' => $include_rules, + 'exclude_rules' => $exclude_rules, + 'exclude_rules_pro' => $exclude_rules_pro, + 'condition_items_parsed' => [$conditionItem], + ]; + + // Adding a condition group + echo NRFramework\Conditions\ConditionBuilder::getLayout('conditionbuilder_group', $payload); + break; + case 'options': + $conditionItemGroup = $input->get('conditionItemGroup', null); + $name = $input->get('name', null); + + echo NRFramework\Conditions\ConditionBuilder::renderOptions($name, $conditionItemGroup); + break; + case 'init_load': + $payload = [ + 'data' => $input->get('data', []), + 'name' => $input->get('name', null), + 'include_rules' => $input->get('include_rules', null), + 'exclude_rules' => $input->get('exclude_rules', null), + 'exclude_rules_pro' => $input->get('exclude_rules_pro', null) === '1' + ]; + + echo NRFramework\Conditions\ConditionBuilder::initLoad($payload); + break; + } + } + + /** + * Remains for backwards compatibility. + * + * @deprecated 4.9.50 + */ + private function ajaxTaskUpdateNotification() + { + echo HTML::updateNotification($this->app->input->get('element')); + } +} \ No newline at end of file diff --git a/plugins/system/nrframework/nrframework.xml b/plugins/system/nrframework/nrframework.xml new file mode 100644 index 00000000..4786c0a2 --- /dev/null +++ b/plugins/system/nrframework/nrframework.xml @@ -0,0 +1,88 @@ + + + plg_system_nrframework + PLG_SYSTEM_NRFRAMEWORK_DESC + 5.0.74 + August 2016 + Tassos Marinos + Copyright © 2024 Tassos All Rights Reserved + http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + info@tassos.gr + http://www.tassos.gr + script.install.php + + nrframework.php + script.install.helper.php + autoload.php + fields + helpers + xml + language + layouts + NRFramework + + + +
+ +
+
+ + +
+
+ + + + + +
+
+
+ + img + css + font + js + svg + +
diff --git a/plugins/system/nrframework/script.install.helper.php b/plugins/system/nrframework/script.install.helper.php new file mode 100644 index 00000000..46731850 --- /dev/null +++ b/plugins/system/nrframework/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgSystemNrframeworkInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/system/nrframework/script.install.php b/plugins/system/nrframework/script.install.php new file mode 100644 index 00000000..42015b18 --- /dev/null +++ b/plugins/system/nrframework/script.install.php @@ -0,0 +1,26 @@ + + * @link https://www.tassos.gr + * @copyright Copyright © 2024 Tassos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgSystemNRFrameworkInstallerScript extends PlgSystemNRFrameworkInstallerScriptHelper +{ + public $name = 'TASSOS_FRAMEWORK'; + public $alias = 'nrframework'; + public $extension_type = 'plugin'; + + public function onBeforeInstall() + { + if (!$this->isNewer()) + { + return false; + } + } +} diff --git a/plugins/system/nrframework/xml/conditionbuilder/base.xml b/plugins/system/nrframework/xml/conditionbuilder/base.xml new file mode 100644 index 00000000..916ae76a --- /dev/null +++ b/plugins/system/nrframework/xml/conditionbuilder/base.xml @@ -0,0 +1,9 @@ + +
+
+ +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/acymailing.xml b/plugins/system/nrframework/xml/conditions/acymailing.xml new file mode 100644 index 00000000..acc8b3bd --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/acymailing.xml @@ -0,0 +1,12 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/akeebasubs.xml b/plugins/system/nrframework/xml/conditions/akeebasubs.xml new file mode 100644 index 00000000..f42da8ba --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/akeebasubs.xml @@ -0,0 +1,11 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/browser.xml b/plugins/system/nrframework/xml/conditions/browser.xml new file mode 100644 index 00000000..f38cd66e --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/browser.xml @@ -0,0 +1,13 @@ + +
+
+ + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/contentarticle.xml b/plugins/system/nrframework/xml/conditions/component/contentarticle.xml new file mode 100644 index 00000000..2401f68a --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/contentarticle.xml @@ -0,0 +1,13 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/contentcategory.xml b/plugins/system/nrframework/xml/conditions/component/contentcategory.xml new file mode 100644 index 00000000..49decbc9 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/contentcategory.xml @@ -0,0 +1,32 @@ + +
+
+ + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/contentview.xml b/plugins/system/nrframework/xml/conditions/component/contentview.xml new file mode 100644 index 00000000..932dd064 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/contentview.xml @@ -0,0 +1,19 @@ + +
+
+ + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcartcontainsproducts.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcartcontainsproducts.xml new file mode 100644 index 00000000..a18dbbc8 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcartcontainsproducts.xml @@ -0,0 +1,23 @@ + +
+
+ + + + + + + +
+ \ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcartcontainsxproducts.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcartcontainsxproducts.xml new file mode 100644 index 00000000..167fffe5 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcartcontainsxproducts.xml @@ -0,0 +1,46 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcartvalue.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcartvalue.xml new file mode 100644 index 00000000..c58b161c --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcartvalue.xml @@ -0,0 +1,52 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcategory.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcategory.xml new file mode 100644 index 00000000..1987ae7d --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcategory.xml @@ -0,0 +1,23 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcategoryview.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcategoryview.xml new file mode 100644 index 00000000..253c9878 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcategoryview.xml @@ -0,0 +1,21 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcurrentproductprice.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcurrentproductprice.xml new file mode 100644 index 00000000..abbbdc1a --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcurrentproductprice.xml @@ -0,0 +1,40 @@ + +
+
+ + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopcurrentproductstock.xml b/plugins/system/nrframework/xml/conditions/component/hikashopcurrentproductstock.xml new file mode 100644 index 00000000..7f51f517 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopcurrentproductstock.xml @@ -0,0 +1,40 @@ + +
+
+ + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashoplastpurchaseddate.xml b/plugins/system/nrframework/xml/conditions/component/hikashoplastpurchaseddate.xml new file mode 100644 index 00000000..5d6f5ed5 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashoplastpurchaseddate.xml @@ -0,0 +1,54 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashoppurchasedproduct.xml b/plugins/system/nrframework/xml/conditions/component/hikashoppurchasedproduct.xml new file mode 100644 index 00000000..2b18247f --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashoppurchasedproduct.xml @@ -0,0 +1,21 @@ + +
+
+ + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashopsingle.xml b/plugins/system/nrframework/xml/conditions/component/hikashopsingle.xml new file mode 100644 index 00000000..e84f4770 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashopsingle.xml @@ -0,0 +1,20 @@ + +
+
+ + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/hikashoptotalspend.xml b/plugins/system/nrframework/xml/conditions/component/hikashoptotalspend.xml new file mode 100644 index 00000000..99196db9 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/hikashoptotalspend.xml @@ -0,0 +1,45 @@ + +
+
+ + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/k2category.xml b/plugins/system/nrframework/xml/conditions/component/k2category.xml new file mode 100644 index 00000000..1226c71a --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/k2category.xml @@ -0,0 +1,32 @@ + +
+
+ + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/k2item.xml b/plugins/system/nrframework/xml/conditions/component/k2item.xml new file mode 100644 index 00000000..abfae58a --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/k2item.xml @@ -0,0 +1,30 @@ + +
+
+ + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/k2pagetype.xml b/plugins/system/nrframework/xml/conditions/component/k2pagetype.xml new file mode 100644 index 00000000..795f7c0e --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/k2pagetype.xml @@ -0,0 +1,13 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/k2tag.xml b/plugins/system/nrframework/xml/conditions/component/k2tag.xml new file mode 100644 index 00000000..04a25b7e --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/k2tag.xml @@ -0,0 +1,13 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcartcontainsproducts.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcartcontainsproducts.xml new file mode 100644 index 00000000..345a4801 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcartcontainsproducts.xml @@ -0,0 +1,23 @@ + +
+
+ + + + + + + +
+ \ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcartcontainsxproducts.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcartcontainsxproducts.xml new file mode 100644 index 00000000..167fffe5 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcartcontainsxproducts.xml @@ -0,0 +1,46 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcartvalue.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcartvalue.xml new file mode 100644 index 00000000..c58b161c --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcartvalue.xml @@ -0,0 +1,52 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcategory.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcategory.xml new file mode 100644 index 00000000..a5eca7a7 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcategory.xml @@ -0,0 +1,23 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcategoryview.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcategoryview.xml new file mode 100644 index 00000000..4d3b18ce --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcategoryview.xml @@ -0,0 +1,21 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcurrentproductprice.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcurrentproductprice.xml new file mode 100644 index 00000000..abbbdc1a --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcurrentproductprice.xml @@ -0,0 +1,40 @@ + +
+
+ + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartcurrentproductstock.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartcurrentproductstock.xml new file mode 100644 index 00000000..7f51f517 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartcurrentproductstock.xml @@ -0,0 +1,40 @@ + +
+
+ + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartlastpurchaseddate.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartlastpurchaseddate.xml new file mode 100644 index 00000000..5d6f5ed5 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartlastpurchaseddate.xml @@ -0,0 +1,54 @@ + +
+
+ + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartpurchasedproduct.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartpurchasedproduct.xml new file mode 100644 index 00000000..69db77ba --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartpurchasedproduct.xml @@ -0,0 +1,20 @@ + +
+
+ + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemartsingle.xml b/plugins/system/nrframework/xml/conditions/component/virtuemartsingle.xml new file mode 100644 index 00000000..d3097534 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemartsingle.xml @@ -0,0 +1,20 @@ + +
+
+ + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/component/virtuemarttotalspend.xml b/plugins/system/nrframework/xml/conditions/component/virtuemarttotalspend.xml new file mode 100644 index 00000000..99196db9 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/component/virtuemarttotalspend.xml @@ -0,0 +1,45 @@ + +
+
+ + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/convertforms.xml b/plugins/system/nrframework/xml/conditions/convertforms.xml new file mode 100644 index 00000000..965c9eea --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/convertforms.xml @@ -0,0 +1,12 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/convertformsform.xml b/plugins/system/nrframework/xml/conditions/convertformsform.xml new file mode 100644 index 00000000..77e50042 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/convertformsform.xml @@ -0,0 +1,14 @@ + +
+
+ + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/cookie.xml b/plugins/system/nrframework/xml/conditions/cookie.xml new file mode 100644 index 00000000..72feeb3c --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/cookie.xml @@ -0,0 +1,32 @@ + +
+
+ + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/date/date.xml b/plugins/system/nrframework/xml/conditions/date/date.xml new file mode 100644 index 00000000..57944946 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/date/date.xml @@ -0,0 +1,28 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/date/day.xml b/plugins/system/nrframework/xml/conditions/date/day.xml new file mode 100644 index 00000000..e734c3d3 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/date/day.xml @@ -0,0 +1,21 @@ + +
+
+ + + + + + + + + + + + + +
+
diff --git a/plugins/system/nrframework/xml/conditions/date/month.xml b/plugins/system/nrframework/xml/conditions/date/month.xml new file mode 100644 index 00000000..37f86185 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/date/month.xml @@ -0,0 +1,24 @@ + +
+
+ + + + + + + + + + + + + + + + +
+
diff --git a/plugins/system/nrframework/xml/conditions/date/time.xml b/plugins/system/nrframework/xml/conditions/date/time.xml new file mode 100644 index 00000000..7f11f7cb --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/date/time.xml @@ -0,0 +1,20 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/device.xml b/plugins/system/nrframework/xml/conditions/device.xml new file mode 100644 index 00000000..c0c8fdb4 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/device.xml @@ -0,0 +1,13 @@ + +
+
+ + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/engagebox.xml b/plugins/system/nrframework/xml/conditions/engagebox.xml new file mode 100644 index 00000000..bdb55ae8 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/engagebox.xml @@ -0,0 +1,13 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/geo/city.xml b/plugins/system/nrframework/xml/conditions/geo/city.xml new file mode 100644 index 00000000..160a4b6e --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/geo/city.xml @@ -0,0 +1,19 @@ + +
+
+ + + + + + + + +
+ \ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/geo/continent.xml b/plugins/system/nrframework/xml/conditions/geo/continent.xml new file mode 100644 index 00000000..44f6dba8 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/geo/continent.xml @@ -0,0 +1,16 @@ + +
+
+ + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/geo/country.xml b/plugins/system/nrframework/xml/conditions/geo/country.xml new file mode 100644 index 00000000..0743b23c --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/geo/country.xml @@ -0,0 +1,15 @@ + +
+
+ + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/geo/region.xml b/plugins/system/nrframework/xml/conditions/geo/region.xml new file mode 100644 index 00000000..e5a85921 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/geo/region.xml @@ -0,0 +1,18 @@ + +
+
+ + + + + + + + +
+ \ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/homepage.xml b/plugins/system/nrframework/xml/conditions/homepage.xml new file mode 100644 index 00000000..8f1645ac --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/homepage.xml @@ -0,0 +1,11 @@ + +
+
+ + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/ip.xml b/plugins/system/nrframework/xml/conditions/ip.xml new file mode 100644 index 00000000..25f0ebd2 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/ip.xml @@ -0,0 +1,19 @@ + +
+
+ + + + + + + +
+ diff --git a/plugins/system/nrframework/xml/conditions/joomla/accesslevel.xml b/plugins/system/nrframework/xml/conditions/joomla/accesslevel.xml new file mode 100644 index 00000000..5e5a8df0 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/joomla/accesslevel.xml @@ -0,0 +1,13 @@ + +
+
+ + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/joomla/component.xml b/plugins/system/nrframework/xml/conditions/joomla/component.xml new file mode 100644 index 00000000..a577a5f7 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/joomla/component.xml @@ -0,0 +1,13 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/joomla/language.xml b/plugins/system/nrframework/xml/conditions/joomla/language.xml new file mode 100644 index 00000000..8d076034 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/joomla/language.xml @@ -0,0 +1,12 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/joomla/menu.xml b/plugins/system/nrframework/xml/conditions/joomla/menu.xml new file mode 100644 index 00000000..e35e5d97 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/joomla/menu.xml @@ -0,0 +1,36 @@ + +
+
+ + + + + + + + + + + + + +
+
diff --git a/plugins/system/nrframework/xml/conditions/joomla/usergroup.xml b/plugins/system/nrframework/xml/conditions/joomla/usergroup.xml new file mode 100644 index 00000000..1a7eb6bf --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/joomla/usergroup.xml @@ -0,0 +1,12 @@ + +
+
+ + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/joomla/userid.xml b/plugins/system/nrframework/xml/conditions/joomla/userid.xml new file mode 100644 index 00000000..d2466e7c --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/joomla/userid.xml @@ -0,0 +1,14 @@ + +
+
+ + + + + + + +
+ \ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/os.xml b/plugins/system/nrframework/xml/conditions/os.xml new file mode 100644 index 00000000..e8390f72 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/os.xml @@ -0,0 +1,13 @@ + +
+
+ + + +
+
diff --git a/plugins/system/nrframework/xml/conditions/pageviews.xml b/plugins/system/nrframework/xml/conditions/pageviews.xml new file mode 100644 index 00000000..ab731905 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/pageviews.xml @@ -0,0 +1,20 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/php.xml b/plugins/system/nrframework/xml/conditions/php.xml new file mode 100644 index 00000000..36543fca --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/php.xml @@ -0,0 +1,18 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/referrer.xml b/plugins/system/nrframework/xml/conditions/referrer.xml new file mode 100644 index 00000000..68b9e03c --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/referrer.xml @@ -0,0 +1,21 @@ + +
+
+ + + + + + + +
+ diff --git a/plugins/system/nrframework/xml/conditions/returningnewvisitor.xml b/plugins/system/nrframework/xml/conditions/returningnewvisitor.xml new file mode 100644 index 00000000..f2aa48a7 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/returningnewvisitor.xml @@ -0,0 +1,11 @@ + +
+
+ + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/timeonsite.xml b/plugins/system/nrframework/xml/conditions/timeonsite.xml new file mode 100644 index 00000000..6c020e80 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/timeonsite.xml @@ -0,0 +1,22 @@ + +
+
+ + + + + + + + + +
+
\ No newline at end of file diff --git a/plugins/system/nrframework/xml/conditions/url.xml b/plugins/system/nrframework/xml/conditions/url.xml new file mode 100644 index 00000000..cd428a13 --- /dev/null +++ b/plugins/system/nrframework/xml/conditions/url.xml @@ -0,0 +1,23 @@ + +
+
+ + + + + + + + + +
+ diff --git a/plugins/system/tgeoip/db/index.html b/plugins/system/tgeoip/db/index.html new file mode 100644 index 00000000..369bdd7b --- /dev/null +++ b/plugins/system/tgeoip/db/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/system/tgeoip/field/lastupdated.php b/plugins/system/tgeoip/field/lastupdated.php new file mode 100644 index 00000000..ac488f6a --- /dev/null +++ b/plugins/system/tgeoip/field/lastupdated.php @@ -0,0 +1,39 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file + +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Factory; +use Joomla\Filesystem\File; + +class JFormFieldTG_LastUpdated extends FormField +{ + /** + * Method to render the input field + * + * @return string + */ + public function getInput() + { + $file = JPATH_PLUGINS . '/system/tgeoip/db/GeoLite2-City.mmdb'; + + if (!file_exists($file)) + { + return ''; + } + + return Factory::getDate(@filemtime($file))->format('d M Y H:m'); + } +} \ No newline at end of file diff --git a/plugins/system/tgeoip/field/lookup.php b/plugins/system/tgeoip/field/lookup.php new file mode 100644 index 00000000..8cd8a89a --- /dev/null +++ b/plugins/system/tgeoip/field/lookup.php @@ -0,0 +1,108 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; +use NRFramework\User; + +class JFormFieldTG_Lookup extends FormField +{ + /** + * GeoIP Class + * + * @var object + */ + private $geoIP; + + /** + * Method to render the input field + * + * @return string + */ + public function getInput() + { + // JavaScript + $ajaxURL = Uri::base() . 'index.php?option=com_ajax&format=raw&plugin=tgeoip&task=get&' . Session::getFormToken() . '=1'; + + Factory::getDocument()->addScriptDeclaration(' + document.addEventListener("DOMContentLoaded", function() { + document.addEventListener("click", function(e) { + var btn = e.target.closest(".tGeoIPtest button"); + if (!btn) { + return; + } + + e.preventDefault(); + + ip = document.querySelector(".tGeoIPtest input").value; + + if (!ip) { + alert("Please enter a valid IP address"); + return false; + } + + var data = new FormData(); + data.append("ip", ip); + + fetch("' . $ajaxURL . '", + { + method: "POST", + body: data + }) + .then(function(res){ return res.json(); }) + .then(function(response){ + if (response) { + if (response.continent) { + document.querySelector(".tGeoIPtest .continent").innerHTML = response.continent.names.en; + } + + if (response.city) { + document.querySelector(".tGeoIPtest .city").innerHTML = response.city.names.en; + } + + if (response.country) { + document.querySelector(".tGeoIPtest .country").innerHTML = response.country.names.en; + document.querySelector(".tGeoIPtest .country_code").innerHTML = response.country.iso_code; + } + + document.querySelector(".tGeoIPtest .results").style.display = "block"; + } else { + alert("Invalid IP address"); + document.querySelector(".tGeoIPtest .results").style.display = "none"; + } + }) + + return false; + }) + }); + '); + + // HTML + $ip = User::getIP(); + + return '
+ + +
'; + } +} \ No newline at end of file diff --git a/plugins/system/tgeoip/field/updatebutton.php b/plugins/system/tgeoip/field/updatebutton.php new file mode 100644 index 00000000..82fcc8f0 --- /dev/null +++ b/plugins/system/tgeoip/field/updatebutton.php @@ -0,0 +1,129 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +// No direct access to this file +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; + +class JFormFieldTG_UpdateButton extends FormField +{ + /** + * Method to render the input field + * + * @return string + */ + public function getInput() + { + if (!NRFramework\Extension::pluginIsEnabled('tgeoip')) + { + return '' . Text::_('PLG_SYSTEM_TGEOIP_ENABLE_PLUGIN') . ''; + } + + HTMLHelper::stylesheet('plg_system_nrframework/joomla4.css', ['relative' => true, 'version' => 'auto']); + + $ajaxURL = Uri::base() . 'index.php?option=com_ajax&format=raw&plugin=tgeoip&task=update&license_key=USER_LICENSE_KEY&' . Session::getFormToken() . '=1'; + + Text::script('PLG_SYSTEM_TGEOIP_DATABASE_UPDATED'); + Text::script('PLG_SYSTEM_TGEOIP_PLEASE_WAIT'); + + Factory::getDocument()->addScriptDeclaration(' + document.addEventListener("DOMContentLoaded", function() { + document.addEventListener("click", function(e) { + var btn = e.target.closest(".geo button"); + if (!btn) { + return; + } + + e.preventDefault(); + + var license_key = e.target.closest("form").querySelector("#jform_params_license_key").value; + if (!license_key) { + return; + } + + var alert = document.querySelector(".geo .alert"); + + var url = "' . $ajaxURL . '"; + url = url.replace("USER_LICENSE_KEY", license_key); + + // before request + alert.style.display = "none"; + btn.querySelector("span").innerHTML = Joomla.Text._("PLG_SYSTEM_TGEOIP_PLEASE_WAIT"); + btn.classList.add("btn-working"); + + fetch(url, + { + method: "POST" + }) + .then(function(res){ return res.text(); }) + .then(function(response){ + if (response == "1") { + alert.innerHTML = Joomla.Text._("PLG_SYSTEM_TGEOIP_DATABASE_UPDATED"); + alert.style.display = "block"; + alert.classList.remove("alert-danger"); + alert.classList.add("alert-success"); + } else { + alert.innerHTML = response; + alert.style.display = "block"; + alert.classList.remove("alert-success"); + alert.classList.add("alert-danger"); + } + + btn.classList.remove("btn-working"); + btn.querySelector("span").innerHTML = btn.dataset.label; + }); + + return false; + }); + }); + '); + + Factory::getDocument()->addStyleDeclaration(' + .geo .btn-working { + pointer-events:none; + } + .geo .alert { + display:none; + margin-bottom: 10px; + } + .geo button { + outline:none !important; + width: auto; + height: auto; + line-height: inherit; + } + .geo button:before { + margin-right:5px; + position:relative; + top:1px; + } + #wrapper .geo .icon-refresh { + margin-right: 5px; + } + '); + + return ' +
+
+ +
'; + } +} \ No newline at end of file diff --git a/plugins/system/tgeoip/helper/fakebcmath.php b/plugins/system/tgeoip/helper/fakebcmath.php new file mode 100644 index 00000000..91c9a902 --- /dev/null +++ b/plugins/system/tgeoip/helper/fakebcmath.php @@ -0,0 +1,132 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +if (!function_exists('bcadd')) +{ + function bcadd($Num1,$Num2,$Scale=null) + { + // check if they're valid positive numbers, extract the whole numbers and decimals + if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)|| + !preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0'); + + // this is where the result is stored + $Output=array(); + + // remove ending zeroes from decimals and remove point + $Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):''; + $Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):''; + + // calculate the longest length of decimals + $DLen=max(strlen($Dec1),strlen($Dec2)); + + // if $Scale is null, automatically set it to the amount of decimal places for accuracy + if($Scale==null) $Scale=$DLen; + + // remove leading zeroes and reverse the whole numbers, then append padded decimals on the end + $Num1=strrev(ltrim($Tmp1[1],'0').str_pad($Dec1,$DLen,'0')); + $Num2=strrev(ltrim($Tmp2[1],'0').str_pad($Dec2,$DLen,'0')); + + // calculate the longest length we need to process + $MLen=max(strlen($Num1),strlen($Num2)); + + // pad the two numbers so they are of equal length (both equal to $MLen) + $Num1=str_pad($Num1,$MLen,'0'); + $Num2=str_pad($Num2,$MLen,'0'); + + // process each digit, keep the ones, carry the tens (remainders) + for($i=0;$i<$MLen;$i++) { + $Sum=((int)$Num1[$i]+(int)$Num2[$i]); + if(isset($Output[$i])) $Sum+=$Output[$i]; + $Output[$i]=$Sum%10; + if($Sum>9) $Output[$i+1]=1; + } + + // convert the array to string and reverse it + $Output=strrev(implode($Output)); + + // substring the decimal digits from the result, pad if necessary (if $Scale > amount of actual decimals) + // next, since actual zero values can cause a problem with the substring values, if so, just simply give '0' + // next, append the decimal value, if $Scale is defined, and return result + $Decimal=str_pad(substr($Output,-$DLen,$Scale),$Scale,'0'); + $Output=(($MLen-$DLen<1)?'0':substr($Output,0,-$DLen)); + $Output.=(($Scale>0)?".{$Decimal}":''); + return($Output); + } +} + +if (!function_exists('bcmul')) +{ + function bcmul($Num1='0',$Num2='0') { + // check if they're both plain numbers + if(!preg_match("/^\d+$/",$Num1)||!preg_match("/^\d+$/",$Num2)) return(0); + + // remove zeroes from beginning of numbers + for($i=0;$i1&&$Rema2[0]=='0') $Rema2=substr($Rema2,1); + + return($Rema2); + } +} + +if (!function_exists('bcpow')) +{ + function bcpow($num, $power) + { + $answer = "1"; + + while ($power) + { + $answer = bcmul($answer, $num, 100); + $power--; + } + return rtrim($answer, '0.'); + } +} \ No newline at end of file diff --git a/plugins/system/tgeoip/helper/tgeoip.php b/plugins/system/tgeoip/helper/tgeoip.php new file mode 100644 index 00000000..a7524de1 --- /dev/null +++ b/plugins/system/tgeoip/helper/tgeoip.php @@ -0,0 +1,551 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined('_JEXEC') or die; + +use Tassos\Vendor\GeoIp2\Database\Reader; +use Tassos\Vendor\splitbrain\PHPArchive\Tar; +use NRFramework\User; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Registry\Registry; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Factory; +use Joomla\Filesystem\Path; +use Joomla\CMS\Http\HttpFactory; + +class TGeoIP +{ + /** + * The MaxMind GeoLite database reader + * + * @var Reader + */ + private $reader = null; + + /** + * Records for IP addresses already looked up + * + * @var array + * + */ + private $lookups = array(); + + /** + * Max Age Database before it needs an update + * + * @var integer + */ + private $maxAge = 30; + + /** + * Database File name + * + * @var string + */ + private $DBFileName = 'GeoLite2-City'; + + /** + * Database Remote URL + * + * @var string + */ + private $DBUpdateURL = 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=USER_LICENSE_KEY&suffix=tar.gz'; + + /** + * GeoIP Enable Geolocations Documentation URL + * + * @var string + */ + private $TGeoIPEnableDocURL = 'https://www.tassos.gr/kb/general/how-to-enable-geolocation-features-in-tassos-gr-extensions'; + + /** + * The IP address to look up + * + * @var string + */ + private $ip; + + /** + * The License Key + * + * @var string + */ + private $key; + + /** + * Public constructor. Loads up the GeoLite2 database. + */ + public function __construct($ip = null) + { + if (!function_exists('bcadd') || !function_exists('bcmul') || !function_exists('bcpow')) + { + require_once __DIR__ . '/fakebcmath.php'; + } + + // Check we have a valid GeoLite2 database + $filePath = $this->getDBPath(); + + if (!file_exists($filePath)) + { + $this->reader = null; + } + + try + { + $this->reader = new Reader($filePath); + } + // If anything goes wrong, MaxMind will raise an exception, resulting in a WSOD. Let's be sure to catch everything. + catch(\Exception $e) + { + $this->reader = null; + } + + // Setup IP + $this->ip = $ip ?: User::getIP(); + + if (in_array($this->ip, array('127.0.0.1', '::1'))) + { + $this->ip = ''; + } + } + + /** + * Sets the license key + * + * @param string + * + * @return mixed + */ + public function setKey($key) + { + $this->key = $key; + } + + /** + * Retrieves the key + * + * @return string + */ + private function getKey() + { + if ($this->key) + { + return $this->key; + } + + $plugin = PluginHelper::getPlugin('system', 'tgeoip'); + $params = new Registry($plugin->params); + + return $params->get('license_key', ''); + } + + /** + * Set the IP to look up + * + * @param string $ip The IP to look up + */ + public function setIP($ip) + { + $this->ip = $ip; + return $this; + } + + /** + * Gets the ISO country code from an IP address + * + * @return mixed A string with the country ISO code if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getCountryCode() + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + return $record->country->isoCode; + } + + /** + * Gets the country name from an IP address + * + * @param string $locale The locale of the country name, e.g 'de' to return the country names in German. If not specified the English (US) names are returned. + * + * @return mixed A string with the country name if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getCountryName($locale = null) + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + if (empty($locale)) + { + return $record->country->name; + } + + return $record->country->names[$locale]; + } + + /** + * Gets the continent ISO code from an IP address + * + * @return mixed A string with the country name if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getContinentCode($locale = null) + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + return $record->continent->code; + } + + /** + * Gets the continent name from an IP address + * + * @param string $locale The locale of the continent name, e.g 'de' to return the country names in German. If not specified the English (US) names are returned. + * + * @return mixed A string with the country name if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getContinentName($locale = null) + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + if (empty($locale)) + { + return $record->continent; + } + + return $record->continent->names[$locale]; + } + + /** + * Gets a raw record from an IP address + * + * @return mixed A \GeoIp2\Model\City record if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getRecord() + { + if (empty($this->ip)) + { + return false; + } + + $ip = $this->ip; + + $needsToLoad = !array_key_exists($ip, $this->lookups); + + if ($needsToLoad) + { + try + { + if (!is_null($this->reader)) + { + $this->lookups[$ip] = $this->reader->city($ip); + } + else + { + $this->lookups[$ip] = null; + } + } + catch (Tassos\Vendor\GeoIp2\Exception\AddressNotFoundException $e) + { + $this->lookups[$ip] = false; + } + catch (\Exception $e) + { + // GeoIp2 could throw several different types of exceptions. Let's be sure that we're going to catch them all + $this->lookups[$ip] = null; + } + } + + return $this->lookups[$ip]; + } + + /** + * Gets the city's name from an IP address + * + * @param string $locale The locale of the city's name, e.g 'de' to return the city names in German. If not specified the English (US) names are returned. + * @return mixed A string with the city name if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getCity($locale = null) + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + if (empty($locale)) + { + return $record->city->name; + } + + return $record->city->names[$locale]; + } + + /** + * Gets a geographical region's (i.e. a country's province/state) name from an IP address + * + * @param string $locale The locale of the regions's name, e.g 'de' to return region names in German. If not specified the English (US) names are returned. + * @return mixed A string with the region's name if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getRegionName($locale = null) + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + // MaxMind stores region information in a 'Subdivision' object (also found in $record->city->subdivision) + // http://maxmind.github.io/GeoIP2-php/doc/v2.9.0/class-GeoIp2.Record.Subdivision.html + if (empty($locale)) + { + return $record->mostSpecificSubdivision->name; + } + + return $record->mostSpecificSubdivision->names[$locale]; + } + + /** + * Gets a geographical region's (i.e. a country's province/state) ISO 3611-2 (alpha-2) code from an IP address + * + * @return mixed A string with the region's code if found, false if the IP address is not found, null if the db can't be loaded + */ + public function getRegionCode() + { + $record = $this->getRecord(); + + if ($record === false || is_null($record)) + { + return false; + } + + // MaxMind stores region information in a 'Subdivision' object + // http://maxmind.github.io/GeoIP2-php/doc/v2.9.0/class-GeoIp2.Record.Subdivision.html + return $record->mostSpecificSubdivision->isoCode; + } + + /** + * Downloads and installs a fresh copy of the GeoLite2 City database + * + * @return mixed True on success, error string on failure + */ + public function updateDatabase() + { + // Try to download the package, if I get any exception I'll simply stop here and display the error + try + { + $compressed = $this->downloadDatabase(); + } + catch (\Exception $e) + { + return $e->getMessage(); + } + + // Write the downloaded file to a temporary location + $target = $this->getTempFolder() . $this->DBFileName . '.tar.gz'; + if (File::write($target, $compressed) === false) + { + return Text::_('PLG_SYSTEM_TGEOIP_ERR_WRITEFAILED'); + } + + // Unzip database to the same temporary location + $tar = new Tar; + $tar->open($target); + $extracted_files = $tar->extract($this->getTempFolder()); + + $database_file = ''; + $extracted_folder = ''; + + // Loop through extracted files to find the name of the extracted folder and the name of the database file + foreach ($extracted_files as $key => $extracted_file) + { + if ($extracted_file->getIsdir()) + { + $extracted_folder = $extracted_file->getPath(); + } + + if (strpos($extracted_file->getPath(), '.mmdb') === false) + { + continue; + } + + $database_file = $extracted_file->getPath(); + } + + // Move database file to the correct location + if (!File::move($this->getTempFolder() . $database_file, $this->getDBPath())) + { + return Text::sprintf('PLG_SYSTEM_TGEOIP_ERR_CANTWRITE', $this->getDBPath()); + } + + // Make sure the database is readable + if (!$this->dbIsValid()) + { + return Text::_('PLG_SYSTEM_TGEOIP_ERR_INVALIDDB'); + } + + // Delete leftovers + File::delete($target); + Folder::delete($this->getTempFolder() . $extracted_folder); + + return true; + } + + /** + * Double check if MaxMind can actually read and validate the downloaded database + * + * @return bool + */ + private function dbIsValid() + { + try + { + $reader = new Reader($this->getDBPath()); + } + catch (\Exception $e) + { + return false; + } + + return true; + } + + /** + * Download the compressed database for the provider + * + * @return string The compressed data + * + * @throws Exception + */ + private function downloadDatabase() + { + // Make sure we have enough memory limit + ini_set('memory_limit', '-1'); + + $license_key = $this->getKey(); + + if (empty($license_key)) + { + throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_LICENSE_KEY_EMPTY') . ' ' . Text::_('PLG_SYSTEM_TGEOIP_ENABLE_DOC_LINK_LABEL') . ''); + } + + $http = HttpFactory::getHttp(); + + $this->DBUpdateURL = str_replace('USER_LICENSE_KEY', $license_key, $this->DBUpdateURL); + + // Let's bubble up the exception, we will take care in the caller + $response = $http->get($this->DBUpdateURL); + $compressed = $response->body; + + // 401 is thrown if you have incorrect credentials or wrong license key + if ($response->code == 401) + { + throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_WRONG_LICENSE_KEY')); + } + + // Generic check on valid HTTP code + if ($response->code > 299) + { + throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_MAXMIND_GENERIC')); + } + + // An empty file indicates a problem with MaxMind's servers + if (empty($compressed)) + { + throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_EMPTYDOWNLOAD')); + } + + // Sometimes you get a rate limit exceeded + if (stristr($compressed, 'Rate limited exceeded') !== false) + { + throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_MAXMINDRATELIMIT')); + } + + return $compressed; + } + + /** + * Reads (and checks) the temp Joomla folder + * + * @return string + */ + private function getTempFolder() + { + $ds = DIRECTORY_SEPARATOR; + + $tmpdir = Factory::getConfig()->get('tmp_path'); + + if (realpath($tmpdir) == $ds . 'tmp') + { + $tmpdir = JPATH_SITE . $ds . 'tmp'; + } + + elseif (!is_dir($tmpdir)) + { + $tmpdir = JPATH_SITE . $ds . 'tmp'; + } + + return Path::clean(trim($tmpdir) . $ds); + } + + /** + * Returns Database local file path + * + * @return string + */ + private function getDBPath() + { + return JPATH_ROOT . '/plugins/system/tgeoip/db/' . $this->DBFileName . '.mmdb'; + } + + /** + * Does the GeoIP database need update? + * + * @return boolean + */ + public function needsUpdate() + { + // Get the modification time of the database file + $modTime = @filemtime($this->getDBPath()); + + // This is now + $now = time(); + + // Minimum time difference + $threshold = $this->maxAge * 24 * 3600; + + // Do we need an update? + $needsUpdate = ($now - $modTime) > $threshold; + + return $needsUpdate; + } +} \ No newline at end of file diff --git a/plugins/system/tgeoip/language/en-GB/en-GB.plg_system_tgeoip.ini b/plugins/system/tgeoip/language/en-GB/en-GB.plg_system_tgeoip.ini new file mode 100644 index 00000000..8fc01450 --- /dev/null +++ b/plugins/system/tgeoip/language/en-GB/en-GB.plg_system_tgeoip.ini @@ -0,0 +1,37 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos +; @link http://www.tassos.gr +; @copyright Copyright © 2024 Tassos Marinos All Rights Reserved +; @license GNU GPLv3 or later + +TGEOIP="Tassos GeoIP" +PLG_SYSTEM_TGEOIP="System - Tassos GeoIP" +PLG_SYSTEM_TGEOIP_DESC="This plugin provides GeoIP features (finding out the country of an IP address) for Tassos.gr extensions. Without it the GeoIP features will not be available. This plugin includes GeoLite2 data created by MaxMind http://www.maxmind.com." +PLG_SYSTEM_TGEOIP_ERR_NOGZSUPPORT="Your server does not support extraction of GZip (.gz) files. The GeoLite2 Country database update cannot proceed." +PLG_SYSTEM_TGEOIP_ERR_EMPTYDOWNLOAD="Downloading the GeoLite2 Country database failed: empty file retrieved from server. Please contact your host." +PLG_SYSTEM_TGEOIP_ERR_WRITEFAILED="Writing the temporary file failed. Please make sure that the temporary directory defined in your site's Global Configuration is writeable. The GeoLite2 Country database update cannot proceed." +PLG_SYSTEM_TGEOIP_ERR_CANTUNCOMPRESS="Cannot decompress the GeoLite2 Country database file. Probably a corrupt download? The GeoLite2 Country database update cannot proceed." +PLG_SYSTEM_TGEOIP_ERR_MAXMINDRATELIMIT="MaxMind's servers are busy. Please retry updating the GeoLite2 Country database in 24 hours." +PLG_SYSTEM_TGEOIP_ERR_MAXMIND_GENERIC="A connection error occurred. Please retry updating the GeoLite2 Country database in 24 hours." +PLG_SYSTEM_TGEOIP_ERR_INVALIDDB="Downloaded database seems to be invalid. Please retry updating the GeoLite2 Country database in 24 hours." +PLG_SYSTEM_TGEOIP_ERR_WRONG_LICENSE_KEY="Your MaxMind license key appears to be incorrect." +PLG_SYSTEM_TGEOIP_ERR_CANTWRITE="Moving the database file failed. Please make sure that the database directory is writeable: %s" +PLG_SYSTEM_TGEOIP_UPDATE_DATABASE="Update Database" +PLG_SYSTEM_TGEOIP_UPDATE_DATABASE_DESC="Update the GeoLite2 database from MaxMind servers. This might take several seconds to finish. Please be patient." +PLG_SYSTEM_TGEOIP_DATABASE="Database" +PLG_SYSTEM_TGEOIP_LAST_UPDATED="Last Updated" +PLG_SYSTEM_TGEOIP_LAST_UPDATED_DESC="Indicates the last datetime the database updated." +PLG_SYSTEM_TGEOIP_CHECK_IP="Lookup IP Address" +PLG_SYSTEM_TGEOIP_CHECK_IP_DESC="Test drive the GeoIP plugin by looking up an IP address." +PLG_SYSTEM_TGEOIP_MAINTENANCE="GeoIP Database Maintenance" +PLG_SYSTEM_TGEOIP_MAINTENANCE_DESC="%s finds the country of your visitors' IP addresses using the MaxMind GeoLite2 Country database. You are advised to update it at least once per month. On most servers you can perform the update by clicking the button below." +PLG_SYSTEM_TGEOIP_DATABASE_UPDATED="GeoIP database successfully updated!" +PLG_SYSTEM_TGEOIP_LICENSE_KEY="License Key" +PLG_SYSTEM_TGEOIP_LICENSE_KEY_DESC="Get your free License Key to download the latest MaxMind GeoLite2 Database." +PLG_SYSTEM_TGEOIP_LICENSE_KEY_GET="Get a free License Key" +PLG_SYSTEM_TGEOIP_LICENSE_KEY_EMPTY="Please enter a valid MaxMind License Key." +PLG_SYSTEM_TGEOIP_ENABLE_PLUGIN="To be able to update the database you will need to enable this plugin first" +PLG_SYSTEM_TGEOIP_ENABLE_DOC_LINK_LABEL="Click to learn how to enable Geolocation features in Tassos.gr extensions" +PLG_SYSTEM_TGEOIP_PLEASE_WAIT="Please wait..." \ No newline at end of file diff --git a/plugins/system/tgeoip/language/en-GB/en-GB.plg_system_tgeoip.sys.ini b/plugins/system/tgeoip/language/en-GB/en-GB.plg_system_tgeoip.sys.ini new file mode 100644 index 00000000..1e7f6ef6 --- /dev/null +++ b/plugins/system/tgeoip/language/en-GB/en-GB.plg_system_tgeoip.sys.ini @@ -0,0 +1,11 @@ +; @package Advanced Custom Fields +; @version 2.8.8 Pro +; +; @author Tassos Marinos +; @link http://www.tassos.gr +; @copyright Copyright © 2024 Tassos Marinos All Rights Reserved +; @license GNU GPLv3 or later + +TGEOIP="Tassos GeoIP" +PLG_SYSTEM_TGEOIP="System - Tassos GeoIP" +PLG_SYSTEM_TGEOIP_DESC="This plugin provides GeoIP features (finding out the country of an IP address) for Tassos.gr extensions. Without it the GeoIP features will not be available. This plugin includes GeoLite2 data created by MaxMind http://www.maxmind.com." \ No newline at end of file diff --git a/plugins/system/tgeoip/script.install.helper.php b/plugins/system/tgeoip/script.install.helper.php new file mode 100644 index 00000000..8cea4eff --- /dev/null +++ b/plugins/system/tgeoip/script.install.helper.php @@ -0,0 +1,691 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2016 Tassos Marinos All Rights Reserved + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\Filesystem\File; +use Joomla\Filesystem\Folder; + +class PlgSystemTgeoipInstallerScriptHelper +{ + public $name = ''; + public $alias = ''; + public $extname = ''; + public $extension_type = ''; + public $plugin_folder = 'system'; + public $module_position = 'status'; + public $client_id = 1; + public $install_type = 'install'; + public $show_message = true; + public $autopublish = true; + public $db = null; + public $app = null; + public $installedVersion; + + public function __construct(&$params) + { + $this->extname = $this->extname ?: $this->alias; + $this->db = Factory::getDbo(); + $this->app = Factory::getApplication(); + $this->installedVersion = $this->getVersion($this->getInstalledXMLFile()); + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function preflight($route, $adapter) + { + if (!in_array($route, array('install', 'update'))) + { + return; + } + + Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller'); + + if ($this->show_message && $this->isInstalled()) + { + $this->install_type = 'update'; + } + + if ($this->onBeforeInstall() === false) + { + return false; + } + } + + /** + * Preflight event + * + * @param string + * @param JAdapterInstance + * + * @return boolean + */ + public function postflight($route, $adapter) + { + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder()); + + if (!in_array($route, array('install', 'update'))) + { + return; + } + + if ($this->onAfterInstall() === false) + { + return false; + } + + if ($route == 'install' && $this->autopublish) + { + $this->publishExtension(); + } + + if ($this->show_message) + { + $this->addInstalledMessage(); + } + + Factory::getCache()->clean('com_plugins'); + Factory::getCache()->clean('_system'); + } + + public function isInstalled() + { + if (!is_file($this->getInstalledXMLFile())) + { + return false; + } + + $query = $this->db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type)) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName())); + $this->db->setQuery($query, 0, 1); + $result = $this->db->loadResult(); + + return empty($result) ? false : true; + } + + public function getMainFolder() + { + switch ($this->extension_type) + { + case 'plugin' : + return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname; + + case 'component' : + return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname; + + case 'module' : + return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname; + + case 'library' : + return JPATH_SITE . '/libraries/' . $this->extname; + } + } + + public function getInstalledXMLFile() + { + return $this->getXMLFile($this->getMainFolder()); + } + + public function getCurrentXMLFile() + { + return $this->getXMLFile(__DIR__); + } + + public function getXMLFile($folder) + { + switch ($this->extension_type) + { + case 'module' : + return $folder . '/mod_' . $this->extname . '.xml'; + default : + return $folder . '/' . $this->extname . '.xml'; + } + } + + public function foldersExist($folders = array()) + { + foreach ($folders as $folder) + { + if (is_dir($folder)) + { + return true; + } + } + + return false; + } + + public function publishExtension() + { + switch ($this->extension_type) + { + case 'plugin' : + $this->publishPlugin(); + + case 'module' : + $this->publishModule(); + } + } + + public function publishPlugin() + { + $query = $this->db->getQuery(true) + ->update('#__extensions') + ->set($this->db->quoteName('enabled') . ' = 1') + ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin')) + ->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname)) + ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder)); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function publishModule() + { + // Get module id + $query = $this->db->getQuery(true) + ->select('id') + ->from('#__modules') + ->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id); + $this->db->setQuery($query, 0, 1); + $id = $this->db->loadResult(); + + if (!$id) + { + return; + } + + // check if module is already in the modules_menu table (meaning is is already saved) + $query->clear() + ->select('moduleid') + ->from('#__modules_menu') + ->where($this->db->quoteName('moduleid') . ' = ' . (int) $id); + $this->db->setQuery($query, 0, 1); + $exists = $this->db->loadResult(); + + if ($exists) + { + return; + } + + // Get highest ordering number in position + $query->clear() + ->select('ordering') + ->from('#__modules') + ->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id) + ->order('ordering DESC'); + $this->db->setQuery($query, 0, 1); + $ordering = $this->db->loadResult(); + $ordering++; + + // publish module and set ordering number + $query->clear() + ->update('#__modules') + ->set($this->db->quoteName('published') . ' = 1') + ->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering) + ->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position)) + ->where($this->db->quoteName('id') . ' = ' . (int) $id); + $this->db->setQuery($query); + $this->db->execute(); + + // add module to the modules_menu table + $query->clear() + ->insert('#__modules_menu') + ->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid'))) + ->values((int) $id . ', 0'); + $this->db->setQuery($query); + $this->db->execute(); + } + + public function addInstalledMessage() + { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'), + '' . Text::_($this->name) . '', + '' . $this->getVersion() . '', + $this->getFullType() + ) + ); + } + + public function getPrefix() + { + switch ($this->extension_type) + { + case 'plugin'; + return Text::_('plg_' . strtolower($this->plugin_folder)); + + case 'component': + return Text::_('com'); + + case 'module': + return Text::_('mod'); + + case 'library': + return Text::_('lib'); + + default: + return $this->extension_type; + } + } + + public function getElementName($type = null, $extname = null) + { + $type = is_null($type) ? $this->extension_type : $type; + $extname = is_null($extname) ? $this->extname : $extname; + + switch ($type) + { + case 'component' : + return 'com_' . $extname; + + case 'module' : + return 'mod_' . $extname; + + case 'plugin' : + default: + return $extname; + } + } + + public function getFullType() + { + return Text::_('NRI_' . strtoupper($this->getPrefix())); + } + + public function isPro() + { + $versionFile = __DIR__ . "/version.php"; + + // If version file does not exist we assume a PRO version + if (!is_file($versionFile)) + { + return true; + } + + // Load version file + require_once $versionFile; + return (bool) $NR_PRO; + } + + public function getVersion($file = '') + { + $file = $file ?: $this->getCurrentXMLFile(); + + if (!is_file($file)) + { + return ''; + } + + $xml = Installer::parseXMLInstallFile($file); + + if (!$xml || !isset($xml['version'])) + { + return ''; + } + + return $xml['version']; + } + + /** + * Checks wether the extension can be installed or not + * + * @return boolean + */ + public function canInstall() + { + // The extension is not installed yet. Accept Install. + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + // Path to extension's version file + $versionFile = $this->getMainFolder() . "/version.php"; + $NR_PRO = true; + + // If version file does not exist we assume we have a PRO version installed + if (file_exists($versionFile)) + { + require_once($versionFile); + } + + // The free version is installed. Accept install. + if (!(bool)$NR_PRO) + { + return true; + } + + // Current package is a PRO version. Accept install. + if ($this->isPro()) + { + return true; + } + + // User is trying to update from PRO version to FREE. Do not accept install. + Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__); + + Factory::getApplication()->enqueueMessage( + Text::_('NRI_ERROR_PRO_TO_FREE'), 'error' + ); + + Factory::getApplication()->enqueueMessage( + html_entity_decode( + Text::sprintf( + 'NRI_ERROR_UNINSTALL_FIRST', + '', + '', + Text::_($this->name) + ) + ), 'error' + ); + + return false; + } + + /** + * Returns the URL alias of the extension. + * + * @return string + */ + private function getUrlAlias() + { + $alias = $this->alias; + + switch ($alias) + { + case 'smilepack': + $alias = 'smile-pack'; + break; + case 'convertforms': + $alias = 'convert-forms'; + break; + case 'rstbox': + $alias = 'engagebox'; + break; + case 'gsd': + $alias = 'google-structured-data'; + break; + } + + // ACF + if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf'))) + { + $alias = 'advanced-custom-fields'; + } + + return $alias; + } + + /** + * Checks whether string starts with substring. + * + * @param string $string + * @param string $query + * + * @return bool + */ + public static function startsWith($string, $query) + { + return substr($string, 0, strlen($query)) === $query; + } + + /** + * Checks if current version is newer than the installed one + * Used for Novarain Framework + * + * @return boolean [description] + */ + public function isNewer() + { + if (!$installed_version = $this->getVersion($this->getInstalledXMLFile())) + { + return true; + } + + $package_version = $this->getVersion(); + + return version_compare($installed_version, $package_version, '<='); + } + + /** + * Helper method triggered before installation + * + * @return bool + */ + public function onBeforeInstall() + { + if (!$this->canInstall()) + { + return false; + } + } + + /** + * Helper method triggered after installation + */ + public function onAfterInstall() + { + + } + + /** + * Delete files + * + * @param array $folders + */ + public function deleteFiles($files = array()) + { + foreach ($files as $key => $file) + { + if (!is_file($file)) + { + continue; + } + + File::delete($file); + } + } + + /** + * Deletes folders + * + * @param array $folders + */ + public function deleteFolders($folders = array()) + { + foreach ($folders as $folder) + { + if (!is_dir($folder)) + { + continue; + } + + Folder::delete($folder); + } + } + + public function dropIndex($table, $index) + { + $db = $this->db; + + // Check if index exists first + $query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index); + $db->setQuery($query); + $db->execute(); + + if (!$db->loadResult()) + { + return; + } + + // Remove index + $query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index); + $db->setQuery($query); + $db->execute(); + } + + public function dropUnwantedTables($tables) { + + if (!$tables) { + return; + } + + foreach ($tables as $table) { + $query = "DROP TABLE IF EXISTS #__".$this->db->escape($table); + $this->db->setQuery($query); + $this->db->execute(); + } + } + + public function dropUnwantedColumns($table, $columns) { + + if (!$columns || !$table) { + return; + } + + $db = $this->db; + + // Check if columns exists in database + function qt($n) { + return(Factory::getDBO()->quote($n)); + } + + $query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')'; + $db->setQuery($query); + $rows = $db->loadColumn(0); + + // Abort if we don't have any rows + if (!$rows) { + return; + } + + // Let's remove the columns + $q = ""; + foreach ($rows as $key => $column) { + $comma = (($key+1) < count($rows)) ? "," : ""; + $q .= "drop ".$this->db->escape($column).$comma; + } + + $query = "alter table #__".$table." $q"; + + $db->setQuery($query); + $db->execute(); + } + + public function fetch($table, $columns = "*", $where = null, $singlerow = false) { + if (!$table) { + return; + } + + $db = $this->db; + $query = $db->getQuery(true); + + $query + ->select($columns) + ->from("#__$table"); + + if (isset($where)) { + $query->where("$where"); + } + + $db->setQuery($query); + + return ($singlerow) ? $db->loadObject() : $db->loadObjectList(); + } + + /** + * Load the Novarain Framework + * + * @return boolean + */ + public function loadFramework() + { + if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php')) + { + include_once JPATH_PLUGINS . '/system/nrframework/autoload.php'; + } + } + + /** + * Re-orders plugin after passed array of plugins + * + * @param string $plugin Plugin element name + * @param array $lowerPluginOrder Array of plugin element names + * + * @return boolean + */ + public function pluginOrderAfter($lowerPluginOrder) + { + + if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder)) + { + return; + } + + $db = $this->db; + + // Get plugins max order + $query = $db->getQuery(true); + $query + ->select($db->quoteName('b.ordering')) + ->from($db->quoteName('#__extensions', 'b')) + ->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")') + ->order('b.ordering desc'); + + $db->setQuery($query); + $maxOrder = $db->loadResult(); + + if (is_null($maxOrder)) + { + return; + } + + // Get plugin details + $query + ->clear() + ->select(array($db->quoteName('extension_id'), $db->quoteName('ordering'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($this->alias)); + + $db->setQuery($query); + $pluginInfo = $db->loadObject(); + + if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder) + { + return; + } + + // Update the new plugin order + $object = new stdClass(); + $object->extension_id = $pluginInfo->extension_id; + $object->ordering = ($maxOrder + 1); + + try { + $db->updateObject('#__extensions', $object, 'extension_id'); + } catch (Exception $e) { + return $e->getMessage(); + } + } +} diff --git a/plugins/system/tgeoip/script.install.php b/plugins/system/tgeoip/script.install.php new file mode 100644 index 00000000..3f134af1 --- /dev/null +++ b/plugins/system/tgeoip/script.install.php @@ -0,0 +1,19 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later +*/ + +defined('_JEXEC') or die; + +require_once __DIR__ . '/script.install.helper.php'; + +class PlgSystemTGeoIPInstallerScript extends PlgSystemTGeoIPInstallerScriptHelper +{ + public $name = 'TGEOIP'; + public $alias = 'tgeoip'; + public $extension_type = 'plugin'; + public $show_message = false; +} diff --git a/plugins/system/tgeoip/tgeoip.php b/plugins/system/tgeoip/tgeoip.php new file mode 100644 index 00000000..414f71ef --- /dev/null +++ b/plugins/system/tgeoip/tgeoip.php @@ -0,0 +1,121 @@ + + * @link http://www.tassos.gr + * @copyright Copyright © 2024 Tassos Marinos All Rights Reserved + * @license GNU GPLv3 or later + */ + +defined( '_JEXEC' ) or die( 'Restricted access' ); + +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Language\Text; + +class plgSystemTGeoIP extends CMSPlugin +{ + /** + * Joomla Application Object + * + * @var object + */ + protected $app; + + /** + * Auto load plugin language + * + * @var boolean + */ + protected $autoloadLanguage = true; + + /** + * GeoIP Class + * + * @var object + */ + private $geoIP; + + /** + * Load GeoIP Classes + * + * @return void + */ + private function loadGeoIP() + { + $path = JPATH_PLUGINS . '/system/tgeoip'; + + if (!class_exists('TGeoIP')) + { + if (@file_exists($path . '/helper/tgeoip.php')) + { + if (@include_once($path . '/vendor/autoload.php')) + { + @include_once $path . '/helper/tgeoip.php'; + } + } + } + + $this->geoIP = new TGeoIP(); + } + + /** + * Listens to AJAX requests on ?option=com_ajax&format=raw&plugin=tgeoip + * + * @return void + */ + public function onAjaxTgeoip() + { + Session::checkToken('request') or die('Invalid Token'); + + // Only in admin + if (!$this->app->isClient('administrator')) + { + return; + } + + $this->loadGeoIP(); + + $task = $this->app->input->get('task', 'update'); + + $this->geoIP->setKey($this->app->input->get('license_key', '')); + + switch ($task) + { + // Update database and redirect + case 'update-red': + + $result = $this->geoIP->updateDatabase(); + + if ($result === true) + { + $msg = Text::_('PLG_SYSTEM_TGEOIP_DATABASE_UPDATED'); + $msgType = 'message'; + } else + { + $msgType = 'error'; + $msg = $result; + } + + $return = base64_decode($this->app->input->get->getBase64('return', null)); + + $this->app->enqueueMessage($msg, $msgType); + $this->app->redirect($return); + break; + + // Update database + case 'update': + echo $this->geoIP->updateDatabase(); + break; + + // IP Lookup + case 'get': + $ip = $this->app->input->get('ip'); + echo json_encode($this->geoIP->setIP($ip)->getRecord()); + break; + } + } +} diff --git a/plugins/system/tgeoip/tgeoip.xml b/plugins/system/tgeoip/tgeoip.xml new file mode 100644 index 00000000..41b9a543 --- /dev/null +++ b/plugins/system/tgeoip/tgeoip.xml @@ -0,0 +1,62 @@ + + + plg_system_tgeoip + PLG_SYSTEM_TGEOIP_DESC + 2.2.5 + 06 Mar 2017 + Tassos Marinos + Copyright © 2024 Tassos Marinos All Rights Reserved + http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL + info@tassos.gr + http://www.tassos.gr + script.install.php + + tgeoip.php + script.install.helper.php + db + field + helper + language + vendor + + + +
+ + + + + + + + +
+
+
+
diff --git a/plugins/system/tgeoip/vendor/autoload.php b/plugins/system/tgeoip/vendor/autoload.php new file mode 100644 index 00000000..5ccb30c5 --- /dev/null +++ b/plugins/system/tgeoip/vendor/autoload.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/plugins/system/tgeoip/vendor/composer/InstalledVersions.php b/plugins/system/tgeoip/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000..d7583fb3 --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/InstalledVersions.php @@ -0,0 +1,313 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Tassos\Vendor\Composer; + +use Tassos\Vendor\Composer\Autoload\ClassLoader; +use Tassos\Vendor\Composer\Semver\VersionParser; +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + /** + * @var bool|null + */ + private static $canGetVendors; + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = \array_keys($installed['versions']); + } + if (1 === \count($packages)) { + return $packages[0]; + } + return \array_keys(\array_flip(\call_user_func_array('array_merge', $packages))); + } + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + return $packagesByType; + } + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = \true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === \false; + } + } + return \false; + } + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + return $provided->matches($constraint); + } + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (\array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = \array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (\array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = \array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (\array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = \array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + return \implode(' || ', $ranges); + } + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + return $installed['versions'][$packageName]['version']; + } + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + return $installed['versions'][$packageName]['pretty_version']; + } + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + return $installed['versions'][$packageName]['reference']; + } + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + return $installed[0]['root']; + } + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @\trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', \E_USER_DEPRECATED); + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (\substr(__DIR__, -8, 1) !== 'C') { + self::$installed = (include __DIR__ . '/installed.php'); + } else { + self::$installed = array(); + } + } + return self::$installed; + } + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = \method_exists('Tassos\\Vendor\\Composer\\Autoload\\ClassLoader', 'getRegisteredLoaders'); + } + $installed = array(); + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (\is_file($vendorDir . '/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = (require $vendorDir . '/composer/installed.php'); + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && \strtr($vendorDir . '/composer', '\\', '/') === \strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[\count($installed) - 1]; + } + } + } + } + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (\substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = (require __DIR__ . '/installed.php'); + self::$installed = $required; + } else { + self::$installed = array(); + } + } + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + return $installed; + } +} diff --git a/plugins/system/tgeoip/vendor/composer/autoload_classmap.php b/plugins/system/tgeoip/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000..0fb0a2c1 --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/plugins/system/tgeoip/vendor/composer/autoload_namespaces.php b/plugins/system/tgeoip/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000..15a2ff3a --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/splitbrain/php-archive/src'), + 'Tassos\\Vendor\\MaxMind\\WebService\\' => array($vendorDir . '/maxmind/web-service-common/src/WebService'), + 'Tassos\\Vendor\\MaxMind\\Exception\\' => array($vendorDir . '/maxmind/web-service-common/src/Exception'), + 'Tassos\\Vendor\\MaxMind\\Db\\' => array($vendorDir . '/maxmind-db/reader/src/MaxMind/Db'), + 'Tassos\\Vendor\\GeoIp2\\' => array($vendorDir . '/geoip2/geoip2/src'), + 'Tassos\\Vendor\\Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'), +); diff --git a/plugins/system/tgeoip/vendor/composer/autoload_real.php b/plugins/system/tgeoip/vendor/composer/autoload_real.php new file mode 100644 index 00000000..456812cb --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/autoload_real.php @@ -0,0 +1,38 @@ +register(true); + + return $loader; + } +} diff --git a/plugins/system/tgeoip/vendor/composer/autoload_static.php b/plugins/system/tgeoip/vendor/composer/autoload_static.php new file mode 100644 index 00000000..6c061bc5 --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/autoload_static.php @@ -0,0 +1,61 @@ + + array ( + 'Tassos\\Vendor\\splitbrain\\PHPArchive\\' => 36, + 'Tassos\\Vendor\\MaxMind\\WebService\\' => 33, + 'Tassos\\Vendor\\MaxMind\\Exception\\' => 32, + 'Tassos\\Vendor\\MaxMind\\Db\\' => 25, + 'Tassos\\Vendor\\GeoIp2\\' => 21, + 'Tassos\\Vendor\\Composer\\CaBundle\\' => 32, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Tassos\\Vendor\\splitbrain\\PHPArchive\\' => + array ( + 0 => __DIR__ . '/..' . '/splitbrain/php-archive/src', + ), + 'Tassos\\Vendor\\MaxMind\\WebService\\' => + array ( + 0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/WebService', + ), + 'Tassos\\Vendor\\MaxMind\\Exception\\' => + array ( + 0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/Exception', + ), + 'Tassos\\Vendor\\MaxMind\\Db\\' => + array ( + 0 => __DIR__ . '/..' . '/maxmind-db/reader/src/MaxMind/Db', + ), + 'Tassos\\Vendor\\GeoIp2\\' => + array ( + 0 => __DIR__ . '/..' . '/geoip2/geoip2/src', + ), + 'Tassos\\Vendor\\Composer\\CaBundle\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/ca-bundle/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/plugins/system/tgeoip/vendor/composer/ca-bundle/res/cacert.pem b/plugins/system/tgeoip/vendor/composer/ca-bundle/res/cacert.pem new file mode 100644 index 00000000..2ae7b6cb --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/ca-bundle/res/cacert.pem @@ -0,0 +1,3372 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Jan 10 04:12:06 2023 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.29. +## SHA256: 90c470e705b4b5f36f09684dc50e2b79c8b86989a848b62cd1a7bd6460ee65f6 +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +GDCA TrustAUTH R5 ROOT +====================== +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw +BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD +DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow +YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs +AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p +OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr +pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ +9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ +xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM +R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ +D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 +oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx +9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 +H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 +6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd ++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ +HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD +F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ +8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv +/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT +aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +SSL.com Root Certification Authority RSA +======================================== +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM +BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x +MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw +MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM +LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C +Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 +P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge +oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp +k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z +fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ +gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 +UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 +1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s +bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr +dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf +ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl +u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq +erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj +MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ +vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI +Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y +wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI +WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +SSL.com Root Certification Authority ECC +======================================== +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv +BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy +MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO +BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ +8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR +hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT +jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW +e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z +5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority RSA R2 +============================================== +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w +DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u +MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD +VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh +hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w +cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO +Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ +B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh +CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim +9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto +RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm +JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 ++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp +qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 +++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx +Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G +guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz +OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 +CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq +lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR +rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 +hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX +9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority ECC +=========================================== +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy +BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw +MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM +LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy +3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O +BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe +5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ +N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm +m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +emSign Root CA - G1 +=================== +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET +MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl +ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx +ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk +aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN +LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1 +cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW +DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ +6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH +hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2 +vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q +NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q ++Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih +U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +emSign ECC Root CA - G3 +======================= +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG +A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg +MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4 +MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11 +ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc +58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr +MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D +CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7 +jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +emSign Root CA - C1 +=================== +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx +EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp +Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD +ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up +ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/ +Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX +OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V +I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms +lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+ +XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp +/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1 +NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9 +wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ +BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +emSign ECC Root CA - C3 +======================= +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG +A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF +Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD +ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd +6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9 +SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA +B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA +MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU +ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 3 +======================= +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG +A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK +Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2 +MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv +bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX +SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz +iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf +jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim +5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe +sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj +0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/ +JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u +y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h ++bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG +xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID +AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN +AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw +W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld +y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov ++BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc +eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw +9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7 +nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY +hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB +60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq +dBb9HxEGmpv0 +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G4 +========================================= +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu +bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1 +dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT +AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D +umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV +3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds +8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ +e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7 +ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X +xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV +7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW +Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n +MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q +jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht +7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK +YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt +jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+ +m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW +RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA +JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G ++TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT +kcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +Microsoft ECC Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND +IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4 +MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6 +thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB +eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM ++Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf +Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR +eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +Microsoft RSA Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg +UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw +NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml +7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e +S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7 +1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+ +dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F +yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS +MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr +lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ +0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ +ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og +6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80 +dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk ++ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex +/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy +AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW +ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE +7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT +c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D +5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +e-Szigno Root CA 2017 +===================== +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw +DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt +MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa +Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE +CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp +Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx +s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G +A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv +vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA +tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO +svxyqltZ+efcMQ== +-----END CERTIFICATE----- + +certSIGN Root CA G2 +=================== +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw +EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy +MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH +TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05 +N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk +abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg +wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp +dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh +ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732 +jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf +95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc +z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL +iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB +ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB +/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5 +8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5 +BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW +atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU +Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M +NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N +0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +Trustwave Global Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29 +zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf +LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq +stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o +WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+ +OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40 +Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE +uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm ++9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj +ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H +PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H +ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla +4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R +vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd +zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O +856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH +Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu +3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP +29FpHOTKyeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +Trustwave Global ECC P256 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1 +NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj +43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm +P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt +0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz +RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +Trustwave Global ECC P384 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4 +NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH +Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr +/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV +HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn +ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl +CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw== +-----END CERTIFICATE----- + +NAVER Global Root Certification Authority +========================================= +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTELMAkG +A1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIwMAYDVQQD +DClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4 +NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVT +UyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVAiQqrDZBb +UGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH38dq6SZeWYp34+hInDEW ++j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lEHoSTGEq0n+USZGnQJoViAbbJAh2+g1G7 +XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2 +aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4 +Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3z +VHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53B +A0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZai +cdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejy +YhbLgGvtPe31HzClrkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNV +HQ4EFgQU0p+I36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK +21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB +jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bx +hYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTg +E34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTH +D8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8piKCk5XQ +A76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLRLBT/DShycpWbXgnbiUSY +qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG +I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg +kpzNNIaRkPpkUZ3+/uul9XXeifdy +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM SERVIDORES SEGUROS +=================================== +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF +UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy +NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4 +MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt +UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB +QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2 +LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG +SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD +zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c= +-----END CERTIFICATE----- + +GlobalSign Root R46 +=================== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv +b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX +BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es +CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/ +r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje +2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt +bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj +K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4 +12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on +ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls +eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9 +vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM +BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy +gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92 +CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm +OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq +JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye +qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz +nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7 +DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3 +QEUxeCp6 +-----END CERTIFICATE----- + +GlobalSign Root E46 +=================== +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT +AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg +RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV +BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB +jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj +QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL +gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk +vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ +CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +GLOBALTRUST 2020 +================ +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx +IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT +VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh +BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy +MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi +D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO +VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM +CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm +fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA +A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR +JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG +DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU +clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ +mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud +IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw +4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 +iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS +8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 +HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS +vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 +oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF +YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl +gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +ANF Secure Server Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4 +NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv +bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg +Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw +MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw +EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz +BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv +T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv +B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse +zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM +VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j +7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z +JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe +8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO +Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ +UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx +j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt +dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM +5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb +5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54 +EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H +hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy +g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3 +r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +Certum EC-384 CA +================ +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ +TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2 +MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh +dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq +vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn +iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo +ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0 +QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +Certum Trusted Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG +EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew +HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY +QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p +fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52 +HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2 +fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt +g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4 +NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk +fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ +P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY +njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK +HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL +LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s +ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K +h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8 +CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA +4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo +WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj +6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT +OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck +bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +TunTrust Root CA +================ +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG +A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj +dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw +NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD +ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz +2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b +bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7 +NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd +gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW +VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f +Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ +juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas +DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS +VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI +04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl +0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd +Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY +YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp +adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x +xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP +jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM +MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z +ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r +AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +HARICA TLS RSA Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz +OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl +bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB +IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN +JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu +a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y +Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K +5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv +dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR +0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH +GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm +haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ +CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU +EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq +QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD +QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR +j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 +vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 +qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 +Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ +PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn +kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= +-----END CERTIFICATE----- + +HARICA TLS ECC Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH +UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD +QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX +DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj +IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv +b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l +AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b +ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW +0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi +rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw +CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1Ud +DgQWBBRlzeurNR4APn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4w +gZswgZgGBFUdIAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4A +bwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL +4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDb +LIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1il +I45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZEEAEeiGaP +cjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV1aUsIC+nmCjuRfzxuIgA +LI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2ebKHTEm9caPARYpoKdrcd7b/+A +lun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH +9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpf +NIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqE +ZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +vTrus ECC Root CA +================= +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkGA1UE +BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBS +b290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAa +BgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+c +ToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUde4BdS49n +TPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwV53dVvHH4+m4SVBrm2nDb+zDfSXkV5UT +QJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQL +YgmRWAD5Tfs0aNoJrSEGGJTO +-----END CERTIFICATE----- + +vTrus Root CA +============= +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzELMAkG +A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZUcnVzIFJv +b3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoG +A1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZots +SKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykUAyyNJJrI +ZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+GrPSbcKvdmaVayqwlHeF +XgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z98Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KA +YPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70 +kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2 +AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu +/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu +1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO +9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYg +scasGrz2iTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOC +AgEAKbqSSaet8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjr +jld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4 +8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJn +xDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554Wg +icEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4 +sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNBUvupLnKW +nyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1PTi07NEPhmg4NpGaXutIc +SkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy6K2ir40zSbofitzmdHxghm+H +l3s= +-----END CERTIFICATE----- + +ISRG Root X2 +============ +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJV +UzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElT +UkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVT +MSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNS +RyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H +ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppb +d9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtF +cP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5 +U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +HiPKI Root CA - G1 +================== +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xGzAZBgNVBAMMEkhpUEtJ +IFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRaFw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYT +AlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kg +Um9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0 +o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twvVcg3Px+k +wJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SE +YYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsA +GJZMoYFL3QRtU6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd +hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj +1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDry+K4 +9a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/ +Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF +8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQD +AgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqcSE5XCV0vrPSl +tJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6FzaZsT0pPBWGTMpWmWSBUdGSquE +wx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9TcXzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07Q +JNBAsNB1CI69aO4I1258EHBGG3zgiLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv +5wiZqAxeJoBF1PhoL5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+Gpz +jLrFNe85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wrkkVbbiVg +hUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+vhV4nYWBSipX3tUZQ9rb +yltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQUYDksswBVLuT1sw5XxJFBAJw/6KXf6vb/ +yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtHbG9i +YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9i +YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkW +ymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/+wpu+74zyTyjhNUwCgYI +KoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147bmF0774BxL4YSFlhgjICICadVGNA3jdg +UM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM +f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7raKb0 +xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnWr4+w +B7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXW +nOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk +9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zq +kUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1A +K/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPX +V2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDW +cfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQAD +ggIBAJ+qQibbC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRi +ClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar +J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYci +NuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5me +LMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJF +fbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ0E6yove+ +7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm2tIMPNuzjsmhDYAPexZ3 +FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3 +gm3c +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv +CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl +e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb +a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS ++LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M +kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG +r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q +S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV +J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL +dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD +ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh +swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel +/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn +jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5 +9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M +7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8 +0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR +WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW +HYbL +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi +MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMw +HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ +R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout +736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24CejQjBA +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP0/Eq +Er24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azT +L818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV +11RZt+cRLInUue4X +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi +MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQw +HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ +R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu +hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqjQjBA +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV2Py1 +PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C +r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh +4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +Telia Root CA v2 +================ +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQxCzAJBgNVBAYT +AkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2 +MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQK +DBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ7 +6zBqAMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9vVYiQJ3q +9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9lRdU2HhE8Qx3FZLgmEKn +pNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTODn3WhUidhOPFZPY5Q4L15POdslv5e2QJl +tI5c0BE0312/UqeBAMN/mUWZFdUXyApT7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW +5olWK8jjfN7j/4nlNW4o6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNr +RBH0pUPCTEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6WT0E +BXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63RDolUK5X6wK0dmBR4 +M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZIpEYslOqodmJHixBTB0hXbOKSTbau +BcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGjYzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7W +xy+G2CQ5MB0GA1UdDgQWBBRyrOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi0f6X+J8wfBj5 +tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMMA8iZGok1GTzTyVR8qPAs5m4H +eW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBSSRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+C +y748fdHif64W1lZYudogsYMVoe+KTTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygC +QMez2P2ccGrGKMOF6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15 +h2Er3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMtTy3EHD70 +sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pTVmBds9hCG1xLEooc6+t9 +xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAWysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQ +raVplI/owd8k+BsHMYeB2F326CjYSlKArBPuUBQemMc= +-----END CERTIFICATE----- + +D-TRUST BR Root CA 1 2020 +========================= +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE +RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0EgMSAy +MDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNV +BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7 +dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0QVK5buXu +QqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/VbNafAkl1bK6CKBrqx9t +MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu +bmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP +PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD +AwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70Pvom +AjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +D-TRUST EV Root CA 1 2020 +========================= +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE +RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0EgMSAy +MDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNV +BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8 +ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rDwpdhQntJ +raOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3OqQo5FD4pPfsazK2/umL +MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu +bmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP +PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD +AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR +AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +DigiCert TLS ECC P384 Root G5 +============================= +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBFQ0MgUDM4 +NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQg +Um9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1Tzvd +lHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp0zVozptj +n4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICISB4CIfBFqMA4GA1UdDwEB +/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCJao1H5+z8blUD2Wds +Jk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIx +AJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +DigiCert TLS RSA4096 Root G5 +============================ +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYDVQQG +EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0 +MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2 +IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS8 +7IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG02C+JFvuU +AT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgpwgscONyfMXdcvyej/Ces +tyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZMpG2T6T867jp8nVid9E6P/DsjyG244gXa +zOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnV +DdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9q +TXeXAaDxZre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvy +z6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/ +MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIk +wcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4E +FgQUUTMc7TZArxfTJc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShN +lnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN +MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/ +u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9G +OUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh +47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilwMUc/dNAU +FvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WFqUITVuwhd4GTWgzqltlJ +yqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWFJqgejF0pW8hL2JpqA15w8oVP +bEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +Certainly Root R1 +================= +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE +BhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3QgUjEwHhcN +MjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2Vy +dGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O +5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbedaFySpvXl +8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b01C7jcvk2xusVtyWMOvwl +DbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGI +XsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkN +KPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQ +AjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2Cnb +rlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1 +VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprS +p6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTgqj8ljZ9EXME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAsz +HQNTVfSVcOQrPbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4v +MMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB +GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+ +gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgH +JBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7 +fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLyyCwzk5Iw +x06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5nwXARPbv0+Em34yaXOp/S +X3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +Certainly Root E1 +================= +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQGEwJV +UzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAeFw0yMTA0 +MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlu +bHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4 +fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9 +YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4hevIIgcwCgYIKoZIzj0E +AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8 +rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +E-Tugra Global Root CA RSA v3 +============================= +-----BEGIN CERTIFICATE----- +MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQELBQAwgYAxCzAJ +BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAb +BgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290 +IENBIFJTQSB2MzAeFw0yMDAzMTgwOTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJU +UjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRF +LVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBSU0Eg +djMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J77gnJY9LTQ91ew6aEOErx +jYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscxuj7X/iWpKo429NEvx7epXTPcMHD4QGxL +sqYxYdE0PD0xesevxKenhOGXpOhL9hd87jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF +/YP9f4RtNGx/ardLAQO/rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8q +QedmCeFLl+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bGwzrw +bMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4znKS4iicvObpCdg6 +04nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBOM/J+JjKsBY04pOZ2PJ8QaQ5tndLB +eSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiM +bIedBi3x7+PmBvrFZhNb/FAHnnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbg +h3cXTJ2w2AmoDVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSytK7mLfcm1ap1 +LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAImocn+M684uGMQQ +gC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN4 +38o2Fi+CiJ+8EUdPdk3ILY7r3y18Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/q +ln0F7psTpURs+APQ3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3s +SdPkvmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn99t2HVhjY +sCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQmhty3QUBjYZgv6Rn7rWl +DdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YAVSgU7NbHEqIbZULpkejLPoeJVF3Zr52X +nGnnCv8PWniLYypMfUeUP95L6VPQMPHF9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFH +IK+WEj5jlB0E5y67hscMmoi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiX +YY60MGo8bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ +-----END CERTIFICATE----- + +E-Tugra Global Root CA ECC v3 +============================= +-----BEGIN CERTIFICATE----- +MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV +BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB +IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP +MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 +Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2 +w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31 +Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ +zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO +PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W +Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3 +-----END CERTIFICATE----- + +Security Communication RootCA3 +============================== +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNVBAYTAkpQMSUw +IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQDEx5TZWN1cml0eSBD +b21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQsw +CQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UE +AxMeU2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4rCmDvu20r +hvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzAlrenfna84xtSGc4RHwsE +NPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MGTfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2 +/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF79+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGm +npjKIG58u4iFW/vAEGK78vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtY +XLVqAvO4g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3weGVPK +p7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst+3A7caoreyYn8xrC +3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M0V9hvqG8OmpI6iZVIhZdXw3/JzOf +GAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQT9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0Vcw +CBEF/VfR2ccCAwEAAaNCMEAwHQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PAFNr0Y/Dq9HHu +Tofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd9XbXv8S2gVj/yP9kaWJ5rW4O +H3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQIUYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASx +YfQAW0q3nHE3GYV5v4GwxxMOdnE+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZ +XSEIx2C/pHF7uNkegr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml ++LLfiAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUVnuiZIesn +KwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD2NCcnWXL0CsnMQMeNuE9 +dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm +6Vwdp6POXiUyK+OVrCoHzrQoeIY8LaadTdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +Security Communication ECC RootCA1 +================================== +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUwIwYD +VQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0eSBDb21t +dW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTEL +MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNV +BAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+CnnfdldB9sELLo +5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpKULGjQjBAMB0GA1UdDgQW +BBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3L +snNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70e +N9k= +-----END CERTIFICATE----- diff --git a/plugins/system/tgeoip/vendor/composer/ca-bundle/src/CaBundle.php b/plugins/system/tgeoip/vendor/composer/ca-bundle/src/CaBundle.php new file mode 100644 index 00000000..690e771d --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/ca-bundle/src/CaBundle.php @@ -0,0 +1,361 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ +namespace Tassos\Vendor\Composer\CaBundle; + +use Tassos\Vendor\Psr\Log\LoggerInterface; +use Tassos\Vendor\Symfony\Component\Process\PhpProcess; +/** + * @author Chris Smith + * @author Jordi Boggiano + */ +class CaBundle +{ + /** @var string|null */ + private static $caPath; + /** @var array */ + private static $caFileValidity = array(); + /** @var bool|null */ + private static $useOpensslParse; + /** + * Returns the system CA bundle path, or a path to the bundled one + * + * This method was adapted from Sslurp. + * https://github.com/EvanDotPro/Sslurp + * + * (c) Evan Coury + * + * For the full copyright and license information, please see below: + * + * Copyright (c) 2013, Evan Coury + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @param LoggerInterface $logger optional logger for information about which CA files were loaded + * @return string path to a CA bundle file or directory + */ + public static function getSystemCaRootBundlePath(LoggerInterface $logger = null) + { + if (self::$caPath !== null) { + return self::$caPath; + } + $caBundlePaths = array(); + // If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that. + // This mimics how OpenSSL uses the SSL_CERT_FILE env variable. + $caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE'); + // If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that. + // This mimics how OpenSSL uses the SSL_CERT_FILE env variable. + $caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR'); + $caBundlePaths[] = \ini_get('openssl.cafile'); + $caBundlePaths[] = \ini_get('openssl.capath'); + $otherLocations = array( + '/etc/pki/tls/certs/ca-bundle.crt', + // Fedora, RHEL, CentOS (ca-certificates package) + '/etc/ssl/certs/ca-certificates.crt', + // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package) + '/etc/ssl/ca-bundle.pem', + // SUSE, openSUSE (ca-certificates package) + '/usr/local/share/certs/ca-root-nss.crt', + // FreeBSD (ca_root_nss_package) + '/usr/ssl/certs/ca-bundle.crt', + // Cygwin + '/opt/local/share/curl/curl-ca-bundle.crt', + // OS X macports, curl-ca-bundle package + '/usr/local/share/curl/curl-ca-bundle.crt', + // Default cURL CA bunde path (without --with-ca-bundle option) + '/usr/share/ssl/certs/ca-bundle.crt', + // Really old RedHat? + '/etc/ssl/cert.pem', + // OpenBSD + '/usr/local/etc/ssl/cert.pem', + // FreeBSD 10.x + '/usr/local/etc/openssl/cert.pem', + // OS X homebrew, openssl package + '/usr/local/etc/openssl@1.1/cert.pem', + ); + foreach ($otherLocations as $location) { + $otherLocations[] = \dirname($location); + } + $caBundlePaths = \array_merge($caBundlePaths, $otherLocations); + foreach ($caBundlePaths as $caBundle) { + if ($caBundle && self::caFileUsable($caBundle, $logger)) { + return self::$caPath = $caBundle; + } + if ($caBundle && self::caDirUsable($caBundle, $logger)) { + return self::$caPath = $caBundle; + } + } + return self::$caPath = static::getBundledCaBundlePath(); + // Bundled CA file, last resort + } + /** + * Returns the path to the bundled CA file + * + * In case you don't want to trust the user or the system, you can use this directly + * + * @return string path to a CA bundle file + */ + public static function getBundledCaBundlePath() + { + $caBundleFile = __DIR__ . '/../res/cacert.pem'; + // cURL does not understand 'phar://' paths + // see https://github.com/composer/ca-bundle/issues/10 + if (0 === \strpos($caBundleFile, 'phar://')) { + $tempCaBundleFile = \tempnam(\sys_get_temp_dir(), 'openssl-ca-bundle-'); + if (\false === $tempCaBundleFile) { + throw new \RuntimeException('Could not create a temporary file to store the bundled CA file'); + } + \file_put_contents($tempCaBundleFile, \file_get_contents($caBundleFile)); + \register_shutdown_function(function () use($tempCaBundleFile) { + @\unlink($tempCaBundleFile); + }); + $caBundleFile = $tempCaBundleFile; + } + return $caBundleFile; + } + /** + * Validates a CA file using opensl_x509_parse only if it is safe to use + * + * @param string $filename + * @param LoggerInterface $logger optional logger for information about which CA files were loaded + * + * @return bool + */ + public static function validateCaFile($filename, LoggerInterface $logger = null) + { + static $warned = \false; + if (isset(self::$caFileValidity[$filename])) { + return self::$caFileValidity[$filename]; + } + $contents = \file_get_contents($filename); + // assume the CA is valid if php is vulnerable to + // https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html + if (!static::isOpensslParseSafe()) { + if (!$warned && $logger) { + $logger->warning(\sprintf('Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.', \PHP_VERSION)); + $warned = \true; + } + $isValid = !empty($contents); + } elseif (\is_string($contents) && \strlen($contents) > 0) { + $contents = \preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents); + if (null === $contents) { + // regex extraction failed + $isValid = \false; + } else { + $isValid = (bool) \openssl_x509_parse($contents); + } + } else { + $isValid = \false; + } + if ($logger) { + $logger->debug('Checked CA file ' . \realpath($filename) . ': ' . ($isValid ? 'valid' : 'invalid')); + } + return self::$caFileValidity[$filename] = $isValid; + } + /** + * Test if it is safe to use the PHP function openssl_x509_parse(). + * + * This checks if OpenSSL extensions is vulnerable to remote code execution + * via the exploit documented as CVE-2013-6420. + * + * @return bool + */ + public static function isOpensslParseSafe() + { + if (null !== self::$useOpensslParse) { + return self::$useOpensslParse; + } + if (\PHP_VERSION_ID >= 50600) { + return self::$useOpensslParse = \true; + } + // Vulnerable: + // PHP 5.3.0 - PHP 5.3.27 + // PHP 5.4.0 - PHP 5.4.22 + // PHP 5.5.0 - PHP 5.5.6 + if (\PHP_VERSION_ID < 50400 && \PHP_VERSION_ID >= 50328 || \PHP_VERSION_ID < 50500 && \PHP_VERSION_ID >= 50423 || \PHP_VERSION_ID >= 50507) { + // This version of PHP has the fix for CVE-2013-6420 applied. + return self::$useOpensslParse = \true; + } + if (\defined('PHP_WINDOWS_VERSION_BUILD')) { + // Windows is probably insecure in this case. + return self::$useOpensslParse = \false; + } + $compareDistroVersionPrefix = function ($prefix, $fixedVersion) { + $regex = '{^' . \preg_quote($prefix) . '([0-9]+)$}'; + if (\preg_match($regex, \PHP_VERSION, $m)) { + return (int) $m[1] >= $fixedVersion; + } + return \false; + }; + // Hard coded list of PHP distributions with the fix backported. + if ($compareDistroVersionPrefix('5.3.3-7+squeeze', 18) || $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) || $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9)) { + return self::$useOpensslParse = \true; + } + // Symfony Process component is missing so we assume it is unsafe at this point + if (!\class_exists('Tassos\\Vendor\\Symfony\\Component\\Process\\PhpProcess')) { + return self::$useOpensslParse = \false; + } + // This is where things get crazy, because distros backport security + // fixes the chances are on NIX systems the fix has been applied but + // it's not possible to verify that from the PHP version. + // + // To verify exec a new PHP process and run the issue testcase with + // known safe input that replicates the bug. + // Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415 + // changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593 + $cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K'; + $script = <<<'EOT' + +error_reporting(-1); +$info = openssl_x509_parse(base64_decode('%s')); +var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']); + +EOT; + $script = '<' . "?php\n" . \sprintf($script, $cert); + try { + $process = new PhpProcess($script); + $process->mustRun(); + } catch (\Exception $e) { + // In the case of any exceptions just accept it is not possible to + // determine the safety of openssl_x509_parse and bail out. + return self::$useOpensslParse = \false; + } + $output = \preg_split('{\\r?\\n}', \trim($process->getOutput())); + $errorOutput = \trim($process->getErrorOutput()); + if (\is_array($output) && \count($output) === 3 && $output[0] === \sprintf('string(%d) "%s"', \strlen(\PHP_VERSION), \PHP_VERSION) && $output[1] === 'string(27) "stefan.esser@sektioneins.de"' && $output[2] === 'int(-1)' && \preg_match('{openssl_x509_parse\\(\\): illegal (?:ASN1 data type for|length in) timestamp in - on line \\d+}', $errorOutput)) { + // This PHP has the fix backported probably by a distro security team. + return self::$useOpensslParse = \true; + } + return self::$useOpensslParse = \false; + } + /** + * Resets the static caches + * @return void + */ + public static function reset() + { + self::$caFileValidity = array(); + self::$caPath = null; + self::$useOpensslParse = null; + } + /** + * @param string $name + * @return string|false + */ + private static function getEnvVariable($name) + { + if (isset($_SERVER[$name])) { + return (string) $_SERVER[$name]; + } + if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== \false && $value !== null) { + return (string) $value; + } + return \false; + } + /** + * @param string|false $certFile + * @param LoggerInterface|null $logger + * @return bool + */ + private static function caFileUsable($certFile, LoggerInterface $logger = null) + { + return $certFile && static::isFile($certFile, $logger) && static::isReadable($certFile, $logger) && static::validateCaFile($certFile, $logger); + } + /** + * @param string|false $certDir + * @param LoggerInterface|null $logger + * @return bool + */ + private static function caDirUsable($certDir, LoggerInterface $logger = null) + { + return $certDir && static::isDir($certDir, $logger) && static::isReadable($certDir, $logger) && static::glob($certDir . '/*', $logger); + } + /** + * @param string $certFile + * @param LoggerInterface|null $logger + * @return bool + */ + private static function isFile($certFile, LoggerInterface $logger = null) + { + $isFile = @\is_file($certFile); + if (!$isFile && $logger) { + $logger->debug(\sprintf('Checked CA file %s does not exist or it is not a file.', $certFile)); + } + return $isFile; + } + /** + * @param string $certDir + * @param LoggerInterface|null $logger + * @return bool + */ + private static function isDir($certDir, LoggerInterface $logger = null) + { + $isDir = @\is_dir($certDir); + if (!$isDir && $logger) { + $logger->debug(\sprintf('Checked directory %s does not exist or it is not a directory.', $certDir)); + } + return $isDir; + } + /** + * @param string $certFileOrDir + * @param LoggerInterface|null $logger + * @return bool + */ + private static function isReadable($certFileOrDir, LoggerInterface $logger = null) + { + $isReadable = @\is_readable($certFileOrDir); + if (!$isReadable && $logger) { + $logger->debug(\sprintf('Checked file or directory %s is not readable.', $certFileOrDir)); + } + return $isReadable; + } + /** + * @param string $pattern + * @param LoggerInterface|null $logger + * @return bool + */ + private static function glob($pattern, LoggerInterface $logger = null) + { + $certs = \glob($pattern); + if ($certs === \false) { + if ($logger) { + $logger->debug(\sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern)); + } + return \false; + } + if (\count($certs) === 0) { + if ($logger) { + $logger->debug(\sprintf("No CA files found for pattern: %s", $pattern)); + } + return \false; + } + return \true; + } +} diff --git a/plugins/system/tgeoip/vendor/composer/installed.json b/plugins/system/tgeoip/vendor/composer/installed.json new file mode 100644 index 00000000..bb0b6db7 --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/installed.json @@ -0,0 +1,327 @@ +{ + "packages": [ + { + "name": "composer\/ca-bundle", + "version": "1.3.5", + "version_normalized": "1.3.5.0", + "source": { + "type": "git", + "url": "https:\/\/github.com\/composer\/ca-bundle.git", + "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd" + }, + "dist": { + "type": "zip", + "url": "https:\/\/api.github.com\/repos\/composer\/ca-bundle\/zipball\/74780ccf8c19d6acb8d65c5f39cd72110e132bbd", + "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan\/phpstan": "^0.12.55", + "psr\/log": "^1.0", + "symfony\/phpunit-bridge": "^4.2 || ^5", + "symfony\/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "time": "2023-01-11T08:27:00+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Tassos\\Vendor\\Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https:\/\/packagist.org\/downloads\/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http:\/\/seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc:\/\/irc.freenode.org\/composer", + "issues": "https:\/\/github.com\/composer\/ca-bundle\/issues", + "source": "https:\/\/github.com\/composer\/ca-bundle\/tree\/1.3.5" + }, + "funding": [ + { + "url": "https:\/\/packagist.com", + "type": "custom" + }, + { + "url": "https:\/\/github.com\/composer", + "type": "github" + }, + { + "url": "https:\/\/tidelift.com\/funding\/github\/packagist\/composer\/composer", + "type": "tidelift" + } + ], + "install-path": ".\/ca-bundle" + }, + { + "name": "geoip2\/geoip2", + "version": "v2.13.0", + "version_normalized": "2.13.0.0", + "source": { + "type": "git", + "url": "git@github.com:maxmind\/GeoIP2-php.git", + "reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23" + }, + "dist": { + "type": "zip", + "url": "https:\/\/api.github.com\/repos\/maxmind\/GeoIP2-php\/zipball\/6a41d8fbd6b90052bc34dff3b4252d0f88067b23", + "reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23", + "shasum": "" + }, + "require": { + "ext-json": "*", + "maxmind-db\/reader": "~1.8", + "maxmind\/web-service-common": "~0.8", + "php": ">=7.2" + }, + "require-dev": { + "friendsofphp\/php-cs-fixer": "3.*", + "phpstan\/phpstan": "*", + "phpunit\/phpunit": "^8.0 || ^9.0", + "squizlabs\/php_codesniffer": "3.*" + }, + "time": "2022-08-05T20:32:58+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Tassos\\Vendor\\GeoIp2\\": "src" + } + }, + "notification-url": "https:\/\/packagist.org\/downloads\/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https:\/\/www.maxmind.com\/" + } + ], + "description": "MaxMind GeoIP2 PHP API", + "homepage": "https:\/\/github.com\/maxmind\/GeoIP2-php", + "keywords": [ + "IP", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "install-path": "..\/geoip2\/geoip2" + }, + { + "name": "maxmind-db\/reader", + "version": "v1.11.0", + "version_normalized": "1.11.0.0", + "source": { + "type": "git", + "url": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php.git", + "reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b" + }, + "dist": { + "type": "zip", + "url": "https:\/\/api.github.com\/repos\/maxmind\/MaxMind-DB-Reader-php\/zipball\/b1f3c0699525336d09cc5161a2861268d9f2ae5b", + "reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "conflict": { + "ext-maxminddb": "<1.10.1,>=2.0.0" + }, + "require-dev": { + "friendsofphp\/php-cs-fixer": "3.*", + "php-coveralls\/php-coveralls": "^2.1", + "phpstan\/phpstan": "*", + "phpunit\/phpcov": ">=6.0.0", + "phpunit\/phpunit": ">=8.0.0,<10.0.0", + "squizlabs\/php_codesniffer": "3.*" + }, + "suggest": { + "ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups" + }, + "time": "2021-10-18T15:23:10+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Tassos\\Vendor\\MaxMind\\Db\\": "src\/MaxMind\/Db" + } + }, + "notification-url": "https:\/\/packagist.org\/downloads\/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https:\/\/www.maxmind.com\/" + } + ], + "description": "MaxMind DB Reader API", + "homepage": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php", + "keywords": [ + "database", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "support": { + "issues": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php\/issues", + "source": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php\/tree\/v1.11.0" + }, + "install-path": "..\/maxmind-db\/reader" + }, + { + "name": "maxmind\/web-service-common", + "version": "v0.9.0", + "version_normalized": "0.9.0.0", + "source": { + "type": "git", + "url": "https:\/\/github.com\/maxmind\/web-service-common-php.git", + "reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53" + }, + "dist": { + "type": "zip", + "url": "https:\/\/api.github.com\/repos\/maxmind\/web-service-common-php\/zipball\/4dc5a3e8df38aea4ca3b1096cee3a038094e9b53", + "reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53", + "shasum": "" + }, + "require": { + "composer\/ca-bundle": "^1.0.3", + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.2" + }, + "require-dev": { + "friendsofphp\/php-cs-fixer": "3.*", + "phpstan\/phpstan": "*", + "phpunit\/phpunit": "^8.0 || ^9.0", + "squizlabs\/php_codesniffer": "3.*" + }, + "time": "2022-03-28T17:43:20+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Tassos\\Vendor\\MaxMind\\Exception\\": "src\/Exception", + "Tassos\\Vendor\\MaxMind\\WebService\\": "src\/WebService" + } + }, + "notification-url": "https:\/\/packagist.org\/downloads\/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory Oschwald", + "email": "goschwald@maxmind.com" + } + ], + "description": "Internal MaxMind Web Service API", + "homepage": "https:\/\/github.com\/maxmind\/web-service-common-php", + "support": { + "issues": "https:\/\/github.com\/maxmind\/web-service-common-php\/issues", + "source": "https:\/\/github.com\/maxmind\/web-service-common-php\/tree\/v0.9.0" + }, + "install-path": "..\/maxmind\/web-service-common" + }, + { + "name": "splitbrain\/php-archive", + "version": "1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https:\/\/github.com\/splitbrain\/php-archive.git", + "reference": "d274e5190ba309777926348900cf9578d9e533c9" + }, + "dist": { + "type": "zip", + "url": "https:\/\/api.github.com\/repos\/splitbrain\/php-archive\/zipball\/d274e5190ba309777926348900cf9578d9e533c9", + "reference": "d274e5190ba309777926348900cf9578d9e533c9", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "ext-bz2": "*", + "ext-zip": "*", + "mikey179\/vfsstream": "^1.6", + "phpunit\/phpunit": "^8" + }, + "suggest": { + "ext-bz2": "For bz2 compression", + "ext-iconv": "Used for proper filename encode handling", + "ext-mbstring": "Can be used alternatively for handling filename encoding", + "ext-zlib": "For zlib compression" + }, + "time": "2022-03-23T09:21:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Tassos\\Vendor\\splitbrain\\PHPArchive\\": "src" + } + }, + "notification-url": "https:\/\/packagist.org\/downloads\/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Gohr", + "email": "andi@splitbrain.org" + } + ], + "description": "Pure-PHP implementation to read and write TAR and ZIP archives", + "keywords": [ + "archive", + "extract", + "tar", + "unpack", + "unzip", + "zip" + ], + "support": { + "issues": "https:\/\/github.com\/splitbrain\/php-archive\/issues", + "source": "https:\/\/github.com\/splitbrain\/php-archive\/tree\/1.3.1" + }, + "install-path": "..\/splitbrain\/php-archive" + } + ], + "dev": true, + "dev-package-names": [] +} \ No newline at end of file diff --git a/plugins/system/tgeoip/vendor/composer/installed.php b/plugins/system/tgeoip/vendor/composer/installed.php new file mode 100644 index 00000000..d2fe3558 --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/installed.php @@ -0,0 +1,5 @@ + array('name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', 'reference' => '015eb26a70321a57e0c514ec4742cbe8a2580208', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => \true), 'versions' => array('__root__' => array('pretty_version' => 'dev-master', 'version' => 'dev-master', 'reference' => '015eb26a70321a57e0c514ec4742cbe8a2580208', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => \false), 'composer/ca-bundle' => array('pretty_version' => '1.3.5', 'version' => '1.3.5.0', 'reference' => '74780ccf8c19d6acb8d65c5f39cd72110e132bbd', 'type' => 'library', 'install_path' => __DIR__ . '/./ca-bundle', 'aliases' => array(), 'dev_requirement' => \false), 'geoip2/geoip2' => array('pretty_version' => 'v2.13.0', 'version' => '2.13.0.0', 'reference' => '6a41d8fbd6b90052bc34dff3b4252d0f88067b23', 'type' => 'library', 'install_path' => __DIR__ . '/../geoip2/geoip2', 'aliases' => array(), 'dev_requirement' => \false), 'maxmind-db/reader' => array('pretty_version' => 'v1.11.0', 'version' => '1.11.0.0', 'reference' => 'b1f3c0699525336d09cc5161a2861268d9f2ae5b', 'type' => 'library', 'install_path' => __DIR__ . '/../maxmind-db/reader', 'aliases' => array(), 'dev_requirement' => \false), 'maxmind/web-service-common' => array('pretty_version' => 'v0.9.0', 'version' => '0.9.0.0', 'reference' => '4dc5a3e8df38aea4ca3b1096cee3a038094e9b53', 'type' => 'library', 'install_path' => __DIR__ . '/../maxmind/web-service-common', 'aliases' => array(), 'dev_requirement' => \false), 'splitbrain/php-archive' => array('pretty_version' => '1.3.1', 'version' => '1.3.1.0', 'reference' => 'd274e5190ba309777926348900cf9578d9e533c9', 'type' => 'library', 'install_path' => __DIR__ . '/../splitbrain/php-archive', 'aliases' => array(), 'dev_requirement' => \false))); diff --git a/plugins/system/tgeoip/vendor/composer/platform_check.php b/plugins/system/tgeoip/vendor/composer/platform_check.php new file mode 100644 index 00000000..589e9e77 --- /dev/null +++ b/plugins/system/tgeoip/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/examples/benchmark.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/examples/benchmark.php new file mode 100644 index 00000000..448994c8 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/examples/benchmark.php @@ -0,0 +1,23 @@ +city($ip); + } catch (\Tassos\Vendor\GeoIp2\Exception\AddressNotFoundException $e) { + } + if ($i % 10000 === 0) { + echo $i . ' ' . $ip . "\n"; + } +} +$endTime = \microtime(\true); +$duration = $endTime - $startTime; +echo 'Requests per second: ' . $count / $duration . "\n"; diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Database/Reader.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Database/Reader.php new file mode 100644 index 00000000..1747f727 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Database/Reader.php @@ -0,0 +1,246 @@ + + */ + private $locales; + /** + * Constructor. + * + * @param string $filename the path to the GeoIP2 database file + * @param array $locales list of locale codes to use in name property + * from most preferred to least preferred + * + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function __construct(string $filename, array $locales = ['en']) + { + $this->dbReader = new DbReader($filename); + $this->dbType = $this->dbReader->metadata()->databaseType; + $this->locales = $locales; + } + /** + * This method returns a GeoIP2 City model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function city(string $ipAddress) : City + { + // @phpstan-ignore-next-line + return $this->modelFor(City::class, 'City', $ipAddress); + } + /** + * This method returns a GeoIP2 Country model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function country(string $ipAddress) : Country + { + // @phpstan-ignore-next-line + return $this->modelFor(Country::class, 'Country', $ipAddress); + } + /** + * This method returns a GeoIP2 Anonymous IP model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function anonymousIp(string $ipAddress) : AnonymousIp + { + // @phpstan-ignore-next-line + return $this->flatModelFor(AnonymousIp::class, 'GeoIP2-Anonymous-IP', $ipAddress); + } + /** + * This method returns a GeoLite2 ASN model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function asn(string $ipAddress) : Asn + { + // @phpstan-ignore-next-line + return $this->flatModelFor(Asn::class, 'GeoLite2-ASN', $ipAddress); + } + /** + * This method returns a GeoIP2 Connection Type model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function connectionType(string $ipAddress) : ConnectionType + { + // @phpstan-ignore-next-line + return $this->flatModelFor(ConnectionType::class, 'GeoIP2-Connection-Type', $ipAddress); + } + /** + * This method returns a GeoIP2 Domain model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function domain(string $ipAddress) : Domain + { + // @phpstan-ignore-next-line + return $this->flatModelFor(Domain::class, 'GeoIP2-Domain', $ipAddress); + } + /** + * This method returns a GeoIP2 Enterprise model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function enterprise(string $ipAddress) : Enterprise + { + // @phpstan-ignore-next-line + return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress); + } + /** + * This method returns a GeoIP2 ISP model. + * + * @param string $ipAddress an IPv4 or IPv6 address as a string + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address is + * not in the database + * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database + * is corrupt or invalid + */ + public function isp(string $ipAddress) : Isp + { + // @phpstan-ignore-next-line + return $this->flatModelFor(Isp::class, 'GeoIP2-ISP', $ipAddress); + } + private function modelFor(string $class, string $type, string $ipAddress) : AbstractModel + { + [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress); + $record['traits']['ip_address'] = $ipAddress; + $record['traits']['prefix_len'] = $prefixLen; + return new $class($record, $this->locales); + } + private function flatModelFor(string $class, string $type, string $ipAddress) : AbstractModel + { + [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress); + $record['ip_address'] = $ipAddress; + $record['prefix_len'] = $prefixLen; + return new $class($record); + } + private function getRecord(string $class, string $type, string $ipAddress) : array + { + if (\strpos($this->dbType, $type) === \false) { + $method = \lcfirst((new \ReflectionClass($class))->getShortName()); + throw new \BadMethodCallException("The {$method} method cannot be used to open a {$this->dbType} database"); + } + [$record, $prefixLen] = $this->dbReader->getWithPrefixLen($ipAddress); + if ($record === null) { + throw new AddressNotFoundException("The address {$ipAddress} is not in the database."); + } + if (!\is_array($record)) { + // This can happen on corrupt databases. Generally, + // MaxMind\Db\Reader will throw a + // MaxMind\Db\Reader\InvalidDatabaseException, but occasionally + // the lookup may result in a record that looks valid but is not + // an array. This mostly happens when the user is ignoring all + // exceptions and the more frequent InvalidDatabaseException + // exceptions go unnoticed. + throw new InvalidDatabaseException("Expected an array when looking up {$ipAddress} but received: " . \gettype($record)); + } + return [$record, $prefixLen]; + } + /** + * @throws \InvalidArgumentException if arguments are passed to the method + * @throws \BadMethodCallException if the database has been closed + * + * @return \MaxMind\Db\Reader\Metadata object for the database + */ + public function metadata() : DbReader\Metadata + { + return $this->dbReader->metadata(); + } + /** + * Closes the GeoIP2 database and returns the resources to the system. + */ + public function close() : void + { + $this->dbReader->close(); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AddressNotFoundException.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AddressNotFoundException.php new file mode 100644 index 00000000..e49a1d08 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AddressNotFoundException.php @@ -0,0 +1,11 @@ +uri = $uri; + parent::__construct($message, $httpStatus, $previous); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/InvalidRequestException.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/InvalidRequestException.php new file mode 100644 index 00000000..a61aec1f --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/InvalidRequestException.php @@ -0,0 +1,23 @@ +error = $error; + parent::__construct($message, $httpStatus, $uri, $previous); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/OutOfQueriesException.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/OutOfQueriesException.php new file mode 100644 index 00000000..99d83534 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/OutOfQueriesException.php @@ -0,0 +1,11 @@ + + */ + protected $raw; + /** + * @ignore + */ + public function __construct(array $raw) + { + $this->raw = $raw; + } + /** + * @ignore + * + * @return mixed + */ + protected function get(string $field) + { + if (isset($this->raw[$field])) { + return $this->raw[$field]; + } + if (\preg_match('/^is_/', $field)) { + return \false; + } + return null; + } + /** + * @ignore + * + * @return mixed + */ + public function __get(string $attr) + { + if ($attr !== 'instance' && \property_exists($this, $attr)) { + return $this->{$attr}; + } + throw new \RuntimeException("Unknown attribute: {$attr}"); + } + /** + * @ignore + */ + public function __isset(string $attr) : bool + { + return $attr !== 'instance' && isset($this->{$attr}); + } + public function jsonSerialize() : array + { + return $this->raw; + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AnonymousIp.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AnonymousIp.php new file mode 100644 index 00000000..b0531819 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AnonymousIp.php @@ -0,0 +1,80 @@ +isAnonymous = $this->get('is_anonymous'); + $this->isAnonymousVpn = $this->get('is_anonymous_vpn'); + $this->isHostingProvider = $this->get('is_hosting_provider'); + $this->isPublicProxy = $this->get('is_public_proxy'); + $this->isResidentialProxy = $this->get('is_residential_proxy'); + $this->isTorExitNode = $this->get('is_tor_exit_node'); + $ipAddress = $this->get('ip_address'); + $this->ipAddress = $ipAddress; + $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Asn.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Asn.php new file mode 100644 index 00000000..8635b954 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Asn.php @@ -0,0 +1,51 @@ +autonomousSystemNumber = $this->get('autonomous_system_number'); + $this->autonomousSystemOrganization = $this->get('autonomous_system_organization'); + $ipAddress = $this->get('ip_address'); + $this->ipAddress = $ipAddress; + $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/City.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/City.php new file mode 100644 index 00000000..e7ab19c1 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/City.php @@ -0,0 +1,105 @@ + + */ + protected $subdivisions = []; + /** + * @ignore + */ + public function __construct(array $raw, array $locales = ['en']) + { + parent::__construct($raw, $locales); + $this->city = new \Tassos\Vendor\GeoIp2\Record\City($this->get('city'), $locales); + $this->location = new \Tassos\Vendor\GeoIp2\Record\Location($this->get('location')); + $this->postal = new \Tassos\Vendor\GeoIp2\Record\Postal($this->get('postal')); + $this->createSubdivisions($raw, $locales); + } + private function createSubdivisions(array $raw, array $locales) : void + { + if (!isset($raw['subdivisions'])) { + return; + } + foreach ($raw['subdivisions'] as $sub) { + $this->subdivisions[] = new \Tassos\Vendor\GeoIp2\Record\Subdivision($sub, $locales); + } + } + /** + * @ignore + * + * @return mixed + */ + public function __get(string $attr) + { + if ($attr === 'mostSpecificSubdivision') { + return $this->{$attr}(); + } + return parent::__get($attr); + } + /** + * @ignore + */ + public function __isset(string $attr) : bool + { + if ($attr === 'mostSpecificSubdivision') { + // We always return a mostSpecificSubdivision, even if it is the + // empty subdivision + return \true; + } + return parent::__isset($attr); + } + private function mostSpecificSubdivision() : \Tassos\Vendor\GeoIp2\Record\Subdivision + { + return empty($this->subdivisions) ? new \Tassos\Vendor\GeoIp2\Record\Subdivision([], $this->locales) : \end($this->subdivisions); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/ConnectionType.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/ConnectionType.php new file mode 100644 index 00000000..fd195d41 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/ConnectionType.php @@ -0,0 +1,44 @@ +connectionType = $this->get('connection_type'); + $ipAddress = $this->get('ip_address'); + $this->ipAddress = $ipAddress; + $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Country.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Country.php new file mode 100644 index 00000000..aacd18c5 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Country.php @@ -0,0 +1,74 @@ + + */ + protected $locales; + /** + * @var \GeoIp2\Record\MaxMind + */ + protected $maxmind; + /** + * @var \GeoIp2\Record\Country + */ + protected $registeredCountry; + /** + * @var \GeoIp2\Record\RepresentedCountry + */ + protected $representedCountry; + /** + * @var \GeoIp2\Record\Traits + */ + protected $traits; + /** + * @ignore + */ + public function __construct(array $raw, array $locales = ['en']) + { + parent::__construct($raw); + $this->continent = new \Tassos\Vendor\GeoIp2\Record\Continent($this->get('continent'), $locales); + $this->country = new \Tassos\Vendor\GeoIp2\Record\Country($this->get('country'), $locales); + $this->maxmind = new \Tassos\Vendor\GeoIp2\Record\MaxMind($this->get('maxmind')); + $this->registeredCountry = new \Tassos\Vendor\GeoIp2\Record\Country($this->get('registered_country'), $locales); + $this->representedCountry = new \Tassos\Vendor\GeoIp2\Record\RepresentedCountry($this->get('represented_country'), $locales); + $this->traits = new \Tassos\Vendor\GeoIp2\Record\Traits($this->get('traits')); + $this->locales = $locales; + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Domain.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Domain.php new file mode 100644 index 00000000..e6d262fa --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Domain.php @@ -0,0 +1,44 @@ +domain = $this->get('domain'); + $ipAddress = $this->get('ip_address'); + $this->ipAddress = $ipAddress; + $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Enterprise.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Enterprise.php new file mode 100644 index 00000000..844b362e --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Enterprise.php @@ -0,0 +1,14 @@ +autonomousSystemNumber = $this->get('autonomous_system_number'); + $this->autonomousSystemOrganization = $this->get('autonomous_system_organization'); + $this->isp = $this->get('isp'); + $this->mobileCountryCode = $this->get('mobile_country_code'); + $this->mobileNetworkCode = $this->get('mobile_network_code'); + $this->organization = $this->get('organization'); + $ipAddress = $this->get('ip_address'); + $this->ipAddress = $ipAddress; + $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/ProviderInterface.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/ProviderInterface.php new file mode 100644 index 00000000..03355b1b --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/ProviderInterface.php @@ -0,0 +1,20 @@ + + */ + private $locales; + /** + * @ignore + */ + public function __construct(?array $record, array $locales = ['en']) + { + $this->locales = $locales; + parent::__construct($record); + } + /** + * @ignore + * + * @return mixed + */ + public function __get(string $attr) + { + if ($attr === 'name') { + return $this->name(); + } + return parent::__get($attr); + } + /** + * @ignore + */ + public function __isset(string $attr) : bool + { + if ($attr === 'name') { + return $this->firstSetNameLocale() !== null; + } + return parent::__isset($attr); + } + private function name() : ?string + { + $locale = $this->firstSetNameLocale(); + // @phpstan-ignore-next-line + return $locale === null ? null : $this->names[$locale]; + } + private function firstSetNameLocale() : ?string + { + foreach ($this->locales as $locale) { + if (isset($this->names[$locale])) { + return $locale; + } + } + return null; + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractRecord.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractRecord.php new file mode 100644 index 00000000..325cfa2a --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractRecord.php @@ -0,0 +1,56 @@ + + */ + private $record; + /** + * @ignore + */ + public function __construct(?array $record) + { + $this->record = isset($record) ? $record : []; + } + /** + * @ignore + * + * @return mixed + */ + public function __get(string $attr) + { + // XXX - kind of ugly but greatly reduces boilerplate code + $key = $this->attributeToKey($attr); + if ($this->__isset($attr)) { + return $this->record[$key]; + } + if ($this->validAttribute($attr)) { + if (\preg_match('/^is_/', $key)) { + return \false; + } + return null; + } + throw new \RuntimeException("Unknown attribute: {$attr}"); + } + public function __isset(string $attr) : bool + { + return $this->validAttribute($attr) && isset($this->record[$this->attributeToKey($attr)]); + } + private function attributeToKey(string $attr) : string + { + return \strtolower(\preg_replace('/([A-Z])/', '_\1', $attr)); + } + private function validAttribute(string $attr) : bool + { + // @phpstan-ignore-next-line + return \in_array($attr, $this->validAttributes, \true); + } + public function jsonSerialize() : ?array + { + return $this->record; + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/City.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/City.php new file mode 100644 index 00000000..9e7bbe54 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/City.php @@ -0,0 +1,32 @@ + + */ + protected $validAttributes = ['confidence', 'geonameId', 'names']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Continent.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Continent.php new file mode 100644 index 00000000..ad120974 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Continent.php @@ -0,0 +1,31 @@ + + */ + protected $validAttributes = ['code', 'geonameId', 'names']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Country.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Country.php new file mode 100644 index 00000000..4e29a287 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Country.php @@ -0,0 +1,37 @@ + + */ + protected $validAttributes = ['confidence', 'geonameId', 'isInEuropeanUnion', 'isoCode', 'names']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Location.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Location.php new file mode 100644 index 00000000..49db851c --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Location.php @@ -0,0 +1,45 @@ + + */ + protected $validAttributes = ['averageIncome', 'accuracyRadius', 'latitude', 'longitude', 'metroCode', 'populationDensity', 'postalCode', 'postalConfidence', 'timeZone']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/MaxMind.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/MaxMind.php new file mode 100644 index 00000000..ef317b6b --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/MaxMind.php @@ -0,0 +1,22 @@ + + */ + protected $validAttributes = ['queriesRemaining']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Postal.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Postal.php new file mode 100644 index 00000000..c1cb98e1 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Postal.php @@ -0,0 +1,29 @@ + + */ + protected $validAttributes = ['code', 'confidence']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/RepresentedCountry.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/RepresentedCountry.php new file mode 100644 index 00000000..a611aa43 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/RepresentedCountry.php @@ -0,0 +1,25 @@ +military + * but this could expand to include other types in the future. + */ +class RepresentedCountry extends Country +{ + /** + * @ignore + * + * @var array + */ + protected $validAttributes = ['confidence', 'geonameId', 'isInEuropeanUnion', 'isoCode', 'names', 'type']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Subdivision.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Subdivision.php new file mode 100644 index 00000000..b6e52a8a --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Subdivision.php @@ -0,0 +1,38 @@ + + */ + protected $validAttributes = ['confidence', 'geonameId', 'isoCode', 'names']; +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Traits.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Traits.php new file mode 100644 index 00000000..504f167d --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Traits.php @@ -0,0 +1,131 @@ +The user type associated with the IP + * address. This can be one of the following values:

+ *
    + *
  • business + *
  • cafe + *
  • cellular + *
  • college + *
  • consumer_privacy_network + *
  • content_delivery_network + *
  • dialup + *
  • government + *
  • hosting + *
  • library + *
  • military + *
  • residential + *
  • router + *
  • school + *
  • search_engine_spider + *
  • traveler + *
+ *

+ * This attribute is only available from the Insights web service and the + * GeoIP2 Enterprise database. + *

+ */ +class Traits extends AbstractRecord +{ + /** + * @ignore + * + * @var array + */ + protected $validAttributes = ['autonomousSystemNumber', 'autonomousSystemOrganization', 'connectionType', 'domain', 'ipAddress', 'isAnonymous', 'isAnonymousProxy', 'isAnonymousVpn', 'isHostingProvider', 'isLegitimateProxy', 'isp', 'isPublicProxy', 'isResidentialProxy', 'isSatelliteProvider', 'isTorExitNode', 'mobileCountryCode', 'mobileNetworkCode', 'network', 'organization', 'staticIpScore', 'userCount', 'userType']; + public function __construct(?array $record) + { + if (!isset($record['network']) && isset($record['ip_address'], $record['prefix_len'])) { + $record['network'] = Util::cidr($record['ip_address'], $record['prefix_len']); + } + parent::__construct($record); + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Util.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Util.php new file mode 100644 index 00000000..b4d455c5 --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/Util.php @@ -0,0 +1,32 @@ + 0; $i++) { + $b = $ipBytes[$i]; + if ($curPrefix < 8) { + $shiftN = 8 - $curPrefix; + $b = \chr(0xff & \ord($b) >> $shiftN << $shiftN); + } + $networkBytes[$i] = $b; + $curPrefix -= 8; + } + $network = \inet_ntop($networkBytes); + return "{$network}/{$prefixLen}"; + } +} diff --git a/plugins/system/tgeoip/vendor/geoip2/geoip2/src/WebService/Client.php b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/WebService/Client.php new file mode 100644 index 00000000..1a5a2f8e --- /dev/null +++ b/plugins/system/tgeoip/vendor/geoip2/geoip2/src/WebService/Client.php @@ -0,0 +1,207 @@ + + */ + private $locales; + /** + * @var WsClient + */ + private $client; + /** + * @var string + */ + private static $basePath = '/geoip/v2.1'; + public const VERSION = 'v2.13.0'; + /** + * Constructor. + * + * @param int $accountId your MaxMind account ID + * @param string $licenseKey your MaxMind license key + * @param array $locales list of locale codes to use in name property + * from most preferred to least preferred + * @param array $options array of options. Valid options include: + * * `host` - The host to use when querying the web + * service. To query the GeoLite2 web service + * instead of the GeoIP2 web service, set the + * host to `geolite.info`. + * * `timeout` - Timeout in seconds. + * * `connectTimeout` - Initial connection timeout in seconds. + * * `proxy` - The HTTP proxy to use. May include a schema, port, + * username, and password, e.g., + * `http://username:password@127.0.0.1:10`. + */ + public function __construct(int $accountId, string $licenseKey, array $locales = ['en'], array $options = []) + { + $this->locales = $locales; + // This is for backwards compatibility. Do not remove except for a + // major version bump. + // @phpstan-ignore-next-line + if (\is_string($options)) { + $options = ['host' => $options]; + } + if (!isset($options['host'])) { + $options['host'] = 'geoip.maxmind.com'; + } + $options['userAgent'] = $this->userAgent(); + $this->client = new WsClient($accountId, $licenseKey, $options); + } + private function userAgent() : string + { + return 'GeoIP2-API/' . self::VERSION; + } + /** + * This method calls the City Plus service. + * + * @param string $ipAddress IPv4 or IPv6 address as a string. If no + * address is provided, the address that the web service is called + * from will be used. + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address you + * provided is not in our database (e.g., a private address). + * @throws \GeoIp2\Exception\AuthenticationException if there is a problem + * with the account ID or license key that you provided + * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out + * of queries + * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is + * invalid for some other reason. This may indicate an issue + * with this API. Please report the error to MaxMind. + * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error code or message was returned. + * This could indicate a problem with the connection between + * your server and the web service or that the web service + * returned an invalid document or 500 error code + * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent + * class to the above exceptions. It will be thrown directly + * if a 200 status code is returned but the body is invalid. + */ + public function city(string $ipAddress = 'me') : City + { + // @phpstan-ignore-next-line + return $this->responseFor('city', City::class, $ipAddress); + } + /** + * This method calls the Country service. + * + * @param string $ipAddress IPv4 or IPv6 address as a string. If no + * address is provided, the address that the web service is called + * from will be used. + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address you provided is not in our database (e.g., + * a private address). + * @throws \GeoIp2\Exception\AuthenticationException if there is a problem + * with the account ID or license key that you provided + * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out of queries + * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is + * invalid for some other reason. This may indicate an + * issue with this API. Please report the error to MaxMind. + * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error + * code or message was returned. This could indicate a problem + * with the connection between your server and the web service + * or that the web service returned an invalid document or 500 + * error code. + * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent class to the above exceptions. It + * will be thrown directly if a 200 status code is returned but + * the body is invalid. + */ + public function country(string $ipAddress = 'me') : Country + { + return $this->responseFor('country', Country::class, $ipAddress); + } + /** + * This method calls the Insights service. Insights is only supported by + * the GeoIP2 web service. The GeoLite2 web service does not support it. + * + * @param string $ipAddress IPv4 or IPv6 address as a string. If no + * address is provided, the address that the web service is called + * from will be used. + * + * @throws \GeoIp2\Exception\AddressNotFoundException if the address you + * provided is not in our database (e.g., a private address). + * @throws \GeoIp2\Exception\AuthenticationException if there is a problem + * with the account ID or license key that you provided + * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out + * of queries + * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is + * invalid for some other reason. This may indicate an + * issue with this API. Please report the error to MaxMind. + * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error code or message was returned. + * This could indicate a problem with the connection between + * your server and the web service or that the web service + * returned an invalid document or 500 error code + * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent + * class to the above exceptions. It will be thrown directly + * if a 200 status code is returned but the body is invalid. + */ + public function insights(string $ipAddress = 'me') : Insights + { + // @phpstan-ignore-next-line + return $this->responseFor('insights', Insights::class, $ipAddress); + } + private function responseFor(string $endpoint, string $class, string $ipAddress) : Country + { + $path = \implode('/', [self::$basePath, $endpoint, $ipAddress]); + try { + $service = (new \ReflectionClass($class))->getShortName(); + $body = $this->client->get('GeoIP2 ' . $service, $path); + } catch (\Tassos\Vendor\MaxMind\Exception\IpAddressNotFoundException $ex) { + throw new AddressNotFoundException($ex->getMessage(), $ex->getStatusCode(), $ex); + } catch (\Tassos\Vendor\MaxMind\Exception\AuthenticationException $ex) { + throw new AuthenticationException($ex->getMessage(), $ex->getStatusCode(), $ex); + } catch (\Tassos\Vendor\MaxMind\Exception\InsufficientFundsException $ex) { + throw new OutOfQueriesException($ex->getMessage(), $ex->getStatusCode(), $ex); + } catch (\Tassos\Vendor\MaxMind\Exception\InvalidRequestException $ex) { + throw new InvalidRequestException($ex->getMessage(), $ex->getErrorCode(), $ex->getStatusCode(), $ex->getUri(), $ex); + } catch (\Tassos\Vendor\MaxMind\Exception\HttpException $ex) { + throw new HttpException($ex->getMessage(), $ex->getStatusCode(), $ex->getUri(), $ex); + } catch (\Tassos\Vendor\MaxMind\Exception\WebServiceException $ex) { + throw new GeoIp2Exception($ex->getMessage(), $ex->getCode(), $ex); + } + return new $class($body, $this->locales); + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/autoload.php b/plugins/system/tgeoip/vendor/maxmind-db/reader/autoload.php new file mode 100644 index 00000000..c1708daa --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/autoload.php @@ -0,0 +1,42 @@ +class. + * + * @param string $class + * the name of the class to load + */ +function mmdb_autoload($class) : void +{ + /* + * A project-specific mapping between the namespaces and where + * they're located. By convention, we include the trailing + * slashes. The one-element array here simply makes things easy + * to extend in the future if (for example) the test classes + * begin to use one another. + */ + $namespace_map = ['MaxMind\\Db\\' => __DIR__ . '/src/MaxMind/Db/']; + foreach ($namespace_map as $prefix => $dir) { + // First swap out the namespace prefix with a directory... + $path = \str_replace($prefix, $dir, $class); + // replace the namespace separator with a directory separator... + $path = \str_replace('\\', '/', $path); + // and finally, add the PHP file extension to the result. + $path = $path . '.php'; + // $path should now contain the path to a PHP file defining $class + if (\file_exists($path)) { + include $path; + } + } +} +\spl_autoload_register('mmdb_autoload'); diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.m4 b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.m4 new file mode 100644 index 00000000..675e00c6 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.m4 @@ -0,0 +1,40 @@ +PHP_ARG_WITH(maxminddb, + [Whether to enable the MaxMind DB Reader extension], + [ --with-maxminddb Enable MaxMind DB Reader extension support]) + +PHP_ARG_ENABLE(maxminddb-debug, for MaxMind DB debug support, + [ --enable-maxminddb-debug Enable enable MaxMind DB deubg support], no, no) + +if test $PHP_MAXMINDDB != "no"; then + + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + + AC_MSG_CHECKING(for libmaxminddb) + if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libmaxminddb; then + dnl retrieve build options from pkg-config + if $PKG_CONFIG libmaxminddb --atleast-version 1.0.0; then + LIBMAXMINDDB_INC=`$PKG_CONFIG libmaxminddb --cflags` + LIBMAXMINDDB_LIB=`$PKG_CONFIG libmaxminddb --libs` + LIBMAXMINDDB_VER=`$PKG_CONFIG libmaxminddb --modversion` + AC_MSG_RESULT(found version $LIBMAXMINDDB_VER) + else + AC_MSG_ERROR(system libmaxminddb must be upgraded to version >= 1.0.0) + fi + PHP_EVAL_LIBLINE($LIBMAXMINDDB_LIB, MAXMINDDB_SHARED_LIBADD) + PHP_EVAL_INCLINE($LIBMAXMINDDB_INC) + else + AC_MSG_RESULT(pkg-config information missing) + AC_MSG_WARN(will use libmaxmxinddb from compiler default path) + + PHP_CHECK_LIBRARY(maxminddb, MMDB_open) + PHP_ADD_LIBRARY(maxminddb, 1, MAXMINDDB_SHARED_LIBADD) + fi + + if test $PHP_MAXMINDDB_DEBUG != "no"; then + CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Werror" + fi + + PHP_SUBST(MAXMINDDB_SHARED_LIBADD) + + PHP_NEW_EXTENSION(maxminddb, maxminddb.c, $ext_shared) +fi diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.w32 b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.w32 new file mode 100644 index 00000000..4eb18f84 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.w32 @@ -0,0 +1,10 @@ +ARG_WITH("maxminddb", "Enable MaxMind DB Reader extension support", "no"); + +if (PHP_MAXMINDDB == "yes") { + if (CHECK_HEADER_ADD_INCLUDE("maxminddb.h", "CFLAGS_MAXMINDDB", PHP_MAXMINDDB + ";" + PHP_PHP_BUILD + "\\include\\maxminddb") && + CHECK_LIB("libmaxminddb.lib", "maxminddb", PHP_MAXMINDDB)) { + EXTENSION("maxminddb", "maxminddb.c"); + } else { + WARNING('Could not find maxminddb.h or libmaxminddb.lib; skipping'); + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/maxminddb.c b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/maxminddb.c new file mode 100644 index 00000000..b00b2fa3 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/maxminddb.c @@ -0,0 +1,811 @@ +/* MaxMind, Inc., licenses this file to you under the Apache License, Version + * 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include "php_maxminddb.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "Zend/zend_exceptions.h" +#include "Zend/zend_types.h" +#include "ext/spl/spl_exceptions.h" +#include "ext/standard/info.h" +#include + +#ifdef ZTS +#include +#endif + +#define __STDC_FORMAT_MACROS +#include + +#define PHP_MAXMINDDB_NS ZEND_NS_NAME("MaxMind", "Db") +#define PHP_MAXMINDDB_READER_NS ZEND_NS_NAME(PHP_MAXMINDDB_NS, "Reader") +#define PHP_MAXMINDDB_METADATA_NS \ + ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata") +#define PHP_MAXMINDDB_READER_EX_NS \ + ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "InvalidDatabaseException") + +#define Z_MAXMINDDB_P(zv) php_maxminddb_fetch_object(Z_OBJ_P(zv)) +typedef size_t strsize_t; +typedef zend_object free_obj_t; + +/* For PHP 8 compatibility */ +#if PHP_VERSION_ID < 80000 + +#define PROP_OBJ(zv) (zv) + +#else + +#define PROP_OBJ(zv) Z_OBJ_P(zv) + +#define TSRMLS_C +#define TSRMLS_CC +#define TSRMLS_DC + +/* End PHP 8 compatibility */ +#endif + +#ifndef ZEND_ACC_CTOR +#define ZEND_ACC_CTOR 0 +#endif + +/* IS_MIXED was added in 2020 */ +#ifndef IS_MIXED +#define IS_MIXED IS_UNDEF +#endif + +/* ZEND_THIS was added in 7.4 */ +#ifndef ZEND_THIS +#define ZEND_THIS (&EX(This)) +#endif + +typedef struct _maxminddb_obj { + MMDB_s *mmdb; + zend_object std; +} maxminddb_obj; + +PHP_FUNCTION(maxminddb); + +static int +get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len); +static const MMDB_entry_data_list_s * +handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static const MMDB_entry_data_list_s * +handle_array(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static const MMDB_entry_data_list_s * +handle_map(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); +static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC); + +#define CHECK_ALLOCATED(val) \ + if (!val) { \ + zend_error(E_ERROR, "Out of memory"); \ + return; \ + } + +static zend_object_handlers maxminddb_obj_handlers; +static zend_class_entry *maxminddb_ce, *maxminddb_exception_ce, *metadata_ce; + +static inline maxminddb_obj * +php_maxminddb_fetch_object(zend_object *obj TSRMLS_DC) { + return (maxminddb_obj *)((char *)(obj)-XtOffsetOf(maxminddb_obj, std)); +} + +ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_construct, 0, 0, 1) +ZEND_ARG_TYPE_INFO(0, db_file, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, __construct) { + char *db_file = NULL; + strsize_t name_len; + zval *_this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "Os", + &_this_zval, + maxminddb_ce, + &db_file, + &name_len) == FAILURE) { + return; + } + + if (0 != php_check_open_basedir(db_file TSRMLS_CC) || + 0 != access(db_file, R_OK)) { + zend_throw_exception_ex( + spl_ce_InvalidArgumentException, + 0 TSRMLS_CC, + "The file \"%s\" does not exist or is not readable.", + db_file); + return; + } + + MMDB_s *mmdb = (MMDB_s *)ecalloc(1, sizeof(MMDB_s)); + uint16_t status = MMDB_open(db_file, MMDB_MODE_MMAP, mmdb); + + if (MMDB_SUCCESS != status) { + zend_throw_exception_ex( + maxminddb_exception_ce, + 0 TSRMLS_CC, + "Error opening database file (%s). Is this a valid " + "MaxMind DB file?", + db_file); + efree(mmdb); + return; + } + + maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(ZEND_THIS); + mmdb_obj->mmdb = mmdb; +} + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + arginfo_maxminddbreader_get, 0, 1, IS_MIXED, 1) +ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, get) { + int prefix_len = 0; + get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value, &prefix_len); +} + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX( + arginfo_maxminddbreader_getWithPrefixLen, 0, 1, IS_ARRAY, 1) +ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, getWithPrefixLen) { + zval record, z_prefix_len; + + int prefix_len = 0; + if (get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, &record, &prefix_len) == + FAILURE) { + return; + } + + array_init(return_value); + add_next_index_zval(return_value, &record); + + ZVAL_LONG(&z_prefix_len, prefix_len); + add_next_index_zval(return_value, &z_prefix_len); +} + +static int +get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) { + char *ip_address = NULL; + strsize_t name_len; + zval *this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "Os", + &this_zval, + maxminddb_ce, + &ip_address, + &name_len) == FAILURE) { + return FAILURE; + } + + const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(ZEND_THIS); + + MMDB_s *mmdb = mmdb_obj->mmdb; + + if (NULL == mmdb) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, + 0 TSRMLS_CC, + "Attempt to read from a closed MaxMind DB."); + return FAILURE; + } + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST, + /* We set ai_socktype so that we only get one result back */ + .ai_socktype = SOCK_STREAM}; + + struct addrinfo *addresses = NULL; + int gai_status = getaddrinfo(ip_address, NULL, &hints, &addresses); + if (gai_status) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, + 0 TSRMLS_CC, + "The value \"%s\" is not a valid IP address.", + ip_address); + return FAILURE; + } + if (!addresses || !addresses->ai_addr) { + zend_throw_exception_ex( + spl_ce_InvalidArgumentException, + 0 TSRMLS_CC, + "getaddrinfo was successful but failed to set the addrinfo"); + return FAILURE; + } + + int sa_family = addresses->ai_addr->sa_family; + + int mmdb_error = MMDB_SUCCESS; + MMDB_lookup_result_s result = + MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); + + freeaddrinfo(addresses); + + if (MMDB_SUCCESS != mmdb_error) { + zend_class_entry *ex; + if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) { + ex = spl_ce_InvalidArgumentException; + } else { + ex = maxminddb_exception_ce; + } + zend_throw_exception_ex(ex, + 0 TSRMLS_CC, + "Error looking up %s. %s", + ip_address, + MMDB_strerror(mmdb_error)); + return FAILURE; + } + + *prefix_len = result.netmask; + + if (sa_family == AF_INET && mmdb->metadata.ip_version == 6) { + /* We return the prefix length given the IPv4 address. If there is + no IPv4 subtree, we return a prefix length of 0. */ + *prefix_len = *prefix_len >= 96 ? *prefix_len - 96 : 0; + } + + if (!result.found_entry) { + ZVAL_NULL(record); + return SUCCESS; + } + + MMDB_entry_data_list_s *entry_data_list = NULL; + int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); + + if (MMDB_SUCCESS != status) { + zend_throw_exception_ex(maxminddb_exception_ce, + 0 TSRMLS_CC, + "Error while looking up data for %s. %s", + ip_address, + MMDB_strerror(status)); + MMDB_free_entry_data_list(entry_data_list); + return FAILURE; + } else if (NULL == entry_data_list) { + zend_throw_exception_ex( + maxminddb_exception_ce, + 0 TSRMLS_CC, + "Error while looking up data for %s. Your database may " + "be corrupt or you have found a bug in libmaxminddb.", + ip_address); + return FAILURE; + } + + const MMDB_entry_data_list_s *rv = + handle_entry_data_list(entry_data_list, record TSRMLS_CC); + if (rv == NULL) { + /* We should have already thrown the exception in handle_entry_data_list + */ + return FAILURE; + } + MMDB_free_entry_data_list(entry_data_list); + return SUCCESS; +} + +ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader, metadata) { + zval *this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "O", + &this_zval, + maxminddb_ce) == FAILURE) { + return; + } + + const maxminddb_obj *const mmdb_obj = + (maxminddb_obj *)Z_MAXMINDDB_P(this_zval); + + if (NULL == mmdb_obj->mmdb) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, + 0 TSRMLS_CC, + "Attempt to read from a closed MaxMind DB."); + return; + } + + object_init_ex(return_value, metadata_ce); + + MMDB_entry_data_list_s *entry_data_list; + MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list); + + zval metadata_array; + const MMDB_entry_data_list_s *rv = + handle_entry_data_list(entry_data_list, &metadata_array TSRMLS_CC); + if (rv == NULL) { + return; + } + MMDB_free_entry_data_list(entry_data_list); + zend_call_method_with_1_params(PROP_OBJ(return_value), + metadata_ce, + &metadata_ce->constructor, + ZEND_CONSTRUCTOR_FUNC_NAME, + NULL, + &metadata_array); + zval_ptr_dtor(&metadata_array); +} + +PHP_METHOD(MaxMind_Db_Reader, close) { + zval *this_zval = NULL; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "O", + &this_zval, + maxminddb_ce) == FAILURE) { + return; + } + + maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(this_zval); + + if (NULL == mmdb_obj->mmdb) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, + 0 TSRMLS_CC, + "Attempt to close a closed MaxMind DB."); + return; + } + MMDB_close(mmdb_obj->mmdb); + efree(mmdb_obj->mmdb); + mmdb_obj->mmdb = NULL; +} + +static const MMDB_entry_data_list_s * +handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + switch (entry_data_list->entry_data.type) { + case MMDB_DATA_TYPE_MAP: + return handle_map(entry_data_list, z_value TSRMLS_CC); + case MMDB_DATA_TYPE_ARRAY: + return handle_array(entry_data_list, z_value TSRMLS_CC); + case MMDB_DATA_TYPE_UTF8_STRING: + ZVAL_STRINGL(z_value, + (char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + break; + case MMDB_DATA_TYPE_BYTES: + ZVAL_STRINGL(z_value, + (char *)entry_data_list->entry_data.bytes, + entry_data_list->entry_data.data_size); + break; + case MMDB_DATA_TYPE_DOUBLE: + ZVAL_DOUBLE(z_value, entry_data_list->entry_data.double_value); + break; + case MMDB_DATA_TYPE_FLOAT: + ZVAL_DOUBLE(z_value, entry_data_list->entry_data.float_value); + break; + case MMDB_DATA_TYPE_UINT16: + ZVAL_LONG(z_value, entry_data_list->entry_data.uint16); + break; + case MMDB_DATA_TYPE_UINT32: + handle_uint32(entry_data_list, z_value TSRMLS_CC); + break; + case MMDB_DATA_TYPE_BOOLEAN: + ZVAL_BOOL(z_value, entry_data_list->entry_data.boolean); + break; + case MMDB_DATA_TYPE_UINT64: + handle_uint64(entry_data_list, z_value TSRMLS_CC); + break; + case MMDB_DATA_TYPE_UINT128: + handle_uint128(entry_data_list, z_value TSRMLS_CC); + break; + case MMDB_DATA_TYPE_INT32: + ZVAL_LONG(z_value, entry_data_list->entry_data.int32); + break; + default: + zend_throw_exception_ex(maxminddb_exception_ce, + 0 TSRMLS_CC, + "Invalid data type arguments: %d", + entry_data_list->entry_data.type); + return NULL; + } + return entry_data_list; +} + +static const MMDB_entry_data_list_s * +handle_map(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + array_init(z_value); + const uint32_t map_size = entry_data_list->entry_data.data_size; + + uint32_t i; + for (i = 0; i < map_size && entry_data_list; i++) { + entry_data_list = entry_data_list->next; + + char *key = estrndup((char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + if (NULL == key) { + zend_throw_exception_ex(maxminddb_exception_ce, + 0 TSRMLS_CC, + "Invalid data type arguments"); + return NULL; + } + + entry_data_list = entry_data_list->next; + zval new_value; + entry_data_list = + handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC); + if (entry_data_list != NULL) { + add_assoc_zval(z_value, key, &new_value); + } + efree(key); + } + return entry_data_list; +} + +static const MMDB_entry_data_list_s * +handle_array(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + const uint32_t size = entry_data_list->entry_data.data_size; + + array_init(z_value); + + uint32_t i; + for (i = 0; i < size && entry_data_list; i++) { + entry_data_list = entry_data_list->next; + zval new_value; + entry_data_list = + handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC); + if (entry_data_list != NULL) { + add_next_index_zval(z_value, &new_value); + } + } + return entry_data_list; +} + +static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + uint64_t high = 0; + uint64_t low = 0; +#if MMDB_UINT128_IS_BYTE_ARRAY + int i; + for (i = 0; i < 8; i++) { + high = (high << 8) | entry_data_list->entry_data.uint128[i]; + } + + for (i = 8; i < 16; i++) { + low = (low << 8) | entry_data_list->entry_data.uint128[i]; + } +#else + high = entry_data_list->entry_data.uint128 >> 64; + low = (uint64_t)entry_data_list->entry_data.uint128; +#endif + + char *num_str; + spprintf(&num_str, 0, "0x%016" PRIX64 "%016" PRIX64, high, low); + CHECK_ALLOCATED(num_str); + + ZVAL_STRING(z_value, num_str); + efree(num_str); +} + +static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + uint32_t val = entry_data_list->entry_data.uint32; + +#if LONG_MAX >= UINT32_MAX + ZVAL_LONG(z_value, val); + return; +#else + if (val <= LONG_MAX) { + ZVAL_LONG(z_value, val); + return; + } + + char *int_str; + spprintf(&int_str, 0, "%" PRIu32, val); + CHECK_ALLOCATED(int_str); + + ZVAL_STRING(z_value, int_str); + efree(int_str); +#endif +} + +static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list, + zval *z_value TSRMLS_DC) { + uint64_t val = entry_data_list->entry_data.uint64; + +#if LONG_MAX >= UINT64_MAX + ZVAL_LONG(z_value, val); + return; +#else + if (val <= LONG_MAX) { + ZVAL_LONG(z_value, val); + return; + } + + char *int_str; + spprintf(&int_str, 0, "%" PRIu64, val); + CHECK_ALLOCATED(int_str); + + ZVAL_STRING(z_value, int_str); + efree(int_str); +#endif +} + +static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) { + maxminddb_obj *obj = + php_maxminddb_fetch_object((zend_object *)object TSRMLS_CC); + if (obj->mmdb != NULL) { + MMDB_close(obj->mmdb); + efree(obj->mmdb); + } + + zend_object_std_dtor(&obj->std TSRMLS_CC); +} + +static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) { + maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj)); + zend_object_std_init(&obj->std, type TSRMLS_CC); + object_properties_init(&(obj->std), type); + + obj->std.handlers = &maxminddb_obj_handlers; + + return &obj->std; +} + +/* clang-format off */ +static zend_function_entry maxminddb_methods[] = { + PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxminddbreader_construct, + ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(MaxMind_Db_Reader, close, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC) + PHP_ME(MaxMind_Db_Reader, get, arginfo_maxminddbreader_get, ZEND_ACC_PUBLIC) + PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxminddbreader_getWithPrefixLen, ZEND_ACC_PUBLIC) + PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC) + { NULL, NULL, NULL } +}; +/* clang-format on */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_metadata_construct, 0, 0, 1) +ZEND_ARG_TYPE_INFO(0, metadata, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +PHP_METHOD(MaxMind_Db_Reader_Metadata, __construct) { + zval *object = NULL; + zval *metadata_array = NULL; + zend_long node_count = 0; + zend_long record_size = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + getThis(), + "Oa", + &object, + metadata_ce, + &metadata_array) == FAILURE) { + return; + } + + zval *tmp = NULL; + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "binary_format_major_version", + sizeof("binary_format_major_version") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "binaryFormatMajorVersion", + sizeof("binaryFormatMajorVersion") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "binary_format_minor_version", + sizeof("binary_format_minor_version") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "binaryFormatMinorVersion", + sizeof("binaryFormatMinorVersion") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "build_epoch", + sizeof("build_epoch") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "buildEpoch", + sizeof("buildEpoch") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "database_type", + sizeof("database_type") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "databaseType", + sizeof("databaseType") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "description", + sizeof("description") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "description", + sizeof("description") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "ip_version", + sizeof("ip_version") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "ipVersion", + sizeof("ipVersion") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find( + HASH_OF(metadata_array), "languages", sizeof("languages") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "languages", + sizeof("languages") - 1, + tmp); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "record_size", + sizeof("record_size") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "recordSize", + sizeof("recordSize") - 1, + tmp); + if (Z_TYPE_P(tmp) == IS_LONG) { + record_size = Z_LVAL_P(tmp); + } + } + + if (record_size != 0) { + zend_update_property_long(metadata_ce, + PROP_OBJ(object), + "nodeByteSize", + sizeof("nodeByteSize") - 1, + record_size / 4); + } + + if ((tmp = zend_hash_str_find(HASH_OF(metadata_array), + "node_count", + sizeof("node_count") - 1))) { + zend_update_property(metadata_ce, + PROP_OBJ(object), + "nodeCount", + sizeof("nodeCount") - 1, + tmp); + if (Z_TYPE_P(tmp) == IS_LONG) { + node_count = Z_LVAL_P(tmp); + } + } + + if (record_size != 0) { + zend_update_property_long(metadata_ce, + PROP_OBJ(object), + "searchTreeSize", + sizeof("searchTreeSize") - 1, + record_size * node_count / 4); + } +} + +// clang-format off +static zend_function_entry metadata_methods[] = { + PHP_ME(MaxMind_Db_Reader_Metadata, __construct, arginfo_metadata_construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + {NULL, NULL, NULL} +}; +// clang-format on + +PHP_MINIT_FUNCTION(maxminddb) { + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_EX_NS, NULL); + maxminddb_exception_ce = + zend_register_internal_class_ex(&ce, zend_ce_exception); + + INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_NS, maxminddb_methods); + maxminddb_ce = zend_register_internal_class(&ce TSRMLS_CC); + maxminddb_ce->create_object = maxminddb_create_handler; + + INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_METADATA_NS, metadata_methods); + metadata_ce = zend_register_internal_class(&ce TSRMLS_CC); + zend_declare_property_null(metadata_ce, + "binaryFormatMajorVersion", + sizeof("binaryFormatMajorVersion") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "binaryFormatMinorVersion", + sizeof("binaryFormatMinorVersion") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "buildEpoch", sizeof("buildEpoch") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "databaseType", + sizeof("databaseType") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "description", sizeof("description") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "ipVersion", sizeof("ipVersion") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "languages", sizeof("languages") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "nodeByteSize", + sizeof("nodeByteSize") - 1, + ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "nodeCount", sizeof("nodeCount") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null( + metadata_ce, "recordSize", sizeof("recordSize") - 1, ZEND_ACC_PUBLIC); + zend_declare_property_null(metadata_ce, + "searchTreeSize", + sizeof("searchTreeSize") - 1, + ZEND_ACC_PUBLIC); + + memcpy(&maxminddb_obj_handlers, + zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + maxminddb_obj_handlers.clone_obj = NULL; + maxminddb_obj_handlers.offset = XtOffsetOf(maxminddb_obj, std); + maxminddb_obj_handlers.free_obj = maxminddb_free_storage; + zend_declare_class_constant_string(maxminddb_ce, + "MMDB_LIB_VERSION", + sizeof("MMDB_LIB_VERSION") - 1, + MMDB_lib_version() TSRMLS_CC); + + return SUCCESS; +} + +static PHP_MINFO_FUNCTION(maxminddb) { + php_info_print_table_start(); + + php_info_print_table_row(2, "MaxMind DB Reader", "enabled"); + php_info_print_table_row( + 2, "maxminddb extension version", PHP_MAXMINDDB_VERSION); + php_info_print_table_row( + 2, "libmaxminddb library version", MMDB_lib_version()); + + php_info_print_table_end(); +} + +zend_module_entry maxminddb_module_entry = {STANDARD_MODULE_HEADER, + PHP_MAXMINDDB_EXTNAME, + NULL, + PHP_MINIT(maxminddb), + NULL, + NULL, + NULL, + PHP_MINFO(maxminddb), + PHP_MAXMINDDB_VERSION, + STANDARD_MODULE_PROPERTIES}; + +#ifdef COMPILE_DL_MAXMINDDB +ZEND_GET_MODULE(maxminddb) +#endif diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/php_maxminddb.h b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/php_maxminddb.h new file mode 100644 index 00000000..cba9ed50 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/ext/php_maxminddb.h @@ -0,0 +1,24 @@ +/* MaxMind, Inc., licenses this file to you under the Apache License, Version + * 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include + +#ifndef PHP_MAXMINDDB_H +#define PHP_MAXMINDDB_H 1 +#define PHP_MAXMINDDB_VERSION "1.10.1" +#define PHP_MAXMINDDB_EXTNAME "maxminddb" + +extern zend_module_entry maxminddb_module_entry; +#define phpext_maxminddb_ptr &maxminddb_module_entry + +#endif diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/package.xml b/plugins/system/tgeoip/vendor/maxmind-db/reader/package.xml new file mode 100644 index 00000000..b687468f --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/package.xml @@ -0,0 +1,63 @@ + + + + maxminddb + pecl.php.net + Reader for the MaxMind DB file format + This is the PHP extension for reading MaxMind DB files. MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6). + + Greg Oschwald + oschwald + goschwald@maxmind.com + yes + + 2021-04-14 + + 1.10.1 + 1.10.1 + + + stable + stable + + Apache License 2.0 + * Fix a `TypeError` exception in the pure PHP reader when using large + databases on 32-bit PHP builds with the `bcmath` extension. Reported + by dodo1708. GitHub #124. + + + + + + + + + + + + + + + + + + + + + + + + + 7.2.0 + + + 1.10.0 + + + + maxminddb + + diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php new file mode 100644 index 00000000..c31ec56d --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php @@ -0,0 +1,287 @@ +fileHandle = $fileHandle; + $fileSize = @\filesize($database); + if ($fileSize === \false) { + throw new UnexpectedValueException("Error determining the size of \"{$database}\"."); + } + $this->fileSize = $fileSize; + $start = $this->findMetadataStart($database); + $metadataDecoder = new Decoder($this->fileHandle, $start); + [$metadataArray] = $metadataDecoder->decode($start); + $this->metadata = new Metadata($metadataArray); + $this->decoder = new Decoder($this->fileHandle, $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE); + $this->ipV4Start = $this->ipV4StartNode(); + } + /** + * Retrieves the record for the IP address. + * + * @param string $ipAddress + * the IP address to look up + * + * @throws BadMethodCallException if this method is called on a closed database + * @throws InvalidArgumentException if something other than a single IP address is passed to the method + * @throws InvalidDatabaseException + * if the database is invalid or there is an error reading + * from it + * + * @return mixed the record for the IP address + */ + public function get(string $ipAddress) + { + if (\func_num_args() !== 1) { + throw new ArgumentCountError(\sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args())); + } + [$record] = $this->getWithPrefixLen($ipAddress); + return $record; + } + /** + * Retrieves the record for the IP address and its associated network prefix length. + * + * @param string $ipAddress + * the IP address to look up + * + * @throws BadMethodCallException if this method is called on a closed database + * @throws InvalidArgumentException if something other than a single IP address is passed to the method + * @throws InvalidDatabaseException + * if the database is invalid or there is an error reading + * from it + * + * @return array an array where the first element is the record and the + * second the network prefix length for the record + */ + public function getWithPrefixLen(string $ipAddress) : array + { + if (\func_num_args() !== 1) { + throw new ArgumentCountError(\sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args())); + } + if (!\is_resource($this->fileHandle)) { + throw new BadMethodCallException('Attempt to read from a closed MaxMind DB.'); + } + [$pointer, $prefixLen] = $this->findAddressInTree($ipAddress); + if ($pointer === 0) { + return [null, $prefixLen]; + } + return [$this->resolveDataPointer($pointer), $prefixLen]; + } + private function findAddressInTree(string $ipAddress) : array + { + $packedAddr = @\inet_pton($ipAddress); + if ($packedAddr === \false) { + throw new InvalidArgumentException("The value \"{$ipAddress}\" is not a valid IP address."); + } + $rawAddress = \unpack('C*', $packedAddr); + $bitCount = \count($rawAddress) * 8; + // The first node of the tree is always node 0, at the beginning of the + // value + $node = 0; + $metadata = $this->metadata; + // Check if we are looking up an IPv4 address in an IPv6 tree. If this + // is the case, we can skip over the first 96 nodes. + if ($metadata->ipVersion === 6) { + if ($bitCount === 32) { + $node = $this->ipV4Start; + } + } elseif ($metadata->ipVersion === 4 && $bitCount === 128) { + throw new InvalidArgumentException("Error looking up {$ipAddress}. You attempted to look up an" . ' IPv6 address in an IPv4-only database.'); + } + $nodeCount = $metadata->nodeCount; + for ($i = 0; $i < $bitCount && $node < $nodeCount; ++$i) { + $tempBit = 0xff & $rawAddress[($i >> 3) + 1]; + $bit = 1 & $tempBit >> 7 - $i % 8; + $node = $this->readNode($node, $bit); + } + if ($node === $nodeCount) { + // Record is empty + return [0, $i]; + } + if ($node > $nodeCount) { + // Record is a data pointer + return [$node, $i]; + } + throw new InvalidDatabaseException('Invalid or corrupt database. Maximum search depth reached without finding a leaf node'); + } + private function ipV4StartNode() : int + { + // If we have an IPv4 database, the start node is the first node + if ($this->metadata->ipVersion === 4) { + return 0; + } + $node = 0; + for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; ++$i) { + $node = $this->readNode($node, 0); + } + return $node; + } + private function readNode(int $nodeNumber, int $index) : int + { + $baseOffset = $nodeNumber * $this->metadata->nodeByteSize; + switch ($this->metadata->recordSize) { + case 24: + $bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3); + [, $node] = \unpack('N', "\x00" . $bytes); + return $node; + case 28: + $bytes = Util::read($this->fileHandle, $baseOffset + 3 * $index, 4); + if ($index === 0) { + $middle = (0xf0 & \ord($bytes[3])) >> 4; + } else { + $middle = 0xf & \ord($bytes[0]); + } + [, $node] = \unpack('N', \chr($middle) . \substr($bytes, $index, 3)); + return $node; + case 32: + $bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4); + [, $node] = \unpack('N', $bytes); + return $node; + default: + throw new InvalidDatabaseException('Unknown record size: ' . $this->metadata->recordSize); + } + } + /** + * @return mixed + */ + private function resolveDataPointer(int $pointer) + { + $resolved = $pointer - $this->metadata->nodeCount + $this->metadata->searchTreeSize; + if ($resolved >= $this->fileSize) { + throw new InvalidDatabaseException("The MaxMind DB file's search tree is corrupt"); + } + [$data] = $this->decoder->decode($resolved); + return $data; + } + /* + * This is an extremely naive but reasonably readable implementation. There + * are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever + * an issue, but I suspect it won't be. + */ + private function findMetadataStart(string $filename) : int + { + $handle = $this->fileHandle; + $fstat = \fstat($handle); + $fileSize = $fstat['size']; + $marker = self::$METADATA_START_MARKER; + $markerLength = self::$METADATA_START_MARKER_LENGTH; + $minStart = $fileSize - \min(self::$METADATA_MAX_SIZE, $fileSize); + for ($offset = $fileSize - $markerLength; $offset >= $minStart; --$offset) { + if (\fseek($handle, $offset) !== 0) { + break; + } + $value = \fread($handle, $markerLength); + if ($value === $marker) { + return $offset + $markerLength; + } + } + throw new InvalidDatabaseException("Error opening database file ({$filename}). " . 'Is this a valid MaxMind DB file?'); + } + /** + * @throws InvalidArgumentException if arguments are passed to the method + * @throws BadMethodCallException if the database has been closed + * + * @return Metadata object for the database + */ + public function metadata() : Metadata + { + if (\func_num_args()) { + throw new ArgumentCountError(\sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args())); + } + // Not technically required, but this makes it consistent with + // C extension and it allows us to change our implementation later. + if (!\is_resource($this->fileHandle)) { + throw new BadMethodCallException('Attempt to read from a closed MaxMind DB.'); + } + return clone $this->metadata; + } + /** + * Closes the MaxMind DB and returns resources to the system. + * + * @throws Exception + * if an I/O error occurs + */ + public function close() : void + { + if (\func_num_args()) { + throw new ArgumentCountError(\sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args())); + } + if (!\is_resource($this->fileHandle)) { + throw new BadMethodCallException('Attempt to close a closed MaxMind DB.'); + } + \fclose($this->fileHandle); + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php new file mode 100644 index 00000000..156aac1f --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php @@ -0,0 +1,275 @@ +fileStream = $fileStream; + $this->pointerBase = $pointerBase; + $this->pointerBaseByteSize = $pointerBase > 0 ? \log($pointerBase, 2) / 8 : 0; + $this->pointerTestHack = $pointerTestHack; + $this->switchByteOrder = $this->isPlatformLittleEndian(); + } + public function decode(int $offset) : array + { + $ctrlByte = \ord(Util::read($this->fileStream, $offset, 1)); + ++$offset; + $type = $ctrlByte >> 5; + // Pointers are a special case, we don't read the next $size bytes, we + // use the size to determine the length of the pointer and then follow + // it. + if ($type === self::_POINTER) { + [$pointer, $offset] = $this->decodePointer($ctrlByte, $offset); + // for unit testing + if ($this->pointerTestHack) { + return [$pointer]; + } + [$result] = $this->decode($pointer); + return [$result, $offset]; + } + if ($type === self::_EXTENDED) { + $nextByte = \ord(Util::read($this->fileStream, $offset, 1)); + $type = $nextByte + 7; + if ($type < 8) { + throw new InvalidDatabaseException('Something went horribly wrong in the decoder. An extended type ' . 'resolved to a type number < 8 (' . $type . ')'); + } + ++$offset; + } + [$size, $offset] = $this->sizeFromCtrlByte($ctrlByte, $offset); + return $this->decodeByType($type, $offset, $size); + } + private function decodeByType(int $type, int $offset, int $size) : array + { + switch ($type) { + case self::_MAP: + return $this->decodeMap($size, $offset); + case self::_ARRAY: + return $this->decodeArray($size, $offset); + case self::_BOOLEAN: + return [$this->decodeBoolean($size), $offset]; + } + $newOffset = $offset + $size; + $bytes = Util::read($this->fileStream, $offset, $size); + switch ($type) { + case self::_BYTES: + case self::_UTF8_STRING: + return [$bytes, $newOffset]; + case self::_DOUBLE: + $this->verifySize(8, $size); + return [$this->decodeDouble($bytes), $newOffset]; + case self::_FLOAT: + $this->verifySize(4, $size); + return [$this->decodeFloat($bytes), $newOffset]; + case self::_INT32: + return [$this->decodeInt32($bytes, $size), $newOffset]; + case self::_UINT16: + case self::_UINT32: + case self::_UINT64: + case self::_UINT128: + return [$this->decodeUint($bytes, $size), $newOffset]; + default: + throw new InvalidDatabaseException('Unknown or unexpected type: ' . $type); + } + } + private function verifySize(int $expected, int $actual) : void + { + if ($expected !== $actual) { + throw new InvalidDatabaseException("The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"); + } + } + private function decodeArray(int $size, int $offset) : array + { + $array = []; + for ($i = 0; $i < $size; ++$i) { + [$value, $offset] = $this->decode($offset); + $array[] = $value; + } + return [$array, $offset]; + } + private function decodeBoolean(int $size) : bool + { + return $size !== 0; + } + private function decodeDouble(string $bytes) : float + { + // This assumes IEEE 754 doubles, but most (all?) modern platforms + // use them. + [, $double] = \unpack('E', $bytes); + return $double; + } + private function decodeFloat(string $bytes) : float + { + // This assumes IEEE 754 floats, but most (all?) modern platforms + // use them. + [, $float] = \unpack('G', $bytes); + return $float; + } + private function decodeInt32(string $bytes, int $size) : int + { + switch ($size) { + case 0: + return 0; + case 1: + case 2: + case 3: + $bytes = \str_pad($bytes, 4, "\x00", \STR_PAD_LEFT); + break; + case 4: + break; + default: + throw new InvalidDatabaseException("The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"); + } + [, $int] = \unpack('l', $this->maybeSwitchByteOrder($bytes)); + return $int; + } + private function decodeMap(int $size, int $offset) : array + { + $map = []; + for ($i = 0; $i < $size; ++$i) { + [$key, $offset] = $this->decode($offset); + [$value, $offset] = $this->decode($offset); + $map[$key] = $value; + } + return [$map, $offset]; + } + private function decodePointer(int $ctrlByte, int $offset) : array + { + $pointerSize = ($ctrlByte >> 3 & 0x3) + 1; + $buffer = Util::read($this->fileStream, $offset, $pointerSize); + $offset = $offset + $pointerSize; + switch ($pointerSize) { + case 1: + $packed = \chr($ctrlByte & 0x7) . $buffer; + [, $pointer] = \unpack('n', $packed); + $pointer += $this->pointerBase; + break; + case 2: + $packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer; + [, $pointer] = \unpack('N', $packed); + $pointer += $this->pointerBase + 2048; + break; + case 3: + $packed = \chr($ctrlByte & 0x7) . $buffer; + // It is safe to use 'N' here, even on 32 bit machines as the + // first bit is 0. + [, $pointer] = \unpack('N', $packed); + $pointer += $this->pointerBase + 526336; + break; + case 4: + // We cannot use unpack here as we might overflow on 32 bit + // machines + $pointerOffset = $this->decodeUint($buffer, $pointerSize); + $pointerBase = $this->pointerBase; + if (\PHP_INT_MAX - $pointerBase >= $pointerOffset) { + $pointer = $pointerOffset + $pointerBase; + } else { + throw new RuntimeException('The database offset is too large to be represented on your platform.'); + } + break; + default: + throw new InvalidDatabaseException('Unexpected pointer size ' . $pointerSize); + } + return [$pointer, $offset]; + } + // @phpstan-ignore-next-line + private function decodeUint(string $bytes, int $byteLength) + { + if ($byteLength === 0) { + return 0; + } + $integer = 0; + // PHP integers are signed. PHP_INT_SIZE - 1 is the number of + // complete bytes that can be converted to an integer. However, + // we can convert another byte if the leading bit is zero. + $useRealInts = $byteLength <= \PHP_INT_SIZE - 1 || $byteLength === \PHP_INT_SIZE && (\ord($bytes[0]) & 0x80) === 0; + for ($i = 0; $i < $byteLength; ++$i) { + $part = \ord($bytes[$i]); + // We only use gmp or bcmath if the final value is too big + if ($useRealInts) { + $integer = ($integer << 8) + $part; + } elseif (\extension_loaded('gmp')) { + $integer = \gmp_strval(\gmp_add(\gmp_mul((string) $integer, '256'), $part)); + } elseif (\extension_loaded('bcmath')) { + $integer = \bcadd(\bcmul((string) $integer, '256'), (string) $part); + } else { + throw new RuntimeException('The gmp or bcmath extension must be installed to read this database.'); + } + } + return $integer; + } + private function sizeFromCtrlByte(int $ctrlByte, int $offset) : array + { + $size = $ctrlByte & 0x1f; + if ($size < 29) { + return [$size, $offset]; + } + $bytesToRead = $size - 28; + $bytes = Util::read($this->fileStream, $offset, $bytesToRead); + if ($size === 29) { + $size = 29 + \ord($bytes); + } elseif ($size === 30) { + [, $adjust] = \unpack('n', $bytes); + $size = 285 + $adjust; + } else { + [, $adjust] = \unpack('N', "\x00" . $bytes); + $size = $adjust + 65821; + } + return [$size, $offset + $bytesToRead]; + } + private function maybeSwitchByteOrder(string $bytes) : string + { + return $this->switchByteOrder ? \strrev($bytes) : $bytes; + } + private function isPlatformLittleEndian() : bool + { + $testint = 0xff; + $packed = \pack('S', $testint); + return $testint === \current(\unpack('v', $packed)); + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/InvalidDatabaseException.php b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/InvalidDatabaseException.php new file mode 100644 index 00000000..b55d5491 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/InvalidDatabaseException.php @@ -0,0 +1,12 @@ +binaryFormatMajorVersion = $metadata['binary_format_major_version']; + $this->binaryFormatMinorVersion = $metadata['binary_format_minor_version']; + $this->buildEpoch = $metadata['build_epoch']; + $this->databaseType = $metadata['database_type']; + $this->languages = $metadata['languages']; + $this->description = $metadata['description']; + $this->ipVersion = $metadata['ip_version']; + $this->nodeCount = $metadata['node_count']; + $this->recordSize = $metadata['record_size']; + $this->nodeByteSize = $this->recordSize / 4; + $this->searchTreeSize = $this->nodeCount * $this->nodeByteSize; + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Util.php b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Util.php new file mode 100644 index 00000000..59447979 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Util.php @@ -0,0 +1,27 @@ +&2 + exit 1 +fi + +php composer.phar self-update +php composer.phar update + +./vendor/bin/phpunit + +echo "Release notes for $tag:" +echo "$notes" + +read -e -p "Commit changes and push to origin? " should_push + +if [ "$should_push" != "y" ]; then + echo "Aborting" + exit 1 +fi + +git push + +gh release create --target "$(git branch --show-current)" -t "$version" -n "$notes" "$tag" + +git push --tags diff --git a/plugins/system/tgeoip/vendor/maxmind/web-service-common/phpstan.neon b/plugins/system/tgeoip/vendor/maxmind/web-service-common/phpstan.neon new file mode 100644 index 00000000..ee1616db --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind/web-service-common/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + level: 6 + paths: + - src + - tests + checkMissingIterableValueType: false + diff --git a/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/AuthenticationException.php b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/AuthenticationException.php new file mode 100644 index 00000000..dd1ab870 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/AuthenticationException.php @@ -0,0 +1,11 @@ +uri = $uri; + parent::__construct($message, $httpStatus, $previous); + } + public function getUri() : string + { + return $this->uri; + } + public function getStatusCode() : int + { + return $this->getCode(); + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/InsufficientFundsException.php b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/InsufficientFundsException.php new file mode 100644 index 00000000..93ca74b7 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/InsufficientFundsException.php @@ -0,0 +1,11 @@ +error = $error; + parent::__construct($message, $httpStatus, $uri, $previous); + } + public function getErrorCode() : string + { + return $this->error; + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/IpAddressNotFoundException.php b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/IpAddressNotFoundException.php new file mode 100644 index 00000000..6383bb87 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/IpAddressNotFoundException.php @@ -0,0 +1,8 @@ +accountId = $accountId; + $this->licenseKey = $licenseKey; + $this->httpRequestFactory = isset($options['httpRequestFactory']) ? $options['httpRequestFactory'] : new RequestFactory(); + if (isset($options['host'])) { + $this->host = $options['host']; + } + if (isset($options['useHttps'])) { + $this->useHttps = $options['useHttps']; + } + if (isset($options['userAgent'])) { + $this->userAgentPrefix = $options['userAgent'] . ' '; + } + $this->caBundle = isset($options['caBundle']) ? $this->caBundle = $options['caBundle'] : $this->getCaBundle(); + if (isset($options['connectTimeout'])) { + $this->connectTimeout = $options['connectTimeout']; + } + if (isset($options['timeout'])) { + $this->timeout = $options['timeout']; + } + if (isset($options['proxy'])) { + $this->proxy = $options['proxy']; + } + } + /** + * @param string $service name of the service querying + * @param string $path the URI path to use + * @param array $input the data to be posted as JSON + * + * @throws InvalidInputException when the request has missing or invalid + * data + * @throws AuthenticationException when there is an issue authenticating the + * request + * @throws InsufficientFundsException when your account is out of funds + * @throws InvalidRequestException when the request is invalid for some + * other reason, e.g., invalid JSON in the POST. + * @throws HttpException when an unexpected HTTP error occurs + * @throws WebServiceException when some other error occurs. This also + * serves as the base class for the above exceptions. + * + * @return array|null The decoded content of a successful response + */ + public function post(string $service, string $path, array $input) : ?array + { + $requestBody = \json_encode($input); + if ($requestBody === \false) { + throw new InvalidInputException('Error encoding input as JSON: ' . $this->jsonErrorDescription()); + } + $request = $this->createRequest($path, ['Content-Type: application/json']); + [$statusCode, $contentType, $responseBody] = $request->post($requestBody); + return $this->handleResponse($statusCode, $contentType, $responseBody, $service, $path); + } + public function get(string $service, string $path) : ?array + { + $request = $this->createRequest($path); + [$statusCode, $contentType, $responseBody] = $request->get(); + return $this->handleResponse($statusCode, $contentType, $responseBody, $service, $path); + } + private function userAgent() : string + { + $curlVersion = \curl_version(); + return $this->userAgentPrefix . 'MaxMind-WS-API/' . self::VERSION . ' PHP/' . \PHP_VERSION . ' curl/' . $curlVersion['version']; + } + private function createRequest(string $path, array $headers = []) : Http\Request + { + \array_push($headers, 'Authorization: Basic ' . \base64_encode($this->accountId . ':' . $this->licenseKey), 'Accept: application/json'); + return $this->httpRequestFactory->request($this->urlFor($path), ['caBundle' => $this->caBundle, 'connectTimeout' => $this->connectTimeout, 'headers' => $headers, 'proxy' => $this->proxy, 'timeout' => $this->timeout, 'userAgent' => $this->userAgent()]); + } + /** + * @param int $statusCode the HTTP status code of the response + * @param string|null $contentType the Content-Type of the response + * @param string|null $responseBody the response body + * @param string $service the name of the service + * @param string $path the path used in the request + * + * @throws AuthenticationException when there is an issue authenticating the + * request + * @throws InsufficientFundsException when your account is out of funds + * @throws InvalidRequestException when the request is invalid for some + * other reason, e.g., invalid JSON in the POST. + * @throws HttpException when an unexpected HTTP error occurs + * @throws WebServiceException when some other error occurs. This also + * serves as the base class for the above exceptions + * + * @return array|null The decoded content of a successful response + */ + private function handleResponse(int $statusCode, ?string $contentType, ?string $responseBody, string $service, string $path) : ?array + { + if ($statusCode >= 400 && $statusCode <= 499) { + $this->handle4xx($statusCode, $contentType, $responseBody, $service, $path); + } elseif ($statusCode >= 500) { + $this->handle5xx($statusCode, $service, $path); + } elseif ($statusCode !== 200 && $statusCode !== 204) { + $this->handleUnexpectedStatus($statusCode, $service, $path); + } + return $this->handleSuccess($statusCode, $responseBody, $service); + } + /** + * @return string describing the JSON error + */ + private function jsonErrorDescription() : string + { + $errno = \json_last_error(); + switch ($errno) { + case \JSON_ERROR_DEPTH: + return 'The maximum stack depth has been exceeded.'; + case \JSON_ERROR_STATE_MISMATCH: + return 'Invalid or malformed JSON.'; + case \JSON_ERROR_CTRL_CHAR: + return 'Control character error.'; + case \JSON_ERROR_SYNTAX: + return 'Syntax error.'; + case \JSON_ERROR_UTF8: + return 'Malformed UTF-8 characters.'; + default: + return "Other JSON error ({$errno})."; + } + } + /** + * @param string $path the path to use in the URL + * + * @return string the constructed URL + */ + private function urlFor(string $path) : string + { + return ($this->useHttps ? 'https://' : 'http://') . $this->host . $path; + } + /** + * @param int $statusCode the HTTP status code + * @param string|null $contentType the response content-type + * @param string|null $body the response body + * @param string $service the service name + * @param string $path the path used in the request + * + * @throws AuthenticationException + * @throws HttpException + * @throws InsufficientFundsException + * @throws InvalidRequestException + */ + private function handle4xx(int $statusCode, ?string $contentType, ?string $body, string $service, string $path) : void + { + if ($body === null || $body === '') { + throw new HttpException("Received a {$statusCode} error for {$service} with no body", $statusCode, $this->urlFor($path)); + } + if ($contentType === null || !\strstr($contentType, 'json')) { + throw new HttpException("Received a {$statusCode} error for {$service} with " . 'the following body: ' . $body, $statusCode, $this->urlFor($path)); + } + $message = \json_decode($body, \true); + if ($message === null) { + throw new HttpException("Received a {$statusCode} error for {$service} but could " . 'not decode the response as JSON: ' . $this->jsonErrorDescription() . ' Body: ' . $body, $statusCode, $this->urlFor($path)); + } + if (!isset($message['code']) || !isset($message['error'])) { + throw new HttpException('Error response contains JSON but it does not ' . 'specify code or error keys: ' . $body, $statusCode, $this->urlFor($path)); + } + $this->handleWebServiceError($message['error'], $message['code'], $statusCode, $path); + } + /** + * @param string $message the error message from the web service + * @param string $code the error code from the web service + * @param int $statusCode the HTTP status code + * @param string $path the path used in the request + * + * @throws AuthenticationException + * @throws InvalidRequestException + * @throws InsufficientFundsException + */ + private function handleWebServiceError(string $message, string $code, int $statusCode, string $path) : void + { + switch ($code) { + case 'IP_ADDRESS_NOT_FOUND': + case 'IP_ADDRESS_RESERVED': + throw new IpAddressNotFoundException($message, $code, $statusCode, $this->urlFor($path)); + case 'ACCOUNT_ID_REQUIRED': + case 'ACCOUNT_ID_UNKNOWN': + case 'AUTHORIZATION_INVALID': + case 'LICENSE_KEY_REQUIRED': + case 'USER_ID_REQUIRED': + case 'USER_ID_UNKNOWN': + throw new AuthenticationException($message, $code, $statusCode, $this->urlFor($path)); + case 'OUT_OF_QUERIES': + case 'INSUFFICIENT_FUNDS': + throw new InsufficientFundsException($message, $code, $statusCode, $this->urlFor($path)); + case 'PERMISSION_REQUIRED': + throw new PermissionRequiredException($message, $code, $statusCode, $this->urlFor($path)); + default: + throw new InvalidRequestException($message, $code, $statusCode, $this->urlFor($path)); + } + } + /** + * @param int $statusCode the HTTP status code + * @param string $service the service name + * @param string $path the URI path used in the request + * + * @throws HttpException + */ + private function handle5xx(int $statusCode, string $service, string $path) : void + { + throw new HttpException("Received a server error ({$statusCode}) for {$service}", $statusCode, $this->urlFor($path)); + } + /** + * @param int $statusCode the HTTP status code + * @param string $service the service name + * @param string $path the URI path used in the request + * + * @throws HttpException + */ + private function handleUnexpectedStatus(int $statusCode, string $service, string $path) : void + { + throw new HttpException('Received an unexpected HTTP status ' . "({$statusCode}) for {$service}", $statusCode, $this->urlFor($path)); + } + /** + * @param int $statusCode the HTTP status code + * @param string|null $body the successful request body + * @param string $service the service name + * + * @throws WebServiceException if a response body is included but not + * expected, or is not expected but not + * included, or is expected and included + * but cannot be decoded as JSON + * + * @return array|null the decoded request body + */ + private function handleSuccess(int $statusCode, ?string $body, string $service) : ?array + { + // A 204 should have no response body + if ($statusCode === 204) { + if ($body !== null && $body !== '') { + throw new WebServiceException("Received a 204 response for {$service} along with an " . "unexpected HTTP body: {$body}"); + } + return null; + } + // A 200 should have a valid JSON body + if ($body === null || $body === '') { + throw new WebServiceException("Received a 200 response for {$service} but did not " . 'receive a HTTP body.'); + } + $decodedContent = \json_decode($body, \true); + if ($decodedContent === null) { + throw new WebServiceException("Received a 200 response for {$service} but could " . 'not decode the response as JSON: ' . $this->jsonErrorDescription() . ' Body: ' . $body); + } + return $decodedContent; + } + private function getCaBundle() : ?string + { + $curlVersion = \curl_version(); + // On OS X, when the SSL version is "SecureTransport", the system's + // keychain will be used. + if ($curlVersion['ssl_version'] === 'SecureTransport') { + return null; + } + $cert = CaBundle::getSystemCaRootBundlePath(); + // Check if the cert is inside a phar. If so, we need to copy the cert + // to a temp file so that curl can see it. + if (\substr($cert, 0, 7) === 'phar://') { + $tempDir = \sys_get_temp_dir(); + $newCert = \tempnam($tempDir, 'geoip2-'); + if ($newCert === \false) { + throw new \RuntimeException("Unable to create temporary file in {$tempDir}"); + } + if (!\copy($cert, $newCert)) { + throw new \RuntimeException("Could not copy {$cert} to {$newCert}: " . \var_export(\error_get_last(), \true)); + } + // We use a shutdown function rather than the destructor as the + // destructor isn't called on a fatal error such as an uncaught + // exception. + \register_shutdown_function(function () use($newCert) { + \unlink($newCert); + }); + $cert = $newCert; + } + if (!\file_exists($cert)) { + throw new \RuntimeException("CA cert does not exist at {$cert}"); + } + return $cert; + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/CurlRequest.php b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/CurlRequest.php new file mode 100644 index 00000000..f11e38a8 --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/CurlRequest.php @@ -0,0 +1,108 @@ +url = $url; + $this->options = $options; + $this->ch = $options['curlHandle']; + } + /** + * @throws HttpException + */ + public function post(string $body) : array + { + $curl = $this->createCurl(); + \curl_setopt($curl, \CURLOPT_POST, \true); + \curl_setopt($curl, \CURLOPT_POSTFIELDS, $body); + return $this->execute($curl); + } + public function get() : array + { + $curl = $this->createCurl(); + \curl_setopt($curl, \CURLOPT_HTTPGET, \true); + return $this->execute($curl); + } + /** + * @return \CurlHandle + */ + private function createCurl() + { + \curl_reset($this->ch); + $opts = []; + $opts[\CURLOPT_URL] = $this->url; + if (!empty($this->options['caBundle'])) { + $opts[\CURLOPT_CAINFO] = $this->options['caBundle']; + } + $opts[\CURLOPT_ENCODING] = ''; + $opts[\CURLOPT_SSL_VERIFYHOST] = 2; + $opts[\CURLOPT_FOLLOWLOCATION] = \false; + $opts[\CURLOPT_SSL_VERIFYPEER] = \true; + $opts[\CURLOPT_RETURNTRANSFER] = \true; + $opts[\CURLOPT_HTTPHEADER] = $this->options['headers']; + $opts[\CURLOPT_USERAGENT] = $this->options['userAgent']; + $opts[\CURLOPT_PROXY] = $this->options['proxy']; + // The defined()s are here as the *_MS opts are not available on older + // cURL versions + $connectTimeout = $this->options['connectTimeout']; + if (\defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $opts[\CURLOPT_CONNECTTIMEOUT_MS] = \ceil($connectTimeout * 1000); + } else { + $opts[\CURLOPT_CONNECTTIMEOUT] = \ceil($connectTimeout); + } + $timeout = $this->options['timeout']; + if (\defined('CURLOPT_TIMEOUT_MS')) { + $opts[\CURLOPT_TIMEOUT_MS] = \ceil($timeout * 1000); + } else { + $opts[\CURLOPT_TIMEOUT] = \ceil($timeout); + } + \curl_setopt_array($this->ch, $opts); + return $this->ch; + } + /** + * @param \CurlHandle $curl + * + * @throws HttpException + */ + private function execute($curl) : array + { + $body = \curl_exec($curl); + if ($errno = \curl_errno($curl)) { + $errorMessage = \curl_error($curl); + throw new HttpException("cURL error ({$errno}): {$errorMessage}", 0, $this->url); + } + $statusCode = \curl_getinfo($curl, \CURLINFO_HTTP_CODE); + $contentType = \curl_getinfo($curl, \CURLINFO_CONTENT_TYPE); + return [ + $statusCode, + // The PHP docs say "Content-Type: of the requested document. NULL + // indicates server did not send valid Content-Type: header" for + // CURLINFO_CONTENT_TYPE. However, it will return FALSE if no header + // is set. To keep our types simple, we return null in this case. + $contentType === \false ? null : $contentType, + $body, + ]; + } +} diff --git a/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/Request.php b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/Request.php new file mode 100644 index 00000000..165cb20c --- /dev/null +++ b/plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/Request.php @@ -0,0 +1,16 @@ +ch)) { + \curl_close($this->ch); + } + } + /** + * @return \CurlHandle + */ + private function getCurlHandle() + { + if (empty($this->ch)) { + $this->ch = \curl_init(); + } + return $this->ch; + } + public function request(string $url, array $options) : Request + { + $options['curlHandle'] = $this->getCurlHandle(); + return new CurlRequest($url, $options); + } +} diff --git a/plugins/system/tgeoip/vendor/scoper-autoload.php b/plugins/system/tgeoip/vendor/scoper-autoload.php new file mode 100644 index 00000000..9cd1ebf3 --- /dev/null +++ b/plugins/system/tgeoip/vendor/scoper-autoload.php @@ -0,0 +1,37 @@ + /dev/null +git checkout -B gh-pages + +# Push generated files +git add . +git commit -m "Docs updated by Travis" +git push origin gh-pages -fq > /dev/null diff --git a/plugins/system/tgeoip/vendor/splitbrain/php-archive/phpunit.xml b/plugins/system/tgeoip/vendor/splitbrain/php-archive/phpunit.xml new file mode 100644 index 00000000..41c7fb15 --- /dev/null +++ b/plugins/system/tgeoip/vendor/splitbrain/php-archive/phpunit.xml @@ -0,0 +1,21 @@ + + + + + ./tests/ + + + + + src + + + diff --git a/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Archive.php b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Archive.php new file mode 100644 index 00000000..ddebce2a --- /dev/null +++ b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Archive.php @@ -0,0 +1,122 @@ +callback = $callback; + } +} diff --git a/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveCorruptedException.php b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveCorruptedException.php new file mode 100644 index 00000000..1366928f --- /dev/null +++ b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveCorruptedException.php @@ -0,0 +1,10 @@ + + * @package splitbrain\PHPArchive + * @license MIT + */ +class FileInfo +{ + protected $isdir = \false; + protected $path = ''; + protected $size = 0; + protected $csize = 0; + protected $mtime = 0; + protected $mode = 0664; + protected $owner = ''; + protected $group = ''; + protected $uid = 0; + protected $gid = 0; + protected $comment = ''; + /** + * initialize dynamic defaults + * + * @param string $path The path of the file, can also be set later through setPath() + */ + public function __construct($path = '') + { + $this->mtime = \time(); + $this->setPath($path); + } + /** + * Handle calls to deprecated methods + * + * @param string $name + * @param array $arguments + * @return mixed + */ + public function __call($name, $arguments) + { + if ($name === 'match') { + \trigger_error('FileInfo::match() is deprecated, use FileInfo::matchExpression() instead.', \E_USER_NOTICE); + return \call_user_func_array([$this, $name], $arguments); + } + \trigger_error('Call to undefined method FileInfo::' . $name . '()', \E_USER_ERROR); + return null; + } + /** + * Factory to build FileInfo from existing file or directory + * + * @param string $path path to a file on the local file system + * @param string $as optional path to use inside the archive + * @throws FileInfoException + * @return FileInfo + */ + public static function fromPath($path, $as = '') + { + \clearstatcache(\false, $path); + if (!\file_exists($path)) { + throw new FileInfoException("{$path} does not exist"); + } + $stat = \stat($path); + $file = new FileInfo(); + $file->setPath($path); + $file->setIsdir(\is_dir($path)); + $file->setMode(\fileperms($path)); + $file->setOwner(\fileowner($path)); + $file->setGroup(\filegroup($path)); + $file->setSize(\filesize($path)); + $file->setUid($stat['uid']); + $file->setGid($stat['gid']); + $file->setMtime($stat['mtime']); + if ($as) { + $file->setPath($as); + } + return $file; + } + /** + * @return int the filesize. always 0 for directories + */ + public function getSize() + { + if ($this->isdir) { + return 0; + } + return $this->size; + } + /** + * @param int $size + */ + public function setSize($size) + { + $this->size = $size; + } + /** + * @return int + */ + public function getCompressedSize() + { + return $this->csize; + } + /** + * @param int $csize + */ + public function setCompressedSize($csize) + { + $this->csize = $csize; + } + /** + * @return int + */ + public function getMtime() + { + return $this->mtime; + } + /** + * @param int $mtime + */ + public function setMtime($mtime) + { + $this->mtime = $mtime; + } + /** + * @return int + */ + public function getGid() + { + return $this->gid; + } + /** + * @param int $gid + */ + public function setGid($gid) + { + $this->gid = $gid; + } + /** + * @return int + */ + public function getUid() + { + return $this->uid; + } + /** + * @param int $uid + */ + public function setUid($uid) + { + $this->uid = $uid; + } + /** + * @return string + */ + public function getComment() + { + return $this->comment; + } + /** + * @param string $comment + */ + public function setComment($comment) + { + $this->comment = $comment; + } + /** + * @return string + */ + public function getGroup() + { + return $this->group; + } + /** + * @param string $group + */ + public function setGroup($group) + { + $this->group = $group; + } + /** + * @return boolean + */ + public function getIsdir() + { + return $this->isdir; + } + /** + * @param boolean $isdir + */ + public function setIsdir($isdir) + { + // default mode for directories + if ($isdir && $this->mode === 0664) { + $this->mode = 0775; + } + $this->isdir = $isdir; + } + /** + * @return int + */ + public function getMode() + { + return $this->mode; + } + /** + * @param int $mode + */ + public function setMode($mode) + { + $this->mode = $mode; + } + /** + * @return string + */ + public function getOwner() + { + return $this->owner; + } + /** + * @param string $owner + */ + public function setOwner($owner) + { + $this->owner = $owner; + } + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + /** + * @param string $path + */ + public function setPath($path) + { + $this->path = $this->cleanPath($path); + } + /** + * Cleans up a path and removes relative parts, also strips leading slashes + * + * @param string $path + * @return string + */ + protected function cleanPath($path) + { + $path = \str_replace('\\', '/', $path); + $path = \explode('/', $path); + $newpath = array(); + foreach ($path as $p) { + if ($p === '' || $p === '.') { + continue; + } + if ($p === '..') { + \array_pop($newpath); + continue; + } + \array_push($newpath, $p); + } + return \trim(\implode('/', $newpath), '/'); + } + /** + * Strip given prefix or number of path segments from the filename + * + * The $strip parameter allows you to strip a certain number of path components from the filenames + * found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when + * an integer is passed as $strip. + * Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix, + * the prefix will be stripped. It is recommended to give prefixes with a trailing slash. + * + * @param int|string $strip + */ + public function strip($strip) + { + $filename = $this->getPath(); + $striplen = \strlen($strip); + if (\is_int($strip)) { + // if $strip is an integer we strip this many path components + $parts = \explode('/', $filename); + if (!$this->getIsdir()) { + $base = \array_pop($parts); + // keep filename itself + } else { + $base = ''; + } + $filename = \join('/', \array_slice($parts, $strip)); + if ($base) { + $filename .= "/{$base}"; + } + } else { + // if strip is a string, we strip a prefix here + if (\substr($filename, 0, $striplen) == $strip) { + $filename = \substr($filename, $striplen); + } + } + $this->setPath($filename); + } + /** + * Does the file match the given include and exclude expressions? + * + * Exclude rules take precedence over include rules + * + * @param string $include Regular expression of files to include + * @param string $exclude Regular expression of files to exclude + * @return bool + */ + public function matchExpression($include = '', $exclude = '') + { + $extract = \true; + if ($include && !\preg_match($include, $this->getPath())) { + $extract = \false; + } + if ($exclude && \preg_match($exclude, $this->getPath())) { + $extract = \false; + } + return $extract; + } +} diff --git a/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfoException.php b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfoException.php new file mode 100644 index 00000000..697f8e0b --- /dev/null +++ b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfoException.php @@ -0,0 +1,10 @@ +100 chars) are supported in POSIX ustar and GNU longlink formats. + * + * @author Andreas Gohr + * @package splitbrain\PHPArchive + * @license MIT + */ +class Tar extends Archive +{ + protected $file = ''; + protected $comptype = Archive::COMPRESS_AUTO; + protected $complevel = 9; + protected $fh; + protected $memory = ''; + protected $closed = \true; + protected $writeaccess = \false; + /** + * Sets the compression to use + * + * @param int $level Compression level (0 to 9) + * @param int $type Type of compression to use (use COMPRESS_* constants) + * @throws ArchiveIllegalCompressionException + */ + public function setCompression($level = 9, $type = Archive::COMPRESS_AUTO) + { + $this->compressioncheck($type); + if ($level < -1 || $level > 9) { + throw new ArchiveIllegalCompressionException('Compression level should be between -1 and 9'); + } + $this->comptype = $type; + $this->complevel = $level; + if ($level == 0) { + $this->comptype = Archive::COMPRESS_NONE; + } + if ($type == Archive::COMPRESS_NONE) { + $this->complevel = 0; + } + } + /** + * Open an existing TAR file for reading + * + * @param string $file + * @throws ArchiveIOException + * @throws ArchiveIllegalCompressionException + */ + public function open($file) + { + $this->file = $file; + // update compression to mach file + if ($this->comptype == Tar::COMPRESS_AUTO) { + $this->setCompression($this->complevel, $this->filetype($file)); + } + // open file handles + if ($this->comptype === Archive::COMPRESS_GZIP) { + $this->fh = @\gzopen($this->file, 'rb'); + } elseif ($this->comptype === Archive::COMPRESS_BZIP) { + $this->fh = @\bzopen($this->file, 'r'); + } else { + $this->fh = @\fopen($this->file, 'rb'); + } + if (!$this->fh) { + throw new ArchiveIOException('Could not open file for reading: ' . $this->file); + } + $this->closed = \false; + } + /** + * Read the contents of a TAR archive + * + * This function lists the files stored in the archive + * + * The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams. + * Reopen the file with open() again if you want to do additional operations + * + * @throws ArchiveIOException + * @throws ArchiveCorruptedException + * @returns FileInfo[] + */ + public function contents() + { + $result = array(); + foreach ($this->yieldContents() as $fileinfo) { + $result[] = $fileinfo; + } + return $result; + } + /** + * Read the contents of a TAR archive and return each entry using yield + * for memory efficiency. + * + * @see contents() + * @throws ArchiveIOException + * @throws ArchiveCorruptedException + * @returns FileInfo[] + */ + public function yieldContents() + { + if ($this->closed || !$this->file) { + throw new ArchiveIOException('Can not read from a closed archive'); + } + while ($read = $this->readbytes(512)) { + $header = $this->parseHeader($read); + if (!\is_array($header)) { + continue; + } + $this->skipbytes(\ceil($header['size'] / 512) * 512); + (yield $this->header2fileinfo($header)); + } + $this->close(); + } + /** + * Extract an existing TAR archive + * + * The $strip parameter allows you to strip a certain number of path components from the filenames + * found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when + * an integer is passed as $strip. + * Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix, + * the prefix will be stripped. It is recommended to give prefixes with a trailing slash. + * + * By default this will extract all files found in the archive. You can restrict the output using the $include + * and $exclude parameter. Both expect a full regular expression (including delimiters and modifiers). If + * $include is set only files that match this expression will be extracted. Files that match the $exclude + * expression will never be extracted. Both parameters can be used in combination. Expressions are matched against + * stripped filenames as described above. + * + * The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams. + * Reopen the file with open() again if you want to do additional operations + * + * @param string $outdir the target directory for extracting + * @param int|string $strip either the number of path components or a fixed prefix to strip + * @param string $exclude a regular expression of files to exclude + * @param string $include a regular expression of files to include + * @throws ArchiveIOException + * @throws ArchiveCorruptedException + * @return FileInfo[] + */ + public function extract($outdir, $strip = '', $exclude = '', $include = '') + { + if ($this->closed || !$this->file) { + throw new ArchiveIOException('Can not read from a closed archive'); + } + $outdir = \rtrim($outdir, '/'); + @\mkdir($outdir, 0777, \true); + if (!\is_dir($outdir)) { + throw new ArchiveIOException("Could not create directory '{$outdir}'"); + } + $extracted = array(); + while ($dat = $this->readbytes(512)) { + // read the file header + $header = $this->parseHeader($dat); + if (!\is_array($header)) { + continue; + } + $fileinfo = $this->header2fileinfo($header); + // apply strip rules + $fileinfo->strip($strip); + // skip unwanted files + if (!\strlen($fileinfo->getPath()) || !$fileinfo->matchExpression($include, $exclude)) { + $this->skipbytes(\ceil($header['size'] / 512) * 512); + continue; + } + // create output directory + $output = $outdir . '/' . $fileinfo->getPath(); + $directory = $fileinfo->getIsdir() ? $output : \dirname($output); + if (!\file_exists($directory)) { + \mkdir($directory, 0777, \true); + } + // extract data + if (!$fileinfo->getIsdir()) { + $fp = @\fopen($output, "wb"); + if (!$fp) { + throw new ArchiveIOException('Could not open file for writing: ' . $output); + } + $size = \floor($header['size'] / 512); + for ($i = 0; $i < $size; $i++) { + \fwrite($fp, $this->readbytes(512), 512); + } + if ($header['size'] % 512 != 0) { + \fwrite($fp, $this->readbytes(512), $header['size'] % 512); + } + \fclose($fp); + @\touch($output, $fileinfo->getMtime()); + @\chmod($output, $fileinfo->getMode()); + } else { + $this->skipbytes(\ceil($header['size'] / 512) * 512); + // the size is usually 0 for directories + } + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + $extracted[] = $fileinfo; + } + $this->close(); + return $extracted; + } + /** + * Create a new TAR file + * + * If $file is empty, the tar file will be created in memory + * + * @param string $file + * @throws ArchiveIOException + * @throws ArchiveIllegalCompressionException + */ + public function create($file = '') + { + $this->file = $file; + $this->memory = ''; + $this->fh = 0; + if ($this->file) { + // determine compression + if ($this->comptype == Archive::COMPRESS_AUTO) { + $this->setCompression($this->complevel, $this->filetype($file)); + } + if ($this->comptype === Archive::COMPRESS_GZIP) { + $this->fh = @\gzopen($this->file, 'wb' . $this->complevel); + } elseif ($this->comptype === Archive::COMPRESS_BZIP) { + $this->fh = @\bzopen($this->file, 'w'); + } else { + $this->fh = @\fopen($this->file, 'wb'); + } + if (!$this->fh) { + throw new ArchiveIOException('Could not open file for writing: ' . $this->file); + } + } + $this->writeaccess = \true; + $this->closed = \false; + } + /** + * Add a file to the current TAR archive using an existing file in the filesystem + * + * @param string $file path to the original file + * @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data, empty to take from original + * @throws ArchiveCorruptedException when the file changes while reading it, the archive will be corrupt and should be deleted + * @throws ArchiveIOException there was trouble reading the given file, it was not added + * @throws FileInfoException trouble reading file info, it was not added + */ + public function addFile($file, $fileinfo = '') + { + if (\is_string($fileinfo)) { + $fileinfo = FileInfo::fromPath($file, $fileinfo); + } + if ($this->closed) { + throw new ArchiveIOException('Archive has been closed, files can no longer be added'); + } + // create file header + $this->writeFileHeader($fileinfo); + // write data, but only if we have data to write. + // note: on Windows fopen() on a directory will fail, so we prevent + // errors on Windows by testing if we have data to write. + if (!$fileinfo->getIsdir() && $fileinfo->getSize() > 0) { + $read = 0; + $fp = @\fopen($file, 'rb'); + if (!$fp) { + throw new ArchiveIOException('Could not open file for reading: ' . $file); + } + while (!\feof($fp)) { + $data = \fread($fp, 512); + $read += \strlen($data); + if ($data === \false) { + break; + } + if ($data === '') { + break; + } + $packed = \pack("a512", $data); + $this->writebytes($packed); + } + \fclose($fp); + if ($read != $fileinfo->getSize()) { + $this->close(); + throw new ArchiveCorruptedException("The size of {$file} changed while reading, archive corrupted. read {$read} expected " . $fileinfo->getSize()); + } + } + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + } + /** + * Add a file to the current TAR archive using the given $data as content + * + * @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data + * @param string $data binary content of the file to add + * @throws ArchiveIOException + */ + public function addData($fileinfo, $data) + { + if (\is_string($fileinfo)) { + $fileinfo = new FileInfo($fileinfo); + } + if ($this->closed) { + throw new ArchiveIOException('Archive has been closed, files can no longer be added'); + } + $len = \strlen($data); + $fileinfo->setSize($len); + $this->writeFileHeader($fileinfo); + for ($s = 0; $s < $len; $s += 512) { + $this->writebytes(\pack("a512", \substr($data, $s, 512))); + } + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + } + /** + * Add the closing footer to the archive if in write mode, close all file handles + * + * After a call to this function no more data can be added to the archive, for + * read access no reading is allowed anymore + * + * "Physically, an archive consists of a series of file entries terminated by an end-of-archive entry, which + * consists of two 512 blocks of zero bytes" + * + * @link http://www.gnu.org/software/tar/manual/html_chapter/tar_8.html#SEC134 + * @throws ArchiveIOException + */ + public function close() + { + if ($this->closed) { + return; + } + // we did this already + // write footer + if ($this->writeaccess) { + $this->writebytes(\pack("a512", "")); + $this->writebytes(\pack("a512", "")); + } + // close file handles + if ($this->file) { + if ($this->comptype === Archive::COMPRESS_GZIP) { + \gzclose($this->fh); + } elseif ($this->comptype === Archive::COMPRESS_BZIP) { + \bzclose($this->fh); + } else { + \fclose($this->fh); + } + $this->file = ''; + $this->fh = 0; + } + $this->writeaccess = \false; + $this->closed = \true; + } + /** + * Returns the created in-memory archive data + * + * This implicitly calls close() on the Archive + * @throws ArchiveIOException + */ + public function getArchive() + { + $this->close(); + if ($this->comptype === Archive::COMPRESS_AUTO) { + $this->comptype = Archive::COMPRESS_NONE; + } + if ($this->comptype === Archive::COMPRESS_GZIP) { + return \gzencode($this->memory, $this->complevel); + } + if ($this->comptype === Archive::COMPRESS_BZIP) { + return \bzcompress($this->memory); + } + return $this->memory; + } + /** + * Save the created in-memory archive data + * + * Note: It more memory effective to specify the filename in the create() function and + * let the library work on the new file directly. + * + * @param string $file + * @throws ArchiveIOException + * @throws ArchiveIllegalCompressionException + */ + public function save($file) + { + if ($this->comptype === Archive::COMPRESS_AUTO) { + $this->setCompression($this->complevel, $this->filetype($file)); + } + if (!@\file_put_contents($file, $this->getArchive())) { + throw new ArchiveIOException('Could not write to file: ' . $file); + } + } + /** + * Read from the open file pointer + * + * @param int $length bytes to read + * @return string + */ + protected function readbytes($length) + { + if ($this->comptype === Archive::COMPRESS_GZIP) { + return @\gzread($this->fh, $length); + } elseif ($this->comptype === Archive::COMPRESS_BZIP) { + return @\bzread($this->fh, $length); + } else { + return @\fread($this->fh, $length); + } + } + /** + * Write to the open filepointer or memory + * + * @param string $data + * @throws ArchiveIOException + * @return int number of bytes written + */ + protected function writebytes($data) + { + if (!$this->file) { + $this->memory .= $data; + $written = \strlen($data); + } elseif ($this->comptype === Archive::COMPRESS_GZIP) { + $written = @\gzwrite($this->fh, $data); + } elseif ($this->comptype === Archive::COMPRESS_BZIP) { + $written = @\bzwrite($this->fh, $data); + } else { + $written = @\fwrite($this->fh, $data); + } + if ($written === \false) { + throw new ArchiveIOException('Failed to write to archive stream'); + } + return $written; + } + /** + * Skip forward in the open file pointer + * + * This is basically a wrapper around seek() (and a workaround for bzip2) + * + * @param int $bytes seek to this position + */ + protected function skipbytes($bytes) + { + if ($this->comptype === Archive::COMPRESS_GZIP) { + @\gzseek($this->fh, $bytes, \SEEK_CUR); + } elseif ($this->comptype === Archive::COMPRESS_BZIP) { + // there is no seek in bzip2, we simply read on + // bzread allows to read a max of 8kb at once + while ($bytes) { + $toread = \min(8192, $bytes); + @\bzread($this->fh, $toread); + $bytes -= $toread; + } + } else { + @\fseek($this->fh, $bytes, \SEEK_CUR); + } + } + /** + * Write the given file meta data as header + * + * @param FileInfo $fileinfo + * @throws ArchiveIOException + */ + protected function writeFileHeader(FileInfo $fileinfo) + { + $this->writeRawFileHeader($fileinfo->getPath(), $fileinfo->getUid(), $fileinfo->getGid(), $fileinfo->getMode(), $fileinfo->getSize(), $fileinfo->getMtime(), $fileinfo->getIsdir() ? '5' : '0'); + } + /** + * Write a file header to the stream + * + * @param string $name + * @param int $uid + * @param int $gid + * @param int $perm + * @param int $size + * @param int $mtime + * @param string $typeflag Set to '5' for directories + * @throws ArchiveIOException + */ + protected function writeRawFileHeader($name, $uid, $gid, $perm, $size, $mtime, $typeflag = '') + { + // handle filename length restrictions + $prefix = ''; + $namelen = \strlen($name); + if ($namelen > 100) { + $file = \basename($name); + $dir = \dirname($name); + if (\strlen($file) > 100 || \strlen($dir) > 155) { + // we're still too large, let's use GNU longlink + $this->writeRawFileHeader('././@LongLink', 0, 0, 0, $namelen, 0, 'L'); + for ($s = 0; $s < $namelen; $s += 512) { + $this->writebytes(\pack("a512", \substr($name, $s, 512))); + } + $name = \substr($name, 0, 100); + // cut off name + } else { + // we're fine when splitting, use POSIX ustar + $prefix = $dir; + $name = $file; + } + } + // values are needed in octal + $uid = \sprintf("%6s ", \decoct($uid)); + $gid = \sprintf("%6s ", \decoct($gid)); + $perm = \sprintf("%6s ", \decoct($perm)); + $size = \sprintf("%11s ", \decoct($size)); + $mtime = \sprintf("%11s", \decoct($mtime)); + $data_first = \pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime); + $data_last = \pack("a1a100a6a2a32a32a8a8a155a12", $typeflag, '', 'ustar', '', '', '', '', '', $prefix, ""); + for ($i = 0, $chks = 0; $i < 148; $i++) { + $chks += \ord($data_first[$i]); + } + for ($i = 156, $chks += 256, $j = 0; $i < 512; $i++, $j++) { + $chks += \ord($data_last[$j]); + } + $this->writebytes($data_first); + $chks = \pack("a8", \sprintf("%6s ", \decoct($chks))); + $this->writebytes($chks . $data_last); + } + /** + * Decode the given tar file header + * + * @param string $block a 512 byte block containing the header data + * @return array|false returns false when this was a null block + * @throws ArchiveCorruptedException + */ + protected function parseHeader($block) + { + if (!$block || \strlen($block) != 512) { + throw new ArchiveCorruptedException('Unexpected length of header'); + } + // null byte blocks are ignored + if (\trim($block) === '') { + return \false; + } + for ($i = 0, $chks = 0; $i < 148; $i++) { + $chks += \ord($block[$i]); + } + for ($i = 156, $chks += 256; $i < 512; $i++) { + $chks += \ord($block[$i]); + } + $header = @\unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $block); + if (!$header) { + throw new ArchiveCorruptedException('Failed to parse header'); + } + $return['checksum'] = \OctDec(\trim($header['checksum'])); + if ($return['checksum'] != $chks) { + throw new ArchiveCorruptedException('Header does not match its checksum'); + } + $return['filename'] = \trim($header['filename']); + $return['perm'] = \OctDec(\trim($header['perm'])); + $return['uid'] = \OctDec(\trim($header['uid'])); + $return['gid'] = \OctDec(\trim($header['gid'])); + $return['size'] = \OctDec(\trim($header['size'])); + $return['mtime'] = \OctDec(\trim($header['mtime'])); + $return['typeflag'] = $header['typeflag']; + $return['link'] = \trim($header['link']); + $return['uname'] = \trim($header['uname']); + $return['gname'] = \trim($header['gname']); + // Handle ustar Posix compliant path prefixes + if (\trim($header['prefix'])) { + $return['filename'] = \trim($header['prefix']) . '/' . $return['filename']; + } + // Handle Long-Link entries from GNU Tar + if ($return['typeflag'] == 'L') { + // following data block(s) is the filename + $filename = \trim($this->readbytes(\ceil($return['size'] / 512) * 512)); + // next block is the real header + $block = $this->readbytes(512); + $return = $this->parseHeader($block); + // overwrite the filename + $return['filename'] = $filename; + } + return $return; + } + /** + * Creates a FileInfo object from the given parsed header + * + * @param $header + * @return FileInfo + */ + protected function header2fileinfo($header) + { + $fileinfo = new FileInfo(); + $fileinfo->setPath($header['filename']); + $fileinfo->setMode($header['perm']); + $fileinfo->setUid($header['uid']); + $fileinfo->setGid($header['gid']); + $fileinfo->setSize($header['size']); + $fileinfo->setMtime($header['mtime']); + $fileinfo->setOwner($header['uname']); + $fileinfo->setGroup($header['gname']); + $fileinfo->setIsdir((bool) $header['typeflag']); + return $fileinfo; + } + /** + * Checks if the given compression type is available and throws an exception if not + * + * @param $comptype + * @throws ArchiveIllegalCompressionException + */ + protected function compressioncheck($comptype) + { + if ($comptype === Archive::COMPRESS_GZIP && !\function_exists('gzopen')) { + throw new ArchiveIllegalCompressionException('No gzip support available'); + } + if ($comptype === Archive::COMPRESS_BZIP && !\function_exists('bzopen')) { + throw new ArchiveIllegalCompressionException('No bzip2 support available'); + } + } + /** + * Guesses the wanted compression from the given file + * + * Uses magic bytes for existing files, the file extension otherwise + * + * You don't need to call this yourself. It's used when you pass Archive::COMPRESS_AUTO somewhere + * + * @param string $file + * @return int + */ + public function filetype($file) + { + // for existing files, try to read the magic bytes + if (\file_exists($file) && \is_readable($file) && \filesize($file) > 5) { + $fh = @\fopen($file, 'rb'); + if (!$fh) { + return \false; + } + $magic = \fread($fh, 5); + \fclose($fh); + if (\strpos($magic, "BZ") === 0) { + return Archive::COMPRESS_BZIP; + } + if (\strpos($magic, "\x1f\x8b") === 0) { + return Archive::COMPRESS_GZIP; + } + } + // otherwise rely on file name + $file = \strtolower($file); + if (\substr($file, -3) == '.gz' || \substr($file, -4) == '.tgz') { + return Archive::COMPRESS_GZIP; + } elseif (\substr($file, -4) == '.bz2' || \substr($file, -4) == '.tbz') { + return Archive::COMPRESS_BZIP; + } + return Archive::COMPRESS_NONE; + } +} diff --git a/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Zip.php b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Zip.php new file mode 100644 index 00000000..7640f9fc --- /dev/null +++ b/plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Zip.php @@ -0,0 +1,898 @@ + + * @package splitbrain\PHPArchive + * @license MIT + */ +class Zip extends Archive +{ + const LOCAL_FILE_HEADER_CRC_OFFSET = 14; + protected $file = ''; + protected $fh; + protected $memory = ''; + protected $closed = \true; + protected $writeaccess = \false; + protected $ctrl_dir; + protected $complevel = 9; + /** + * Set the compression level. + * + * Compression Type is ignored for ZIP + * + * You can call this function before adding each file to set differen compression levels + * for each file. + * + * @param int $level Compression level (0 to 9) + * @param int $type Type of compression to use ignored for ZIP + * @throws ArchiveIllegalCompressionException + */ + public function setCompression($level = 9, $type = Archive::COMPRESS_AUTO) + { + if ($level < -1 || $level > 9) { + throw new ArchiveIllegalCompressionException('Compression level should be between -1 and 9'); + } + $this->complevel = $level; + } + /** + * Open an existing ZIP file for reading + * + * @param string $file + * @throws ArchiveIOException + */ + public function open($file) + { + $this->file = $file; + $this->fh = @\fopen($this->file, 'rb'); + if (!$this->fh) { + throw new ArchiveIOException('Could not open file for reading: ' . $this->file); + } + $this->closed = \false; + } + /** + * Read the contents of a ZIP archive + * + * This function lists the files stored in the archive, and returns an indexed array of FileInfo objects + * + * The archive is closed afer reading the contents, for API compatibility with TAR files + * Reopen the file with open() again if you want to do additional operations + * + * @throws ArchiveIOException + * @return FileInfo[] + */ + public function contents() + { + $result = array(); + foreach ($this->yieldContents() as $fileinfo) { + $result[] = $fileinfo; + } + return $result; + } + /** + * Read the contents of a ZIP archive and return each entry using yield + * for memory efficiency. + * + * @see contents() + * @throws ArchiveIOException + * @return FileInfo[] + */ + public function yieldContents() + { + if ($this->closed || !$this->file) { + throw new ArchiveIOException('Can not read from a closed archive'); + } + $centd = $this->readCentralDir(); + @\rewind($this->fh); + @\fseek($this->fh, $centd['offset']); + for ($i = 0; $i < $centd['entries']; $i++) { + (yield $this->header2fileinfo($this->readCentralFileHeader())); + } + $this->close(); + } + /** + * Extract an existing ZIP archive + * + * The $strip parameter allows you to strip a certain number of path components from the filenames + * found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when + * an integer is passed as $strip. + * Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix, + * the prefix will be stripped. It is recommended to give prefixes with a trailing slash. + * + * By default this will extract all files found in the archive. You can restrict the output using the $include + * and $exclude parameter. Both expect a full regular expression (including delimiters and modifiers). If + * $include is set only files that match this expression will be extracted. Files that match the $exclude + * expression will never be extracted. Both parameters can be used in combination. Expressions are matched against + * stripped filenames as described above. + * + * @param string $outdir the target directory for extracting + * @param int|string $strip either the number of path components or a fixed prefix to strip + * @param string $exclude a regular expression of files to exclude + * @param string $include a regular expression of files to include + * @throws ArchiveIOException + * @return FileInfo[] + */ + public function extract($outdir, $strip = '', $exclude = '', $include = '') + { + if ($this->closed || !$this->file) { + throw new ArchiveIOException('Can not read from a closed archive'); + } + $outdir = \rtrim($outdir, '/'); + @\mkdir($outdir, 0777, \true); + $extracted = array(); + $cdir = $this->readCentralDir(); + $pos_entry = $cdir['offset']; + // begin of the central file directory + for ($i = 0; $i < $cdir['entries']; $i++) { + // read file header + @\fseek($this->fh, $pos_entry); + $header = $this->readCentralFileHeader(); + $header['index'] = $i; + $pos_entry = \ftell($this->fh); + // position of the next file in central file directory + \fseek($this->fh, $header['offset']); + // seek to beginning of file header + $header = $this->readFileHeader($header); + $fileinfo = $this->header2fileinfo($header); + // apply strip rules + $fileinfo->strip($strip); + // skip unwanted files + if (!\strlen($fileinfo->getPath()) || !$fileinfo->matchExpression($include, $exclude)) { + continue; + } + $extracted[] = $fileinfo; + // create output directory + $output = $outdir . '/' . $fileinfo->getPath(); + $directory = $header['folder'] ? $output : \dirname($output); + @\mkdir($directory, 0777, \true); + // nothing more to do for directories + if ($fileinfo->getIsdir()) { + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + continue; + } + // compressed files are written to temporary .gz file first + if ($header['compression'] == 0) { + $extractto = $output; + } else { + $extractto = $output . '.gz'; + } + // open file for writing + $fp = @\fopen($extractto, "wb"); + if (!$fp) { + throw new ArchiveIOException('Could not open file for writing: ' . $extractto); + } + // prepend compression header + if ($header['compression'] != 0) { + $binary_data = \pack('va1a1Va1a1', 0x8b1f, \chr($header['compression']), \chr(0x0), \time(), \chr(0x0), \chr(3)); + \fwrite($fp, $binary_data, 10); + } + // read the file and store it on disk + $size = $header['compressed_size']; + while ($size != 0) { + $read_size = $size < 2048 ? $size : 2048; + $buffer = \fread($this->fh, $read_size); + $binary_data = \pack('a' . $read_size, $buffer); + \fwrite($fp, $binary_data, $read_size); + $size -= $read_size; + } + // finalize compressed file + if ($header['compression'] != 0) { + $binary_data = \pack('VV', $header['crc'], $header['size']); + \fwrite($fp, $binary_data, 8); + } + // close file + \fclose($fp); + // unpack compressed file + if ($header['compression'] != 0) { + $gzp = @\gzopen($extractto, 'rb'); + if (!$gzp) { + @\unlink($extractto); + throw new ArchiveIOException('Failed file extracting. gzip support missing?'); + } + $fp = @\fopen($output, 'wb'); + if (!$fp) { + throw new ArchiveIOException('Could not open file for writing: ' . $extractto); + } + $size = $header['size']; + while ($size != 0) { + $read_size = $size < 2048 ? $size : 2048; + $buffer = \gzread($gzp, $read_size); + $binary_data = \pack('a' . $read_size, $buffer); + @\fwrite($fp, $binary_data, $read_size); + $size -= $read_size; + } + \fclose($fp); + \gzclose($gzp); + \unlink($extractto); + // remove temporary gz file + } + @\touch($output, $fileinfo->getMtime()); + //FIXME what about permissions? + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + } + $this->close(); + return $extracted; + } + /** + * Create a new ZIP file + * + * If $file is empty, the zip file will be created in memory + * + * @param string $file + * @throws ArchiveIOException + */ + public function create($file = '') + { + $this->file = $file; + $this->memory = ''; + $this->fh = 0; + if ($this->file) { + $this->fh = @\fopen($this->file, 'wb'); + if (!$this->fh) { + throw new ArchiveIOException('Could not open file for writing: ' . $this->file); + } + } + $this->writeaccess = \true; + $this->closed = \false; + $this->ctrl_dir = array(); + } + /** + * Add a file to the current ZIP archive using an existing file in the filesystem + * + * @param string $file path to the original file + * @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data, empty to take from original + * @throws ArchiveIOException + */ + /** + * Add a file to the current archive using an existing file in the filesystem + * + * @param string $file path to the original file + * @param string|FileInfo $fileinfo either the name to use in archive (string) or a FileInfo oject with all meta data, empty to take from original + * @throws ArchiveIOException + * @throws FileInfoException + */ + public function addFile($file, $fileinfo = '') + { + if (\is_string($fileinfo)) { + $fileinfo = FileInfo::fromPath($file, $fileinfo); + } + if ($this->closed) { + throw new ArchiveIOException('Archive has been closed, files can no longer be added'); + } + $fp = @\fopen($file, 'rb'); + if ($fp === \false) { + throw new ArchiveIOException('Could not open file for reading: ' . $file); + } + $offset = $this->dataOffset(); + $name = $fileinfo->getPath(); + $time = $fileinfo->getMtime(); + // write local file header (temporary CRC and size) + $this->writebytes($this->makeLocalFileHeader($time, 0, 0, 0, $name, (bool) $this->complevel)); + // we store no encryption header + // prepare info, compress and write data to archive + $deflate_context = \deflate_init(\ZLIB_ENCODING_DEFLATE, ['level' => $this->complevel]); + $crc_context = \hash_init('crc32b'); + $size = $csize = 0; + while (!\feof($fp)) { + $block = \fread($fp, 512); + if ($this->complevel) { + $is_first_block = $size === 0; + $is_last_block = \feof($fp); + if ($is_last_block) { + $c_block = \deflate_add($deflate_context, $block, \ZLIB_FINISH); + // get rid of the compression footer + $c_block = \substr($c_block, 0, -4); + } else { + $c_block = \deflate_add($deflate_context, $block, \ZLIB_NO_FLUSH); + } + // get rid of the compression header + if ($is_first_block) { + $c_block = \substr($c_block, 2); + } + $csize += \strlen($c_block); + $this->writebytes($c_block); + } else { + $this->writebytes($block); + } + $size += \strlen($block); + \hash_update($crc_context, $block); + } + \fclose($fp); + // update the local file header with the computed CRC and size + $crc = \hexdec(\hash_final($crc_context)); + $csize = $this->complevel ? $csize : $size; + $this->writebytesAt($this->makeCrcAndSize($crc, $size, $csize), $offset + self::LOCAL_FILE_HEADER_CRC_OFFSET); + // we store no data descriptor + // add info to central file directory + $this->ctrl_dir[] = $this->makeCentralFileRecord($offset, $time, $crc, $size, $csize, $name, (bool) $this->complevel); + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + } + /** + * Add a file to the current Zip archive using the given $data as content + * + * @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data + * @param string $data binary content of the file to add + * @throws ArchiveIOException + */ + public function addData($fileinfo, $data) + { + if (\is_string($fileinfo)) { + $fileinfo = new FileInfo($fileinfo); + } + if ($this->closed) { + throw new ArchiveIOException('Archive has been closed, files can no longer be added'); + } + // prepare info and compress data + $size = \strlen($data); + $crc = \crc32($data); + if ($this->complevel) { + $data = \gzcompress($data, $this->complevel); + $data = \substr($data, 2, -4); + // strip compression headers + } + $csize = \strlen($data); + $offset = $this->dataOffset(); + $name = $fileinfo->getPath(); + $time = $fileinfo->getMtime(); + // write local file header + $this->writebytes($this->makeLocalFileHeader($time, $crc, $size, $csize, $name, (bool) $this->complevel)); + // we store no encryption header + // write data + $this->writebytes($data); + // we store no data descriptor + // add info to central file directory + $this->ctrl_dir[] = $this->makeCentralFileRecord($offset, $time, $crc, $size, $csize, $name, (bool) $this->complevel); + if (\is_callable($this->callback)) { + \call_user_func($this->callback, $fileinfo); + } + } + /** + * Add the closing footer to the archive if in write mode, close all file handles + * + * After a call to this function no more data can be added to the archive, for + * read access no reading is allowed anymore + * @throws ArchiveIOException + */ + public function close() + { + if ($this->closed) { + return; + } + // we did this already + if ($this->writeaccess) { + // write central directory + $offset = $this->dataOffset(); + $ctrldir = \join('', $this->ctrl_dir); + $this->writebytes($ctrldir); + // write end of central directory record + $this->writebytes("PK\x05\x06"); + // end of central dir signature + $this->writebytes(\pack('v', 0)); + // number of this disk + $this->writebytes(\pack('v', 0)); + // number of the disk with the start of the central directory + $this->writebytes(\pack('v', \count($this->ctrl_dir))); + // total number of entries in the central directory on this disk + $this->writebytes(\pack('v', \count($this->ctrl_dir))); + // total number of entries in the central directory + $this->writebytes(\pack('V', \strlen($ctrldir))); + // size of the central directory + $this->writebytes(\pack('V', $offset)); + // offset of start of central directory with respect to the starting disk number + $this->writebytes(\pack('v', 0)); + // .ZIP file comment length + $this->ctrl_dir = array(); + } + // close file handles + if ($this->file) { + \fclose($this->fh); + $this->file = ''; + $this->fh = 0; + } + $this->writeaccess = \false; + $this->closed = \true; + } + /** + * Returns the created in-memory archive data + * + * This implicitly calls close() on the Archive + * @throws ArchiveIOException + */ + public function getArchive() + { + $this->close(); + return $this->memory; + } + /** + * Save the created in-memory archive data + * + * Note: It's more memory effective to specify the filename in the create() function and + * let the library work on the new file directly. + * + * @param $file + * @throws ArchiveIOException + */ + public function save($file) + { + if (!@\file_put_contents($file, $this->getArchive())) { + throw new ArchiveIOException('Could not write to file: ' . $file); + } + } + /** + * Read the central directory + * + * This key-value list contains general information about the ZIP file + * + * @return array + */ + protected function readCentralDir() + { + $size = \filesize($this->file); + if ($size < 277) { + $maximum_size = $size; + } else { + $maximum_size = 277; + } + @\fseek($this->fh, $size - $maximum_size); + $pos = \ftell($this->fh); + $bytes = 0x0; + while ($pos < $size) { + $byte = @\fread($this->fh, 1); + $bytes = $bytes << 8 & 0xffffffff | \ord($byte); + if ($bytes == 0x504b0506) { + break; + } + $pos++; + } + $data = \unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', \fread($this->fh, 18)); + if ($data['comment_size'] != 0) { + $centd['comment'] = \fread($this->fh, $data['comment_size']); + } else { + $centd['comment'] = ''; + } + $centd['entries'] = $data['entries']; + $centd['disk_entries'] = $data['disk_entries']; + $centd['offset'] = $data['offset']; + $centd['disk_start'] = $data['disk_start']; + $centd['size'] = $data['size']; + $centd['disk'] = $data['disk']; + return $centd; + } + /** + * Read the next central file header + * + * Assumes the current file pointer is pointing at the right position + * + * @return array + */ + protected function readCentralFileHeader() + { + $binary_data = \fread($this->fh, 46); + $header = \unpack('vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data); + if ($header['filename_len'] != 0) { + $header['filename'] = \fread($this->fh, $header['filename_len']); + } else { + $header['filename'] = ''; + } + if ($header['extra_len'] != 0) { + $header['extra'] = \fread($this->fh, $header['extra_len']); + $header['extradata'] = $this->parseExtra($header['extra']); + } else { + $header['extra'] = ''; + $header['extradata'] = array(); + } + if ($header['comment_len'] != 0) { + $header['comment'] = \fread($this->fh, $header['comment_len']); + } else { + $header['comment'] = ''; + } + $header['mtime'] = $this->makeUnixTime($header['mdate'], $header['mtime']); + $header['stored_filename'] = $header['filename']; + $header['status'] = 'ok'; + if (\substr($header['filename'], -1) == '/') { + $header['external'] = 0x41ff0010; + } + $header['folder'] = $header['external'] == 0x41ff0010 || $header['external'] == 16 ? 1 : 0; + return $header; + } + /** + * Reads the local file header + * + * This header precedes each individual file inside the zip file. Assumes the current file pointer is pointing at + * the right position already. Enhances the given central header with the data found at the local header. + * + * @param array $header the central file header read previously (see above) + * @return array + */ + protected function readFileHeader($header) + { + $binary_data = \fread($this->fh, 30); + $data = \unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data); + $header['filename'] = \fread($this->fh, $data['filename_len']); + if ($data['extra_len'] != 0) { + $header['extra'] = \fread($this->fh, $data['extra_len']); + $header['extradata'] = \array_merge($header['extradata'], $this->parseExtra($header['extra'])); + } else { + $header['extra'] = ''; + $header['extradata'] = array(); + } + $header['compression'] = $data['compression']; + foreach (array('size', 'compressed_size', 'crc') as $hd) { + // On ODT files, these headers are 0. Keep the previous value. + if ($data[$hd] != 0) { + $header[$hd] = $data[$hd]; + } + } + $header['flag'] = $data['flag']; + $header['mtime'] = $this->makeUnixTime($data['mdate'], $data['mtime']); + $header['stored_filename'] = $header['filename']; + $header['status'] = "ok"; + $header['folder'] = $header['external'] == 0x41ff0010 || $header['external'] == 16 ? 1 : 0; + return $header; + } + /** + * Parse the extra headers into fields + * + * @param string $header + * @return array + */ + protected function parseExtra($header) + { + $extra = array(); + // parse all extra fields as raw values + while (\strlen($header) !== 0) { + $set = \unpack('vid/vlen', $header); + $header = \substr($header, 4); + $value = \substr($header, 0, $set['len']); + $header = \substr($header, $set['len']); + $extra[$set['id']] = $value; + } + // handle known ones + if (isset($extra[0x6375])) { + $extra['utf8comment'] = \substr($extra[0x7075], 5); + // strip version and crc + } + if (isset($extra[0x7075])) { + $extra['utf8path'] = \substr($extra[0x7075], 5); + // strip version and crc + } + return $extra; + } + /** + * Create fileinfo object from header data + * + * @param $header + * @return FileInfo + */ + protected function header2fileinfo($header) + { + $fileinfo = new FileInfo(); + $fileinfo->setSize($header['size']); + $fileinfo->setCompressedSize($header['compressed_size']); + $fileinfo->setMtime($header['mtime']); + $fileinfo->setComment($header['comment']); + $fileinfo->setIsdir($header['external'] == 0x41ff0010 || $header['external'] == 16); + if (isset($header['extradata']['utf8path'])) { + $fileinfo->setPath($header['extradata']['utf8path']); + } else { + $fileinfo->setPath($this->cpToUtf8($header['filename'])); + } + if (isset($header['extradata']['utf8comment'])) { + $fileinfo->setComment($header['extradata']['utf8comment']); + } else { + $fileinfo->setComment($this->cpToUtf8($header['comment'])); + } + return $fileinfo; + } + /** + * Convert the given CP437 encoded string to UTF-8 + * + * Tries iconv with the correct encoding first, falls back to mbstring with CP850 which is + * similar enough. CP437 seems not to be available in mbstring. Lastly falls back to keeping the + * string as is, which is still better than nothing. + * + * On some systems iconv is available, but the codepage is not. We also check for that. + * + * @param $string + * @return string + */ + protected function cpToUtf8($string) + { + if (\function_exists('iconv') && @\iconv_strlen('', 'CP437') !== \false) { + return \iconv('CP437', 'UTF-8', $string); + } elseif (\function_exists('mb_convert_encoding')) { + return \mb_convert_encoding($string, 'UTF-8', 'CP850'); + } else { + return $string; + } + } + /** + * Convert the given UTF-8 encoded string to CP437 + * + * Same caveats as for cpToUtf8() apply + * + * @param $string + * @return string + */ + protected function utf8ToCp($string) + { + // try iconv first + if (\function_exists('iconv')) { + $conv = @\iconv('UTF-8', 'CP437//IGNORE', $string); + if ($conv) { + return $conv; + } + // it worked + } + // still here? iconv failed to convert the string. Try another method + // see http://php.net/manual/en/function.iconv.php#108643 + if (\function_exists('mb_convert_encoding')) { + return \mb_convert_encoding($string, 'CP850', 'UTF-8'); + } else { + return $string; + } + } + /** + * Write to the open filepointer or memory + * + * @param string $data + * @throws ArchiveIOException + * @return int number of bytes written + */ + protected function writebytes($data) + { + if (!$this->file) { + $this->memory .= $data; + $written = \strlen($data); + } else { + $written = @\fwrite($this->fh, $data); + } + if ($written === \false) { + throw new ArchiveIOException('Failed to write to archive stream'); + } + return $written; + } + /** + * Write to the open filepointer or memory at the specified offset + * + * @param string $data + * @param int $offset + * @throws ArchiveIOException + * @return int number of bytes written + */ + protected function writebytesAt($data, $offset) + { + if (!$this->file) { + $this->memory .= \substr_replace($this->memory, $data, $offset); + $written = \strlen($data); + } else { + @\fseek($this->fh, $offset); + $written = @\fwrite($this->fh, $data); + @\fseek($this->fh, 0, \SEEK_END); + } + if ($written === \false) { + throw new ArchiveIOException('Failed to write to archive stream'); + } + return $written; + } + /** + * Current data pointer position + * + * @fixme might need a -1 + * @return int + */ + protected function dataOffset() + { + if ($this->file) { + return \ftell($this->fh); + } else { + return \strlen($this->memory); + } + } + /** + * Create a DOS timestamp from a UNIX timestamp + * + * DOS timestamps start at 1980-01-01, earlier UNIX stamps will be set to this date + * + * @param $time + * @return int + */ + protected function makeDosTime($time) + { + $timearray = \getdate($time); + if ($timearray['year'] < 1980) { + $timearray['year'] = 1980; + $timearray['mon'] = 1; + $timearray['mday'] = 1; + $timearray['hours'] = 0; + $timearray['minutes'] = 0; + $timearray['seconds'] = 0; + } + return $timearray['year'] - 1980 << 25 | $timearray['mon'] << 21 | $timearray['mday'] << 16 | $timearray['hours'] << 11 | $timearray['minutes'] << 5 | $timearray['seconds'] >> 1; + } + /** + * Create a UNIX timestamp from a DOS timestamp + * + * @param $mdate + * @param $mtime + * @return int + */ + protected function makeUnixTime($mdate = null, $mtime = null) + { + if ($mdate && $mtime) { + $year = (($mdate & 0xfe00) >> 9) + 1980; + $month = ($mdate & 0x1e0) >> 5; + $day = $mdate & 0x1f; + $hour = ($mtime & 0xf800) >> 11; + $minute = ($mtime & 0x7e0) >> 5; + $seconde = ($mtime & 0x1f) << 1; + $mtime = \mktime($hour, $minute, $seconde, $month, $day, $year); + } else { + $mtime = \time(); + } + return $mtime; + } + /** + * Returns a local file header for the given data + * + * @param int $offset location of the local header + * @param int $ts unix timestamp + * @param int $crc CRC32 checksum of the uncompressed data + * @param int $len length of the uncompressed data + * @param int $clen length of the compressed data + * @param string $name file name + * @param boolean|null $comp if compression is used, if null it's determined from $len != $clen + * @return string + */ + protected function makeCentralFileRecord($offset, $ts, $crc, $len, $clen, $name, $comp = null) + { + if (\is_null($comp)) { + $comp = $len != $clen; + } + $comp = $comp ? 8 : 0; + $dtime = \dechex($this->makeDosTime($ts)); + list($name, $extra) = $this->encodeFilename($name); + $header = "PK\x01\x02"; + // central file header signature + $header .= \pack('v', 14); + // version made by - VFAT + $header .= \pack('v', 20); + // version needed to extract - 2.0 + $header .= \pack('v', 0); + // general purpose flag - no flags set + $header .= \pack('v', $comp); + // compression method - deflate|none + $header .= \pack('H*', $dtime[6] . $dtime[7] . $dtime[4] . $dtime[5] . $dtime[2] . $dtime[3] . $dtime[0] . $dtime[1]); + // last mod file time and date + $header .= \pack('V', $crc); + // crc-32 + $header .= \pack('V', $clen); + // compressed size + $header .= \pack('V', $len); + // uncompressed size + $header .= \pack('v', \strlen($name)); + // file name length + $header .= \pack('v', \strlen($extra)); + // extra field length + $header .= \pack('v', 0); + // file comment length + $header .= \pack('v', 0); + // disk number start + $header .= \pack('v', 0); + // internal file attributes + $header .= \pack('V', 0); + // external file attributes @todo was 0x32!? + $header .= \pack('V', $offset); + // relative offset of local header + $header .= $name; + // file name + $header .= $extra; + // extra (utf-8 filename) + return $header; + } + /** + * Returns a local file header for the given data + * + * @param int $ts unix timestamp + * @param int $crc CRC32 checksum of the uncompressed data + * @param int $len length of the uncompressed data + * @param int $clen length of the compressed data + * @param string $name file name + * @param boolean|null $comp if compression is used, if null it's determined from $len != $clen + * @return string + */ + protected function makeLocalFileHeader($ts, $crc, $len, $clen, $name, $comp = null) + { + if (\is_null($comp)) { + $comp = $len != $clen; + } + $comp = $comp ? 8 : 0; + $dtime = \dechex($this->makeDosTime($ts)); + list($name, $extra) = $this->encodeFilename($name); + $header = "PK\x03\x04"; + // local file header signature + $header .= \pack('v', 20); + // version needed to extract - 2.0 + $header .= \pack('v', 0); + // general purpose flag - no flags set + $header .= \pack('v', $comp); + // compression method - deflate|none + $header .= \pack('H*', $dtime[6] . $dtime[7] . $dtime[4] . $dtime[5] . $dtime[2] . $dtime[3] . $dtime[0] . $dtime[1]); + // last mod file time and date + $header .= \pack('V', $crc); + // crc-32 + $header .= \pack('V', $clen); + // compressed size + $header .= \pack('V', $len); + // uncompressed size + $header .= \pack('v', \strlen($name)); + // file name length + $header .= \pack('v', \strlen($extra)); + // extra field length + $header .= $name; + // file name + $header .= $extra; + // extra (utf-8 filename) + return $header; + } + /** + * Returns only a part of the local file header containing the CRC, size and compressed size. + * Used to update these fields for an already written header. + * + * @param int $crc CRC32 checksum of the uncompressed data + * @param int $len length of the uncompressed data + * @param int $clen length of the compressed data + * @return string + */ + protected function makeCrcAndSize($crc, $len, $clen) + { + $header = \pack('V', $crc); + // crc-32 + $header .= \pack('V', $clen); + // compressed size + $header .= \pack('V', $len); + // uncompressed size + return $header; + } + /** + * Returns an allowed filename and an extra field header + * + * When encoding stuff outside the 7bit ASCII range it needs to be placed in a separate + * extra field + * + * @param $original + * @return array($filename, $extra) + */ + protected function encodeFilename($original) + { + $cp437 = $this->utf8ToCp($original); + if ($cp437 === $original) { + return array($original, ''); + } + $extra = \pack( + 'vvCV', + 0x7075, + // tag + \strlen($original) + 5, + // length of file + version + crc + 1, + // version + \crc32($original) + ); + $extra .= $original; + return array($cp437, $extra); + } +} diff --git a/templates/joomla-italia-theme/html/com_contact/category/default.php b/templates/joomla-italia-theme/html/com_contact/category/default.php index 2b878e23..f266b524 100644 --- a/templates/joomla-italia-theme/html/com_contact/category/default.php +++ b/templates/joomla-italia-theme/html/com_contact/category/default.php @@ -17,7 +17,7 @@ use Joomla\CMS\Layout\LayoutHelper;
-
+
subtemplatename = 'items'; diff --git a/templates/joomla-italia-theme/html/com_contact/contact/default.php b/templates/joomla-italia-theme/html/com_contact/contact/default.php index e145d8a5..f6bb4ab5 100644 --- a/templates/joomla-italia-theme/html/com_contact/contact/default.php +++ b/templates/joomla-italia-theme/html/com_contact/contact/default.php @@ -32,7 +32,7 @@ $htag2 = ($tparams->get('show_page_heading') && $tparams->get('show_name')) ?
-
+
get('show_page_heading')) : ?> diff --git a/templates/joomla-italia-theme/html/com_content/article/default.php b/templates/joomla-italia-theme/html/com_content/article/default.php index 394a1385..bba7fa14 100644 --- a/templates/joomla-italia-theme/html/com_content/article/default.php +++ b/templates/joomla-italia-theme/html/com_content/article/default.php @@ -77,7 +77,7 @@ $baseImagePath = Uri::root(false) . "media/templates/site/joomla-italia-theme/im
-
+

escape($this->item->title); ?>

@@ -162,6 +162,7 @@ $baseImagePath = Uri::root(false) . "media/templates/site/joomla-italia-theme/im item->event->afterDisplayContent; ?> +