aboutsummaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rw-r--r--library/cacert.pem650
-rw-r--r--library/certs/cacert.pem650
-rw-r--r--library/jquery_ac/friendica.complete.js4
-rw-r--r--library/jsonld/LICENSE30
-rw-r--r--library/jsonld/README.md193
-rw-r--r--library/jsonld/composer.json29
-rw-r--r--library/jsonld/jsonld.php6038
-rw-r--r--library/jsonld/test.php765
-rw-r--r--library/sticky-kit/sticky-kit.js2
9 files changed, 7798 insertions, 563 deletions
diff --git a/library/cacert.pem b/library/cacert.pem
index 29dbfa286..55958581d 100644
--- a/library/cacert.pem
+++ b/library/cacert.pem
@@ -1,20 +1,20 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Wed Apr 20 03:12:05 2016
+## Certificate data from Mozilla as of: Wed Jun 7 03:12:05 2017 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:
-## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
+## 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.25.
-## SHA1: 5df367cda83086392e1acdf22bfef00c48d5eba6
+## Conversion done with mk-ca-bundle.pl version 1.27.
+## SHA256: 93753268e1c596aee21893fb1c6975338389132f15c942ed65fc394a904371d7
##
@@ -252,27 +252,6 @@ W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----
-RSA Security 2048 v3
-====================
------BEGIN CERTIFICATE-----
-MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
-ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
-MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
-BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
-Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
-WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
-KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
-+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
-MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
-FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
-v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
-0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
-VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
-nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
-pKnXwiJPZ9d37CAFYd4=
------END CERTIFICATE-----
-
GeoTrust Global CA
==================
-----BEGIN CERTIFICATE-----
@@ -1241,33 +1220,6 @@ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----
-WellsSecure Public Root Certificate Authority
-=============================================
------BEGIN CERTIFICATE-----
-MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
-F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
-NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
-MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
-bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
-VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
-iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
-i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
-bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
-K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
-AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
-cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
-lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
-i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
-GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
-Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
-K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
-bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
-qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
-E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
-tylv2G0xffX8oRAHh84vWdw+WNs=
------END CERTIFICATE-----
-
COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@@ -1285,30 +1237,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----
-IGC/A
-=====
------BEGIN CERTIFICATE-----
-MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
-VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
-Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
-MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
-EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
-STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
-IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
-TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
-So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
-HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
-frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
-tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
-egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
-iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
-q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
-MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
-Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
-lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
-0mBWWg==
------END CERTIFICATE-----
-
Security Communication EV RootCA1
=================================
-----BEGIN CERTIFICATE-----
@@ -1353,46 +1281,6 @@ hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
-----END CERTIFICATE-----
-Microsec e-Szigno Root CA
-=========================
------BEGIN CERTIFICATE-----
-MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
-BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
-EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
-MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
-dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
-GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
-AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
-d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
-oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
-QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
-PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
-MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
-IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
-VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
-LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
-dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
-AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
-4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
-AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
-egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
-Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
-PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
-c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
-cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
-IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
-WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
-MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
-MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
-Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
-HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
-nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
-aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
-86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
-yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
-S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
------END CERTIFICATE-----
-
Certigna
========
-----BEGIN CERTIFICATE-----
@@ -1518,58 +1406,6 @@ LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
-----END CERTIFICATE-----
-Buypass Class 2 CA 1
-====================
------BEGIN CERTIFICATE-----
-MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
-QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
-MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
-c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
-cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
-0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
-0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
-uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
-MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
-AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
-1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
-7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
-fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
-wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
------END CERTIFICATE-----
-
-EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
-==========================================================================
------BEGIN CERTIFICATE-----
-MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
-QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
-Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
-ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
-IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
-X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
-gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
-eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
-TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
-Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
-uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
-qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
-ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
-Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
-/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
-Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
-FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
-zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
-XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
-bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
-RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
-1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
-2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
-Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
-AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
------END CERTIFICATE-----
-
certSIGN ROOT CA
================
-----BEGIN CERTIFICATE-----
@@ -1611,28 +1447,6 @@ G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
mxE=
-----END CERTIFICATE-----
-ApplicationCA - Japanese Government
-===================================
------BEGIN CERTIFICATE-----
-MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
-SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
-MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
-cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
-fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
-wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
-jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
-nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
-WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
-BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
-vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
-o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
-/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
-io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
-dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
-rosot4LKGAfmt1t06SAZf7IbiVQ=
------END CERTIFICATE-----
-
GeoTrust Primary Certification Authority - G3
=============================================
-----BEGIN CERTIFICATE-----
@@ -1764,7 +1578,7 @@ AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
-----END CERTIFICATE-----
NetLock Arany (Class Gold) Főtanúsítvány
-============================================
+========================================
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
@@ -1819,34 +1633,6 @@ IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
66+KAQ==
-----END CERTIFICATE-----
-Juur-SK
-=======
------BEGIN CERTIFICATE-----
-MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
-c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
-DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
-SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
-aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
-TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
-+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
-UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
-Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
-MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
-HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
-AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
-cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
-AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
-cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
-FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
-A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
-ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
-abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
-IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
-Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
-yyqcjg==
------END CERTIFICATE-----
-
Hongkong Post Root CA 1
=======================
-----BEGIN CERTIFICATE-----
@@ -2280,7 +2066,7 @@ Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
-----END CERTIFICATE-----
Certinomis - Autorité Racine
-=============================
+============================
-----BEGIN CERTIFICATE-----
MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
@@ -2310,41 +2096,6 @@ wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
vgt2Fl43N+bYdJeimUV5
-----END CERTIFICATE-----
-Root CA Generalitat Valenciana
-==============================
------BEGIN CERTIFICATE-----
-MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
-ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
-IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
-WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
-CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
-F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
-ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
-D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
-JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
-AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
-dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
-ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
-AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
-YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
-AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
-aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
-AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
-YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
-AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
-OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
-dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
-BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
-A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
-b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
-TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
-Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
-NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
-iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
-+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
------END CERTIFICATE-----
-
TWCA Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
@@ -3675,7 +3426,7 @@ ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
-----END CERTIFICATE-----
TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5
-=========================================================
+====================================================
-----BEGIN CERTIFICATE-----
MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN
BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
@@ -3698,30 +3449,6 @@ lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
-----END CERTIFICATE-----
-TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6
-=========================================================
------BEGIN CERTIFICATE-----
-MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G
-A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
-acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5
-MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL
-BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf
-aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm
-aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a
-2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED
-wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb
-HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV
-+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT
-9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
-9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R
-fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy
-o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW
-hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1
-O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw==
------END CERTIFICATE-----
-
Certinomis - Root CA
====================
-----BEGIN CERTIFICATE-----
@@ -3863,6 +3590,369 @@ 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-----
+
+Certplus Root CA G1
+===================
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV
+BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe
+Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD
+ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN
+r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx
+Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj
+BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv
+LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2
+z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc
+4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd
+4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj
+jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+
+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G
+A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY
+lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
+66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG
+YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/
+2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F
+6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX
+CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe
+tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC
+VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/
++mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+
+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
+-----END CERTIFICATE-----
+
+Certplus Root CA G2
+===================
+-----BEGIN CERTIFICATE-----
+MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT
+AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x
+NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0
+cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA
+BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN
+Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud
+IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV
+HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl
+vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw==
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G1
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx
+MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa
+Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87
+ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO
+YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9
+xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO
+9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq
+3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi
+n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9
+URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr
+TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px
+N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
+PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv
+uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK
+n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh
+X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80
+nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm
+GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/
+bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o
+4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA
+OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G2
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy
+MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+
+Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz
+4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV
+eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt
+UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz
+3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj
+3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz
+9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0
+0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT
+y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59
+M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
+Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI
+mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG
+S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp
+EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ
+6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr
+gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo
+SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0
+YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm
+u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G3
+====================
+-----BEGIN CERTIFICATE-----
+MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT
+AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X
+DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w
+ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B
+ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB
+/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf
+BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM
+BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta
+3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB
+-----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-----
+
+LuxTrust Global Root 2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG
+A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh
+bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW
+MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC
+AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm
+Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2
+xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC
+wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm
+1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm
+FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF
+wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/
+a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U
+ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ
+MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB
+/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5
+Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT
++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ
+FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN
+H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW
+7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu
+ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA
+VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR
+TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt
+/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc
+7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I
+iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
+-----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-----
-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
diff --git a/library/certs/cacert.pem b/library/certs/cacert.pem
index c15368bdc..8f1357b66 100644
--- a/library/certs/cacert.pem
+++ b/library/certs/cacert.pem
@@ -1,20 +1,20 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Wed Apr 20 03:12:05 2016
+## Certificate data from Mozilla as of: Wed Jun 7 03:12:05 2017 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:
-## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
+## 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.25.
-## SHA1: 5df367cda83086392e1acdf22bfef00c48d5eba6
+## Conversion done with mk-ca-bundle.pl version 1.27.
+## SHA256: 93753268e1c596aee21893fb1c6975338389132f15c942ed65fc394a904371d7
##
@@ -252,27 +252,6 @@ W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----
-RSA Security 2048 v3
-====================
------BEGIN CERTIFICATE-----
-MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
-ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
-MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
-BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
-Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
-WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
-KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
-+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
-MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
-FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
-v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
-0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
-VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
-nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
-pKnXwiJPZ9d37CAFYd4=
------END CERTIFICATE-----
-
GeoTrust Global CA
==================
-----BEGIN CERTIFICATE-----
@@ -1241,33 +1220,6 @@ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----
-WellsSecure Public Root Certificate Authority
-=============================================
------BEGIN CERTIFICATE-----
-MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
-F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
-NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
-MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
-bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
-VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
-iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
-i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
-bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
-K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
-AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
-cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
-lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
-i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
-GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
-Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
-K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
-bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
-qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
-E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
-tylv2G0xffX8oRAHh84vWdw+WNs=
------END CERTIFICATE-----
-
COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@@ -1285,30 +1237,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----
-IGC/A
-=====
------BEGIN CERTIFICATE-----
-MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
-VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
-Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
-MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
-EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
-STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
-IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
-TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
-So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
-HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
-frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
-tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
-egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
-iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
-q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
-MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
-Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
-lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
-0mBWWg==
------END CERTIFICATE-----
-
Security Communication EV RootCA1
=================================
-----BEGIN CERTIFICATE-----
@@ -1353,46 +1281,6 @@ hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
-----END CERTIFICATE-----
-Microsec e-Szigno Root CA
-=========================
------BEGIN CERTIFICATE-----
-MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
-BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
-EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
-MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
-dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
-GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
-AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
-d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
-oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
-QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
-PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
-MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
-IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
-VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
-LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
-dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
-AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
-4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
-AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
-egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
-Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
-PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
-c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
-cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
-IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
-WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
-MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
-MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
-Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
-HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
-nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
-aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
-86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
-yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
-S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
------END CERTIFICATE-----
-
Certigna
========
-----BEGIN CERTIFICATE-----
@@ -1518,58 +1406,6 @@ LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
-----END CERTIFICATE-----
-Buypass Class 2 CA 1
-====================
------BEGIN CERTIFICATE-----
-MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
-QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
-MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
-c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
-cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
-0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
-0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
-uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
-MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
-AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
-1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
-7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
-fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
-wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
------END CERTIFICATE-----
-
-EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
-==========================================================================
------BEGIN CERTIFICATE-----
-MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
-QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
-Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
-ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
-IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
-X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
-gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
-eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
-TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
-Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
-uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
-qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
-ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
-Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
-/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
-Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
-FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
-zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
-XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
-bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
-RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
-1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
-2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
-Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
-AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
------END CERTIFICATE-----
-
certSIGN ROOT CA
================
-----BEGIN CERTIFICATE-----
@@ -1611,28 +1447,6 @@ G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
mxE=
-----END CERTIFICATE-----
-ApplicationCA - Japanese Government
-===================================
------BEGIN CERTIFICATE-----
-MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
-SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
-MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
-cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
-fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
-wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
-jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
-nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
-WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
-BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
-vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
-o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
-/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
-io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
-dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
-rosot4LKGAfmt1t06SAZf7IbiVQ=
------END CERTIFICATE-----
-
GeoTrust Primary Certification Authority - G3
=============================================
-----BEGIN CERTIFICATE-----
@@ -1764,7 +1578,7 @@ AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
-----END CERTIFICATE-----
NetLock Arany (Class Gold) Főtanúsítvány
-============================================
+========================================
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
@@ -1819,34 +1633,6 @@ IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
66+KAQ==
-----END CERTIFICATE-----
-Juur-SK
-=======
------BEGIN CERTIFICATE-----
-MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
-c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
-DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
-SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
-aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
-TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
-+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
-UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
-Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
-MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
-HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
-AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
-cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
-AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
-cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
-FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
-A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
-ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
-abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
-IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
-Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
-yyqcjg==
------END CERTIFICATE-----
-
Hongkong Post Root CA 1
=======================
-----BEGIN CERTIFICATE-----
@@ -2280,7 +2066,7 @@ Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
-----END CERTIFICATE-----
Certinomis - Autorité Racine
-=============================
+============================
-----BEGIN CERTIFICATE-----
MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
@@ -2310,41 +2096,6 @@ wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
vgt2Fl43N+bYdJeimUV5
-----END CERTIFICATE-----
-Root CA Generalitat Valenciana
-==============================
------BEGIN CERTIFICATE-----
-MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
-ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
-IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
-WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
-CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
-F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
-ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
-D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
-JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
-AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
-dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
-ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
-AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
-YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
-AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
-aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
-AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
-YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
-AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
-OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
-dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
-BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
-A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
-b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
-TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
-Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
-NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
-iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
-+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
------END CERTIFICATE-----
-
TWCA Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
@@ -3675,7 +3426,7 @@ ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
-----END CERTIFICATE-----
TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5
-=========================================================
+====================================================
-----BEGIN CERTIFICATE-----
MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN
BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
@@ -3698,30 +3449,6 @@ lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
-----END CERTIFICATE-----
-TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6
-=========================================================
------BEGIN CERTIFICATE-----
-MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G
-A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
-acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5
-MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL
-BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf
-aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm
-aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a
-2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED
-wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb
-HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV
-+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT
-9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
-9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R
-fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy
-o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW
-hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1
-O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw==
------END CERTIFICATE-----
-
Certinomis - Root CA
====================
-----BEGIN CERTIFICATE-----
@@ -3863,3 +3590,366 @@ 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-----
+
+Certplus Root CA G1
+===================
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV
+BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe
+Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD
+ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN
+r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx
+Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj
+BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv
+LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2
+z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc
+4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd
+4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj
+jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+
+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G
+A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY
+lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
+66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG
+YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/
+2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F
+6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX
+CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe
+tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC
+VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/
++mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+
+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
+-----END CERTIFICATE-----
+
+Certplus Root CA G2
+===================
+-----BEGIN CERTIFICATE-----
+MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT
+AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x
+NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0
+cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA
+BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN
+Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud
+IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV
+HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl
+vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw==
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G1
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx
+MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa
+Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87
+ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO
+YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9
+xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO
+9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq
+3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi
+n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9
+URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr
+TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px
+N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
+PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv
+uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK
+n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh
+X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80
+nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm
+GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/
+bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o
+4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA
+OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G2
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy
+MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+
+Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz
+4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV
+eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt
+UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz
+3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj
+3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz
+9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0
+0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT
+y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59
+M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
+Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI
+mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG
+S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp
+EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ
+6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr
+gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo
+SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0
+YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm
+u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G3
+====================
+-----BEGIN CERTIFICATE-----
+MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT
+AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X
+DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w
+ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B
+ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB
+/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf
+BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM
+BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta
+3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB
+-----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-----
+
+LuxTrust Global Root 2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG
+A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh
+bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW
+MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC
+AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm
+Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2
+xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC
+wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm
+1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm
+FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF
+wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/
+a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U
+ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ
+MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB
+/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5
+Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT
++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ
+FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN
+H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW
+7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu
+ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA
+VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR
+TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt
+/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc
+7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I
+iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
+-----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-----
diff --git a/library/jquery_ac/friendica.complete.js b/library/jquery_ac/friendica.complete.js
index 73a6e91d5..3012a754b 100644
--- a/library/jquery_ac/friendica.complete.js
+++ b/library/jquery_ac/friendica.complete.js
@@ -243,7 +243,7 @@
} else if (!this.isBadQuery(q)) {
me = this;
me.options.params.query = q;
- $('#nav-search-spinner').spin('tiny');
+ $('#nav-search-spinner').show();
$.get(this.serviceUrl, me.options.params, function(txt) { me.processResponse(txt); }, 'text');
}
},
@@ -306,7 +306,7 @@
this.data = response.data;
this.suggest();
}
- $('#nav-search-spinner').spin(false);
+ $('#nav-search-spinner').hide();
},
activate: function(index) {
diff --git a/library/jsonld/LICENSE b/library/jsonld/LICENSE
new file mode 100644
index 000000000..bd572d3e0
--- /dev/null
+++ b/library/jsonld/LICENSE
@@ -0,0 +1,30 @@
+BSD 3-Clause License
+Copyright (c) 2011, Digital Bazaar, Inc.
+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.
+
+Neither the name of the Digital Bazaar, Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+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
+HOLDER 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.
+
diff --git a/library/jsonld/README.md b/library/jsonld/README.md
new file mode 100644
index 000000000..5853c94bf
--- /dev/null
+++ b/library/jsonld/README.md
@@ -0,0 +1,193 @@
+php-json-ld
+===========
+
+[![Build Status][travis-ci-png]][travis-ci-site]
+[travis-ci-png]: https://travis-ci.org/digitalbazaar/php-json-ld.png?branch=master
+[travis-ci-site]: https://travis-ci.org/digitalbazaar/php-json-ld
+
+Introduction
+------------
+
+This library is an implementation of the [JSON-LD][] specification in [PHP][].
+
+JSON, as specified in [RFC7159][], is a simple language for representing
+objects on the Web. Linked Data is a way of describing content across
+different documents or Web sites. Web resources are described using
+IRIs, and typically are dereferencable entities that may be used to find
+more information, creating a "Web of Knowledge". [JSON-LD][] is intended
+to be a simple publishing method for expressing not only Linked Data in
+JSON, but for adding semantics to existing JSON.
+
+JSON-LD is designed as a light-weight syntax that can be used to express
+Linked Data. It is primarily intended to be a way to express Linked Data
+in JavaScript and other Web-based programming environments. It is also
+useful when building interoperable Web Services and when storing Linked
+Data in JSON-based document storage engines. It is practical and
+designed to be as simple as possible, utilizing the large number of JSON
+parsers and existing code that is in use today. It is designed to be
+able to express key-value pairs, RDF data, [RDFa][] data,
+[Microformats][] data, and [Microdata][]. That is, it supports every
+major Web-based structured data model in use today.
+
+The syntax does not require many applications to change their JSON, but
+easily add meaning by adding context in a way that is either in-band or
+out-of-band. The syntax is designed to not disturb already deployed
+systems running on JSON, but provide a smooth migration path from JSON
+to JSON with added semantics. Finally, the format is intended to be fast
+to parse, fast to generate, stream-based and document-based processing
+compatible, and require a very small memory footprint in order to operate.
+
+## Quick Examples
+
+```php
+$doc = (object)array(
+ "http://schema.org/name" => "Manu Sporny",
+ "http://schema.org/url" => (object)array("@id" => "http://manu.sporny.org/"),
+ "http://schema.org/image" => (object)array("@id" => "http://manu.sporny.org/images/manu.png")
+);
+
+$context = (object)array(
+ "name" => "http://schema.org/name",
+ "homepage" => (object)array("@id" => "http://schema.org/url", "@type" => "@id"),
+ "image" => (object)array("@id" => "http://schema.org/image", "@type" => "@id")
+);
+
+// compact a document according to a particular context
+// see: http://json-ld.org/spec/latest/json-ld/#compacted-document-form
+$compacted = jsonld_compact($doc, $context);
+
+echo json_encode($compacted, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+/* Output:
+{
+ "@context": {...},
+ "image": "http://manu.sporny.org/images/manu.png",
+ "homepage": "http://manu.sporny.org/",
+ "name": "Manu Sporny"
+}
+*/
+
+// compact using URLs
+jsonld_compact('http://example.org/doc', 'http://example.org/context');
+
+// expand a document, removing its context
+// see: http://json-ld.org/spec/latest/json-ld/#expanded-document-form
+$expanded = jsonld_expand($compacted) {
+echo json_encode($expanded, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+/* Output:
+{
+ "http://schema.org/image": [{"@id": "http://manu.sporny.org/images/manu.png"}],
+ "http://schema.org/name": [{"@value": "Manu Sporny"}],
+ "http://schema.org/url": [{"@id": "http://manu.sporny.org/"}]
+}
+*/
+
+// expand using URLs
+jsonld_expand('http://example.org/doc');
+
+// flatten a document
+// see: http://json-ld.org/spec/latest/json-ld/#flattened-document-form
+$flattened = jsonld_flatten($doc);
+// all deep-level trees flattened to the top-level
+
+// frame a document
+// see: http://json-ld.org/spec/latest/json-ld-framing/#introduction
+$framed = jsonld_frame($doc, $frame);
+// document transformed into a particular tree structure per the given frame
+
+// normalize a document using the RDF Dataset Normalization Algorithm
+// (URDNA2015), see: http://json-ld.github.io/normalization/spec/
+$normalized = jsonld_normalize(
+ $doc, array('algorithm' => 'URDNA2015', 'format' => 'application/nquads'));
+// normalized is a string that is a canonical representation of the document
+// that can be used for hashing, comparison, etc.
+
+// force HTTPS-only context loading:
+// use built-in secure document loader
+jsonld_set_document_loader('jsonld_default_secure_document_loader');
+
+// set a default custom document loader
+jsonld_set_document_loader('my_custom_doc_loader');
+
+// a custom loader that demonstrates using a simple in-memory mock for
+// certain contexts before falling back to the default loader
+// note: if you want to set this loader as the new default, you'll need to
+// store the previous default in another variable first and access that inside
+// the loader
+global $mocks;
+$mocks = array('http://example.com/mycontext' => (object)array(
+ 'hombre' => 'http://schema.org/name'));
+function mock_load($url) {
+ global $jsonld_default_load_document, $mocks;
+ if(isset($mocks[$url])) {
+ // return a "RemoteDocument", it has these three properties:
+ return (object)array(
+ 'contextUrl' => null,
+ 'document' => $mocks[$url],
+ 'documentUrl' => $url);
+ }
+ // use default loader
+ return call_user_func($jsonld_default_load_document, $url);
+}
+
+// use the mock loader for just this call, witout modifying the default one
+$compacted = jsonld_compact($foo, 'http://example.com/mycontext', array(
+ 'documentLoader' => 'mock_load'));
+
+// a custom loader that uses a simplistic in-memory cache (no invalidation)
+global $cache;
+$cache = array();
+function cache_load($url) {
+ global $jsonld_default_load_document, $cache;
+ if(isset($cache[$url])) {
+ return $cache[$url];
+ }
+ // use default loader
+ $doc = call_user_func($jsonld_default_load_document, $url);
+ $cache[$url] = $doc;
+ return $doc;
+}
+
+// use the cache loader for just this call, witout modifying the default one
+$compacted = jsonld_compact($foo, 'http://schema.org', array(
+ 'documentLoader' => 'cache_load'));
+```
+
+Commercial Support
+------------------
+
+Commercial support for this library is available upon request from
+[Digital Bazaar][]: support@digitalbazaar.com
+
+Source
+------
+
+The source code for the PHP implementation of the JSON-LD API
+is available at:
+
+http://github.com/digitalbazaar/php-json-ld
+
+Tests
+-----
+
+This library includes a sample testing utility which may be used to verify
+that changes to the processor maintain the correct output.
+
+To run the sample tests you will need to get the test suite files by cloning
+the `json-ld.org` and `normalization` repositories hosted on GitHub:
+
+- https://github.com/json-ld/json-ld.org
+- https://github.com/json-ld/normalization
+
+Then run the PHPUnit test.php application and point it at the directories
+containing the tests:
+
+ phpunit --group json-ld.org test.php -d {PATH_TO_JSON_LD_ORG/test-suite}
+ phpunit --group normalization test.php -d {PATH_TO_NORMALIZATION/tests}
+
+[Digital Bazaar]: http://digitalbazaar.com/
+[JSON-LD]: http://json-ld.org/
+[Microdata]: http://www.w3.org/TR/microdata/
+[Microformats]: http://microformats.org/
+[PHP]: http://php.net
+[RDFa]: http://www.w3.org/TR/rdfa-core/
+[RFC7159]: http://tools.ietf.org/html/rfc7159
diff --git a/library/jsonld/composer.json b/library/jsonld/composer.json
new file mode 100644
index 000000000..cc985b23e
--- /dev/null
+++ b/library/jsonld/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "digitalbazaar/json-ld",
+ "type": "library",
+ "description": "A JSON-LD Processor and API implementation in PHP.",
+ "keywords": [
+ "JSON",
+ "Linked Data",
+ "JSON-LD",
+ "RDF",
+ "Semantic Web",
+ "jsonld"
+ ],
+ "homepage": "https://github.com/digitalbazaar/php-json-ld",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Digital Bazaar, Inc.",
+ "email": "support@digitalbazaar.com",
+ "url": "http://digitalbazaar.com/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "ext-json": "*"
+ },
+ "autoload": {
+ "files": [ "jsonld.php" ]
+ }
+}
diff --git a/library/jsonld/jsonld.php b/library/jsonld/jsonld.php
new file mode 100644
index 000000000..28b3e7ce3
--- /dev/null
+++ b/library/jsonld/jsonld.php
@@ -0,0 +1,6038 @@
+<?php
+/**
+ * PHP implementation of the JSON-LD API.
+ * Version: 0.4.8-dev
+ *
+ * @author Dave Longley
+ *
+ * BSD 3-Clause License
+ * Copyright (c) 2011-2014 Digital Bazaar, Inc.
+ * 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.
+ *
+ * Neither the name of the Digital Bazaar, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * 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
+ * HOLDER 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.
+ */
+
+/**
+ * Performs JSON-LD compaction.
+ *
+ * @param mixed $input the JSON-LD object to compact.
+ * @param mixed $ctx the context to compact with.
+ * @param assoc [$options] options to use:
+ * [base] the base IRI to use.
+ * [graph] true to always output a top-level graph (default: false).
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the compacted JSON-LD output.
+ */
+function jsonld_compact($input, $ctx, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->compact($input, $ctx, $options);
+}
+
+/**
+ * Performs JSON-LD expansion.
+ *
+ * @param mixed $input the JSON-LD object to expand.
+ * @param assoc[$options] the options to use:
+ * [base] the base IRI to use.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return array the expanded JSON-LD output.
+ */
+function jsonld_expand($input, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->expand($input, $options);
+}
+
+/**
+ * Performs JSON-LD flattening.
+ *
+ * @param mixed $input the JSON-LD to flatten.
+ * @param mixed $ctx the context to use to compact the flattened output, or
+ * null.
+ * @param [options] the options to use:
+ * [base] the base IRI to use.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the flattened JSON-LD output.
+ */
+function jsonld_flatten($input, $ctx, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->flatten($input, $ctx, $options);
+}
+
+/**
+ * Performs JSON-LD framing.
+ *
+ * @param mixed $input the JSON-LD object to frame.
+ * @param stdClass $frame the JSON-LD frame to use.
+ * @param assoc [$options] the framing options.
+ * [base] the base IRI to use.
+ * [embed] default @embed flag (default: true).
+ * [explicit] default @explicit flag (default: false).
+ * [requireAll] default @requireAll flag (default: true).
+ * [omitDefault] default @omitDefault flag (default: false).
+ * [documentLoader(url)] the document loader.
+ *
+ * @return stdClass the framed JSON-LD output.
+ */
+function jsonld_frame($input, $frame, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->frame($input, $frame, $options);
+}
+
+/**
+ * **Experimental**
+ *
+ * Links a JSON-LD document's nodes in memory.
+ *
+ * @param mixed $input the JSON-LD document to link.
+ * @param mixed $ctx the JSON-LD context to apply or null.
+ * @param assoc [$options] the options to use:
+ * [base] the base IRI to use.
+ * [expandContext] a context to expand with.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return the linked JSON-LD output.
+ */
+function jsonld_link($input, $ctx, $options) {
+ // API matches running frame with a wildcard frame and embed: '@link'
+ // get arguments
+ $frame = new stdClass();
+ if($ctx) {
+ $frame->{'@context'} = $ctx;
+ }
+ $frame->{'@embed'} = '@link';
+ return jsonld_frame($input, $frame, $options);
+};
+
+/**
+ * Performs RDF dataset normalization on the given input. The input is
+ * JSON-LD unless the 'inputFormat' option is used. The output is an RDF
+ * dataset unless the 'format' option is used.
+ *
+ * @param mixed $input the JSON-LD object to normalize.
+ * @param assoc [$options] the options to use:
+ * [base] the base IRI to use.
+ * [intputFormat] the format if input is not JSON-LD:
+ * 'application/nquads' for N-Quads.
+ * [format] the format if output is a string:
+ * 'application/nquads' for N-Quads.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the normalized output.
+ */
+function jsonld_normalize($input, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->normalize($input, $options);
+}
+
+/**
+ * Converts an RDF dataset to JSON-LD.
+ *
+ * @param mixed $input a serialized string of RDF in a format specified
+ * by the format option or an RDF dataset to convert.
+ * @param assoc [$options] the options to use:
+ * [format] the format if input not an array:
+ * 'application/nquads' for N-Quads (default).
+ * [useRdfType] true to use rdf:type, false to use @type
+ * (default: false).
+ * [useNativeTypes] true to convert XSD types into native types
+ * (boolean, integer, double), false not to (default: false).
+ *
+ * @return array the JSON-LD output.
+ */
+function jsonld_from_rdf($input, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->fromRDF($input, $options);
+}
+
+/**
+ * Outputs the RDF dataset found in the given JSON-LD object.
+ *
+ * @param mixed $input the JSON-LD object.
+ * @param assoc [$options] the options to use:
+ * [base] the base IRI to use.
+ * [format] the format to use to output a string:
+ * 'application/nquads' for N-Quads.
+ * [produceGeneralizedRdf] true to output generalized RDF, false
+ * to produce only standard RDF (default: false).
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the resulting RDF dataset (or a serialization of it).
+ */
+function jsonld_to_rdf($input, $options=array()) {
+ $p = new JsonLdProcessor();
+ return $p->toRDF($input, $options);
+}
+
+/**
+ * JSON-encodes (with unescaped slashes) the given stdClass or array.
+ *
+ * @param mixed $input the native PHP stdClass or array which will be
+ * converted to JSON by json_encode().
+ * @param int $options the options to use.
+ * [JSON_PRETTY_PRINT] pretty print.
+ * @param int $depth the maximum depth to use.
+ *
+ * @return the encoded JSON data.
+ */
+function jsonld_encode($input, $options=0, $depth=512) {
+ // newer PHP has a flag to avoid escaped '/'
+ if(defined('JSON_UNESCAPED_SLASHES')) {
+ return json_encode($input, JSON_UNESCAPED_SLASHES | $options, $depth);
+ }
+ // use a simple string replacement of '\/' to '/'.
+ return str_replace('\\/', '/', json_encode($input, $options, $depth));
+}
+
+/**
+ * Decodes a serialized JSON-LD object.
+ *
+ * @param string $input the JSON-LD input.
+ *
+ * @return mixed the resolved JSON-LD object, null on error.
+ */
+function jsonld_decode($input) {
+ return json_decode($input);
+}
+
+/**
+ * Parses a link header. The results will be key'd by the value of "rel".
+ *
+ * Link: <http://json-ld.org/contexts/person.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
+ *
+ * Parses as: {
+ * 'http://www.w3.org/ns/json-ld#context': {
+ * target: http://json-ld.org/contexts/person.jsonld,
+ * type: 'application/ld+json'
+ * }
+ * }
+ *
+ * If there is more than one "rel" with the same IRI, then entries in the
+ * resulting map for that "rel" will be arrays of objects, otherwise they will
+ * be single objects.
+ *
+ * @param string $header the link header to parse.
+ *
+ * @return assoc the parsed result.
+ */
+function jsonld_parse_link_header($header) {
+ $rval = array();
+ // split on unbracketed/unquoted commas
+ if(!preg_match_all(
+ '/(?:<[^>]*?>|"[^"]*?"|[^,])+/', $header, $entries, PREG_SET_ORDER)) {
+ return $rval;
+ }
+ $r_link_header = '/\s*<([^>]*?)>\s*(?:;\s*(.*))?/';
+ foreach($entries as $entry) {
+ if(!preg_match($r_link_header, $entry[0], $match)) {
+ continue;
+ }
+ $result = (object)array('target' => $match[1]);
+ $params = $match[2];
+ $r_params = '/(.*?)=(?:(?:"([^"]*?)")|([^"]*?))\s*(?:(?:;\s*)|$)/';
+ preg_match_all($r_params, $params, $matches, PREG_SET_ORDER);
+ foreach($matches as $match) {
+ $result->{$match[1]} = $match[2] ?: $match[3];
+ }
+ $rel = property_exists($result, 'rel') ? $result->rel : '';
+ if(!isset($rval[$rel])) {
+ $rval[$rel] = $result;
+ } else if(is_array($rval[$rel])) {
+ $rval[$rel][] = $result;
+ } else {
+ $rval[$rel] = array($rval[$rel], $result);
+ }
+ }
+ return $rval;
+}
+
+/**
+ * Relabels all blank nodes in the given JSON-LD input.
+ *
+ * @param mixed input the JSON-LD input.
+ */
+function jsonld_relabel_blank_nodes($input) {
+ $p = new JsonLdProcessor();
+ return $p->_labelBlankNodes(new UniqueNamer('_:b'), $input);
+}
+
+/** JSON-LD shared in-memory cache. */
+global $jsonld_cache;
+$jsonld_cache = new stdClass();
+
+/** The default active context cache. */
+$jsonld_cache->activeCtx = new ActiveContextCache();
+
+/** Stores the default JSON-LD document loader. */
+global $jsonld_default_load_document;
+$jsonld_default_load_document = 'jsonld_default_document_loader';
+
+/**
+ * Sets the default JSON-LD document loader.
+ *
+ * @param callable load_document(url) the document loader.
+ */
+function jsonld_set_document_loader($load_document) {
+ global $jsonld_default_load_document;
+ $jsonld_default_load_document = $load_document;
+}
+
+/**
+ * Retrieves JSON-LD at the given URL.
+ *
+ * @param string $url the URL to retrieve.
+ *
+ * @return the JSON-LD.
+ */
+function jsonld_get_url($url) {
+ global $jsonld_default_load_document;
+ if($jsonld_default_load_document !== null) {
+ $document_loader = $jsonld_default_load_document;
+ } else {
+ $document_loader = 'jsonld_default_document_loader';
+ }
+
+ $remote_doc = call_user_func($document_loader, $url);
+ if($remote_doc) {
+ return $remote_doc->document;
+ }
+ return null;
+}
+
+/**
+ * The default implementation to retrieve JSON-LD at the given URL.
+ *
+ * @param string $url the URL to to retrieve.
+ *
+ * @return stdClass the RemoteDocument object.
+ */
+function jsonld_default_document_loader($url) {
+ $doc = (object)array(
+ 'contextUrl' => null, 'document' => null, 'documentUrl' => $url);
+ $redirects = array();
+
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' =>
+ "Accept: application/ld+json\r\n"),
+ /* Note: Use jsonld_default_secure_document_loader for security. */
+ 'ssl' => array(
+ 'verify_peer' => false,
+ 'allow_self_signed' => true)
+ );
+
+ $context = stream_context_create($opts);
+ $content_type = null;
+ stream_context_set_params($context, array('notification' =>
+ function($notification_code, $severity, $message) use (
+ &$redirects, &$content_type) {
+ switch($notification_code) {
+ case STREAM_NOTIFY_REDIRECTED:
+ $redirects[] = $message;
+ break;
+ case STREAM_NOTIFY_MIME_TYPE_IS:
+ $content_type = $message;
+ break;
+ };
+ }));
+ $result = @file_get_contents($url, false, $context);
+ if($result === false) {
+ throw new JsonLdException(
+ 'Could not retrieve a JSON-LD document from the URL: ' . $url,
+ 'jsonld.LoadDocumentError', 'loading document failed');
+ }
+ $link_header = array();
+ foreach($http_response_header as $header) {
+ if(strpos($header, 'link') === 0) {
+ $value = explode(': ', $header);
+ if(count($value) > 1) {
+ $link_header[] = $value[1];
+ }
+ }
+ }
+ $link_header = jsonld_parse_link_header(join(',', $link_header));
+ if(isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
+ $link_header = $link_header['http://www.w3.org/ns/json-ld#context'];
+ } else {
+ $link_header = null;
+ }
+ if($link_header && $content_type !== 'application/ld+json') {
+ // only 1 related link header permitted
+ if(is_array($link_header)) {
+ throw new JsonLdException(
+ 'URL could not be dereferenced, it has more than one ' .
+ 'associated HTTP Link Header.', 'jsonld.LoadDocumentError',
+ 'multiple context link headers', array('url' => $url));
+ }
+ $doc->{'contextUrl'} = $link_header->target;
+ }
+
+ // update document url based on redirects
+ $redirs = count($redirects);
+ if($redirs > 0) {
+ $url = $redirects[$redirs - 1];
+ }
+ $doc->document = $result;
+ $doc->documentUrl = $url;
+ return $doc;
+}
+
+/**
+ * The default implementation to retrieve JSON-LD at the given secure URL.
+ *
+ * @param string $url the secure URL to to retrieve.
+ *
+ * @return stdClass the RemoteDocument object.
+ */
+function jsonld_default_secure_document_loader($url) {
+ if(strpos($url, 'https') !== 0) {
+ throw new JsonLdException(
+ "Could not GET url: '$url'; 'https' is required.",
+ 'jsonld.LoadDocumentError', 'loading document failed');
+ }
+
+ $doc = (object)array(
+ 'contextUrl' => null, 'document' => null, 'documentUrl' => $url);
+ $redirects = array();
+
+ // default JSON-LD https GET implementation
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' =>
+ "Accept: application/ld+json\r\n"),
+ 'ssl' => array(
+ 'verify_peer' => true,
+ 'allow_self_signed' => false,
+ 'cafile' => '/etc/ssl/certs/ca-certificates.crt'));
+ $context = stream_context_create($opts);
+ $content_type = null;
+ stream_context_set_params($context, array('notification' =>
+ function($notification_code, $severity, $message) use (
+ &$redirects, &$content_type) {
+ switch($notification_code) {
+ case STREAM_NOTIFY_REDIRECTED:
+ $redirects[] = $message;
+ break;
+ case STREAM_NOTIFY_MIME_TYPE_IS:
+ $content_type = $message;
+ break;
+ };
+ }));
+ $result = @file_get_contents($url, false, $context);
+ if($result === false) {
+ throw new JsonLdException(
+ 'Could not retrieve a JSON-LD document from the URL: ' + $url,
+ 'jsonld.LoadDocumentError', 'loading document failed');
+ }
+ $link_header = array();
+ foreach($http_response_header as $header) {
+ if(strpos($header, 'link') === 0) {
+ $value = explode(': ', $header);
+ if(count($value) > 1) {
+ $link_header[] = $value[1];
+ }
+ }
+ }
+ $link_header = jsonld_parse_link_header(join(',', $link_header));
+ if(isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
+ $link_header = $link_header['http://www.w3.org/ns/json-ld#context'];
+ } else {
+ $link_header = null;
+ }
+ if($link_header && $content_type !== 'application/ld+json') {
+ // only 1 related link header permitted
+ if(is_array($link_header)) {
+ throw new JsonLdException(
+ 'URL could not be dereferenced, it has more than one ' .
+ 'associated HTTP Link Header.', 'jsonld.LoadDocumentError',
+ 'multiple context link headers', array('url' => $url));
+ }
+ $doc->{'contextUrl'} = $link_header->target;
+ }
+
+ // update document url based on redirects
+ foreach($redirects as $redirect) {
+ if(strpos($redirect, 'https') !== 0) {
+ throw new JsonLdException(
+ "Could not GET redirected url: '$redirect'; 'https' is required.",
+ 'jsonld.LoadDocumentError', 'loading document failed');
+ }
+ $url = $redirect;
+ }
+ $doc->document = $result;
+ $doc->documentUrl = $url;
+ return $doc;
+}
+
+/** Registered global RDF dataset parsers hashed by content-type. */
+global $jsonld_rdf_parsers;
+$jsonld_rdf_parsers = new stdClass();
+
+/**
+ * Registers a global RDF dataset parser by content-type, for use with
+ * jsonld_from_rdf. Global parsers will be used by JsonLdProcessors that do
+ * not register their own parsers.
+ *
+ * @param string $content_type the content-type for the parser.
+ * @param callable $parser(input) the parser function (takes a string as
+ * a parameter and returns an RDF dataset).
+ */
+function jsonld_register_rdf_parser($content_type, $parser) {
+ global $jsonld_rdf_parsers;
+ $jsonld_rdf_parsers->{$content_type} = $parser;
+}
+
+/**
+ * Unregisters a global RDF dataset parser by content-type.
+ *
+ * @param string $content_type the content-type for the parser.
+ */
+function jsonld_unregister_rdf_parser($content_type) {
+ global $jsonld_rdf_parsers;
+ if(property_exists($jsonld_rdf_parsers, $content_type)) {
+ unset($jsonld_rdf_parsers->{$content_type});
+ }
+}
+
+/**
+ * Parses a URL into its component parts.
+ *
+ * @param string $url the URL to parse.
+ *
+ * @return assoc the parsed URL.
+ */
+function jsonld_parse_url($url) {
+ if($url === null) {
+ $url = '';
+ }
+
+ $keys = array(
+ 'href', 'protocol', 'scheme', '?authority', 'authority',
+ '?auth', 'auth', 'user', 'pass', 'host', '?port', 'port', 'path',
+ '?query', 'query', '?fragment', 'fragment');
+ $regex = "/^(([^:\/?#]+):)?(\/\/(((([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(:(\d*))?))?([^?#]*)(\?([^#]*))?(#(.*))?/";
+ preg_match($regex, $url, $match);
+
+ $rval = array();
+ $flags = array();
+ $len = count($keys);
+ for($i = 0; $i < $len; ++$i) {
+ $key = $keys[$i];
+ if(strpos($key, '?') === 0) {
+ $flags[substr($key, 1)] = !empty($match[$i]);
+ } else if(!isset($match[$i])) {
+ $rval[$key] = null;
+ } else {
+ $rval[$key] = $match[$i];
+ }
+ }
+
+ if(!$flags['authority']) {
+ $rval['authority'] = null;
+ }
+ if(!$flags['auth']) {
+ $rval['auth'] = $rval['user'] = $rval['pass'] = null;
+ }
+ if(!$flags['port']) {
+ $rval['port'] = null;
+ }
+ if(!$flags['query']) {
+ $rval['query'] = null;
+ }
+ if(!$flags['fragment']) {
+ $rval['fragment'] = null;
+ }
+
+ $rval['normalizedPath'] = jsonld_remove_dot_segments(
+ $rval['path'], !!$rval['authority']);
+
+ return $rval;
+}
+
+/**
+ * Removes dot segments from a URL path.
+ *
+ * @param string $path the path to remove dot segments from.
+ * @param bool $has_authority true if the URL has an authority, false if not.
+ */
+function jsonld_remove_dot_segments($path, $has_authority) {
+ $rval = '';
+
+ if(strpos($path, '/') === 0) {
+ $rval = '/';
+ }
+
+ // RFC 3986 5.2.4 (reworked)
+ $input = explode('/', $path);
+ $output = array();
+ while(count($input) > 0) {
+ if($input[0] === '.' || ($input[0] === '' && count($input) > 1)) {
+ array_shift($input);
+ continue;
+ }
+ if($input[0] === '..') {
+ array_shift($input);
+ if($has_authority ||
+ (count($output) > 0 && $output[count($output) - 1] !== '..')) {
+ array_pop($output);
+ } else {
+ // leading relative URL '..'
+ $output[] = '..';
+ }
+ continue;
+ }
+ $output[] = array_shift($input);
+ }
+
+ return $rval . implode('/', $output);
+}
+
+/**
+ * Prepends a base IRI to the given relative IRI.
+ *
+ * @param mixed $base a string or the parsed base IRI.
+ * @param string $iri the relative IRI.
+ *
+ * @return string the absolute IRI.
+ */
+function jsonld_prepend_base($base, $iri) {
+ // skip IRI processing
+ if($base === null) {
+ return $iri;
+ }
+
+ // already an absolute IRI
+ if(strpos($iri, ':') !== false) {
+ return $iri;
+ }
+
+ // parse base if it is a string
+ if(is_string($base)) {
+ $base = jsonld_parse_url($base);
+ }
+
+ // parse given IRI
+ $rel = jsonld_parse_url($iri);
+
+ // per RFC3986 5.2.2
+ $transform = array('protocol' => $base['protocol']);
+
+ if($rel['authority'] !== null) {
+ $transform['authority'] = $rel['authority'];
+ $transform['path'] = $rel['path'];
+ $transform['query'] = $rel['query'];
+ } else {
+ $transform['authority'] = $base['authority'];
+
+ if($rel['path'] === '') {
+ $transform['path'] = $base['path'];
+ if($rel['query'] !== null) {
+ $transform['query'] = $rel['query'];
+ } else {
+ $transform['query'] = $base['query'];
+ }
+ } else {
+ if(strpos($rel['path'], '/') === 0) {
+ // IRI represents an absolute path
+ $transform['path'] = $rel['path'];
+ } else {
+ // merge paths
+ $path = $base['path'];
+
+ // append relative path to the end of the last directory from base
+ if($rel['path'] !== '') {
+ $idx = strrpos($path, '/');
+ $idx = ($idx === false) ? 0 : $idx + 1;
+ $path = substr($path, 0, $idx);
+ if(strlen($path) > 0 && substr($path, -1) !== '/') {
+ $path .= '/';
+ }
+ $path .= $rel['path'];
+ }
+
+ $transform['path'] = $path;
+ }
+ $transform['query'] = $rel['query'];
+ }
+ }
+
+ // remove slashes and dots in path
+ $transform['path'] = jsonld_remove_dot_segments(
+ $transform['path'], !!$transform['authority']);
+
+ // construct URL
+ $rval = $transform['protocol'];
+ if($transform['authority'] !== null) {
+ $rval .= '//' . $transform['authority'];
+ }
+ $rval .= $transform['path'];
+ if($transform['query'] !== null) {
+ $rval .= '?' . $transform['query'];
+ }
+ if($rel['fragment'] !== null) {
+ $rval .= '#' . $rel['fragment'];
+ }
+
+ // handle empty base
+ if($rval === '') {
+ $rval = './';
+ }
+
+ return $rval;
+}
+
+/**
+ * Removes a base IRI from the given absolute IRI.
+ *
+ * @param mixed $base the base IRI.
+ * @param string $iri the absolute IRI.
+ *
+ * @return string the relative IRI if relative to base, otherwise the absolute
+ * IRI.
+ */
+function jsonld_remove_base($base, $iri) {
+ // skip IRI processing
+ if($base === null) {
+ return $iri;
+ }
+
+ if(is_string($base)) {
+ $base = jsonld_parse_url($base);
+ }
+
+ // establish base root
+ $root = '';
+ if($base['href'] !== '') {
+ $root .= "{$base['protocol']}//{$base['authority']}";
+ } else if(strpos($iri, '//') === false) {
+ // support network-path reference with empty base
+ $root .= '//';
+ }
+
+ // IRI not relative to base
+ if($root === '' || strpos($iri, $root) !== 0) {
+ return $iri;
+ }
+
+ // remove root from IRI
+ $rel = jsonld_parse_url(substr($iri, strlen($root)));
+
+ // remove path segments that match (do not remove last segment unless there
+ // is a hash or query)
+ $base_segments = explode('/', $base['normalizedPath']);
+ $iri_segments = explode('/', $rel['normalizedPath']);
+ $last = ($rel['query'] || $rel['fragment']) ? 0 : 1;
+ while(count($base_segments) > 0 && count($iri_segments) > $last) {
+ if($base_segments[0] !== $iri_segments[0]) {
+ break;
+ }
+ array_shift($base_segments);
+ array_shift($iri_segments);
+ }
+
+ // use '../' for each non-matching base segment
+ $rval = '';
+ if(count($base_segments) > 0) {
+ // don't count the last segment (if it ends with '/' last path doesn't
+ // count and if it doesn't end with '/' it isn't a path)
+ array_pop($base_segments);
+ foreach($base_segments as $segment) {
+ $rval .= '../';
+ }
+ }
+
+ // prepend remaining segments
+ $rval .= implode('/', $iri_segments);
+
+ // add query and hash
+ if($rel['query'] !== null) {
+ $rval .= "?{$rel['query']}";
+ }
+ if($rel['fragment'] !== null) {
+ $rval .= "#{$rel['fragment']}";
+ }
+
+ if($rval === '') {
+ $rval = './';
+ }
+
+ return $rval;
+}
+
+
+/**
+ * A JSON-LD processor.
+ */
+class JsonLdProcessor {
+ /** XSD constants */
+ const XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean';
+ const XSD_DOUBLE = 'http://www.w3.org/2001/XMLSchema#double';
+ const XSD_INTEGER = 'http://www.w3.org/2001/XMLSchema#integer';
+ const XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string';
+
+ /** RDF constants */
+ const RDF_LIST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#List';
+ const RDF_FIRST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first';
+ const RDF_REST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest';
+ const RDF_NIL = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil';
+ const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
+ const RDF_LANGSTRING =
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString';
+
+ /** Restraints */
+ const MAX_CONTEXT_URLS = 10;
+
+ /** Processor-specific RDF dataset parsers. */
+ protected $rdfParsers = null;
+
+ /**
+ * Constructs a JSON-LD processor.
+ */
+ public function __construct() {}
+
+ /**
+ * Performs JSON-LD compaction.
+ *
+ * @param mixed $input the JSON-LD object to compact.
+ * @param mixed $ctx the context to compact with.
+ * @param assoc $options the compaction options.
+ * [base] the base IRI to use.
+ * [compactArrays] true to compact arrays to single values when
+ * appropriate, false not to (default: true).
+ * [graph] true to always output a top-level graph (default: false).
+ * [skipExpansion] true to assume the input is expanded and skip
+ * expansion, false not to, defaults to false.
+ * [activeCtx] true to also return the active context used.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the compacted JSON-LD output.
+ */
+ public function compact($input, $ctx, $options) {
+ global $jsonld_default_load_document;
+
+ if($ctx === null) {
+ throw new JsonLdException(
+ 'The compaction context must not be null.',
+ 'jsonld.CompactError', 'invalid local context');
+ }
+
+ // nothing to compact
+ if($input === null) {
+ return null;
+ }
+
+ self::setdefaults($options, array(
+ 'base' => is_string($input) ? $input : '',
+ 'compactArrays' => true,
+ 'graph' => false,
+ 'skipExpansion' => false,
+ 'activeCtx' => false,
+ 'documentLoader' => $jsonld_default_load_document,
+ 'link' => false));
+ if($options['link']) {
+ // force skip expansion when linking, "link" is not part of the
+ // public API, it should only be called from framing
+ $options['skipExpansion'] = true;
+ }
+
+ if($options['skipExpansion'] === true) {
+ $expanded = $input;
+ } else {
+ // expand input
+ try {
+ $expanded = $this->expand($input, $options);
+ } catch(JsonLdException $e) {
+ throw new JsonLdException(
+ 'Could not expand input before compaction.',
+ 'jsonld.CompactError', null, null, $e);
+ }
+ }
+
+ // process context
+ $active_ctx = $this->_getInitialContext($options);
+ try {
+ $active_ctx = $this->processContext($active_ctx, $ctx, $options);
+ } catch(JsonLdException $e) {
+ throw new JsonLdException(
+ 'Could not process context before compaction.',
+ 'jsonld.CompactError', null, null, $e);
+ }
+
+ // do compaction
+ $compacted = $this->_compact($active_ctx, null, $expanded, $options);
+
+ if($options['compactArrays'] &&
+ !$options['graph'] && is_array($compacted)) {
+ if(count($compacted) === 1) {
+ // simplify to a single item
+ $compacted = $compacted[0];
+ } else if(count($compacted) === 0) {
+ // simplify to an empty object
+ $compacted = new stdClass();
+ }
+ } else if($options['graph']) {
+ // always use array if graph option is on
+ $compacted = self::arrayify($compacted);
+ }
+
+ // follow @context key
+ if(is_object($ctx) && property_exists($ctx, '@context')) {
+ $ctx = $ctx->{'@context'};
+ }
+
+ // build output context
+ $ctx = self::copy($ctx);
+ $ctx = self::arrayify($ctx);
+
+ // remove empty contexts
+ $tmp = $ctx;
+ $ctx = array();
+ foreach($tmp as $v) {
+ if(!is_object($v) || count(array_keys((array)$v)) > 0) {
+ $ctx[] = $v;
+ }
+ }
+
+ // remove array if only one context
+ $ctx_length = count($ctx);
+ $has_context = ($ctx_length > 0);
+ if($ctx_length === 1) {
+ $ctx = $ctx[0];
+ }
+
+ // add context and/or @graph
+ if(is_array($compacted)) {
+ // use '@graph' keyword
+ $kwgraph = $this->_compactIri($active_ctx, '@graph');
+ $graph = $compacted;
+ $compacted = new stdClass();
+ if($has_context) {
+ $compacted->{'@context'} = $ctx;
+ }
+ $compacted->{$kwgraph} = $graph;
+ } else if(is_object($compacted) && $has_context) {
+ // reorder keys so @context is first
+ $graph = $compacted;
+ $compacted = new stdClass();
+ $compacted->{'@context'} = $ctx;
+ foreach($graph as $k => $v) {
+ $compacted->{$k} = $v;
+ }
+ }
+
+ if($options['activeCtx']) {
+ return array('compacted' => $compacted, 'activeCtx' => $active_ctx);
+ }
+
+ return $compacted;
+ }
+
+ /**
+ * Performs JSON-LD expansion.
+ *
+ * @param mixed $input the JSON-LD object to expand.
+ * @param assoc $options the options to use:
+ * [base] the base IRI to use.
+ * [expandContext] a context to expand with.
+ * [keepFreeFloatingNodes] true to keep free-floating nodes,
+ * false not to, defaults to false.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return array the expanded JSON-LD output.
+ */
+ public function expand($input, $options) {
+ global $jsonld_default_load_document;
+ self::setdefaults($options, array(
+ 'keepFreeFloatingNodes' => false,
+ 'documentLoader' => $jsonld_default_load_document));
+
+ // if input is a string, attempt to dereference remote document
+ if(is_string($input)) {
+ $remote_doc = call_user_func($options['documentLoader'], $input);
+ } else {
+ $remote_doc = (object)array(
+ 'contextUrl' => null,
+ 'documentUrl' => null,
+ 'document' => $input);
+ }
+
+ try {
+ if($remote_doc->document === null) {
+ throw new JsonLdException(
+ 'No remote document found at the given URL.',
+ 'jsonld.NullRemoteDocument');
+ }
+ if(is_string($remote_doc->document)) {
+ $remote_doc->document = self::_parse_json($remote_doc->document);
+ }
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not retrieve a JSON-LD document from the URL.',
+ 'jsonld.LoadDocumentError', 'loading document failed',
+ array('remoteDoc' => $remote_doc), $e);
+ }
+
+ // set default base
+ self::setdefault($options, 'base', $remote_doc->documentUrl ?: '');
+
+ // build meta-object and retrieve all @context urls
+ $input = (object)array(
+ 'document' => self::copy($remote_doc->document),
+ 'remoteContext' => (object)array(
+ '@context' => $remote_doc->contextUrl));
+ if(isset($options['expandContext'])) {
+ $expand_context = self::copy($options['expandContext']);
+ if(is_object($expand_context) &&
+ property_exists($expand_context, '@context')) {
+ $input->expandContext = $expand_context;
+ } else {
+ $input->expandContext = (object)array('@context' => $expand_context);
+ }
+ }
+
+ // retrieve all @context URLs in the input
+ try {
+ $this->_retrieveContextUrls(
+ $input, new stdClass(), $options['documentLoader'], $options['base']);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not perform JSON-LD expansion.',
+ 'jsonld.ExpandError', null, null, $e);
+ }
+
+ $active_ctx = $this->_getInitialContext($options);
+ $document = $input->document;
+ $remote_context = $input->remoteContext->{'@context'};
+
+ // process optional expandContext
+ if(property_exists($input, 'expandContext')) {
+ $active_ctx = self::_processContext(
+ $active_ctx, $input->expandContext, $options);
+ }
+
+ // process remote context from HTTP Link Header
+ if($remote_context) {
+ $active_ctx = self::_processContext(
+ $active_ctx, $remote_context, $options);
+ }
+
+ // do expansion
+ $expanded = $this->_expand($active_ctx, null, $document, $options, false);
+
+ // optimize away @graph with no other properties
+ if(is_object($expanded) && property_exists($expanded, '@graph') &&
+ count(array_keys((array)$expanded)) === 1) {
+ $expanded = $expanded->{'@graph'};
+ } else if($expanded === null) {
+ $expanded = array();
+ }
+ // normalize to an array
+ return self::arrayify($expanded);
+ }
+
+ /**
+ * Performs JSON-LD flattening.
+ *
+ * @param mixed $input the JSON-LD to flatten.
+ * @param ctx the context to use to compact the flattened output, or null.
+ * @param assoc $options the options to use:
+ * [base] the base IRI to use.
+ * [expandContext] a context to expand with.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return array the flattened output.
+ */
+ public function flatten($input, $ctx, $options) {
+ global $jsonld_default_load_document;
+ self::setdefaults($options, array(
+ 'base' => is_string($input) ? $input : '',
+ 'documentLoader' => $jsonld_default_load_document));
+
+ try {
+ // expand input
+ $expanded = $this->expand($input, $options);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not expand input before flattening.',
+ 'jsonld.FlattenError', null, null, $e);
+ }
+
+ // do flattening
+ $flattened = $this->_flatten($expanded);
+
+ if($ctx === null) {
+ return $flattened;
+ }
+
+ // compact result (force @graph option to true, skip expansion)
+ $options['graph'] = true;
+ $options['skipExpansion'] = true;
+ try {
+ $compacted = $this->compact($flattened, $ctx, $options);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not compact flattened output.',
+ 'jsonld.FlattenError', null, null, $e);
+ }
+
+ return $compacted;
+ }
+
+ /**
+ * Performs JSON-LD framing.
+ *
+ * @param mixed $input the JSON-LD object to frame.
+ * @param stdClass $frame the JSON-LD frame to use.
+ * @param $options the framing options.
+ * [base] the base IRI to use.
+ * [expandContext] a context to expand with.
+ * [embed] default @embed flag: '@last', '@always', '@never', '@link'
+ * (default: '@last').
+ * [explicit] default @explicit flag (default: false).
+ * [requireAll] default @requireAll flag (default: true).
+ * [omitDefault] default @omitDefault flag (default: false).
+ * [documentLoader(url)] the document loader.
+ *
+ * @return stdClass the framed JSON-LD output.
+ */
+ public function frame($input, $frame, $options) {
+ global $jsonld_default_load_document;
+ self::setdefaults($options, array(
+ 'base' => is_string($input) ? $input : '',
+ 'compactArrays' => true,
+ 'embed' => '@last',
+ 'explicit' => false,
+ 'requireAll' => true,
+ 'omitDefault' => false,
+ 'documentLoader' => $jsonld_default_load_document));
+
+ // if frame is a string, attempt to dereference remote document
+ if(is_string($frame)) {
+ $remote_frame = call_user_func($options['documentLoader'], $frame);
+ } else {
+ $remote_frame = (object)array(
+ 'contextUrl' => null,
+ 'documentUrl' => null,
+ 'document' => $frame);
+ }
+
+ try {
+ if($remote_frame->document === null) {
+ throw new JsonLdException(
+ 'No remote document found at the given URL.',
+ 'jsonld.NullRemoteDocument');
+ }
+ if(is_string($remote_frame->document)) {
+ $remote_frame->document = self::_parse_json($remote_frame->document);
+ }
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not retrieve a JSON-LD document from the URL.',
+ 'jsonld.LoadDocumentError', 'loading document failed',
+ array('remoteDoc' => $remote_frame), $e);
+ }
+
+ // preserve frame context
+ $frame = $remote_frame->document;
+ if($frame !== null) {
+ $ctx = (property_exists($frame, '@context') ?
+ $frame->{'@context'} : new stdClass());
+ if($remote_frame->contextUrl !== null) {
+ if($ctx !== null) {
+ $ctx = $remote_frame->contextUrl;
+ } else {
+ $ctx = self::arrayify($ctx);
+ $ctx[] = $remote_frame->contextUrl;
+ }
+ $frame->{'@context'} = $ctx;
+ }
+ }
+
+ try {
+ // expand input
+ $expanded = $this->expand($input, $options);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not expand input before framing.',
+ 'jsonld.FrameError', null, null, $e);
+ }
+
+ try {
+ // expand frame
+ $opts = $options;
+ $opts['keepFreeFloatingNodes'] = true;
+ $expanded_frame = $this->expand($frame, $opts);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not expand frame before framing.',
+ 'jsonld.FrameError', null, null, $e);
+ }
+
+ // do framing
+ $framed = $this->_frame($expanded, $expanded_frame, $options);
+
+ try {
+ // compact result (force @graph option to true, skip expansion, check
+ // for linked embeds)
+ $options['graph'] = true;
+ $options['skipExpansion'] = true;
+ $options['link'] = new ArrayObject();
+ $options['activeCtx'] = true;
+ $result = $this->compact($framed, $ctx, $options);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not compact framed output.',
+ 'jsonld.FrameError', null, null, $e);
+ }
+
+ $compacted = $result['compacted'];
+ $active_ctx = $result['activeCtx'];
+
+ // get graph alias
+ $graph = $this->_compactIri($active_ctx, '@graph');
+ // remove @preserve from results
+ $options['link'] = new ArrayObject();
+ $compacted->{$graph} = $this->_removePreserve(
+ $active_ctx, $compacted->{$graph}, $options);
+ return $compacted;
+ }
+
+ /**
+ * Performs JSON-LD normalization.
+ *
+ * @param mixed $input the JSON-LD object to normalize.
+ * @param assoc $options the options to use:
+ * [base] the base IRI to use.
+ * [expandContext] a context to expand with.
+ * [inputFormat] the format if input is not JSON-LD:
+ * 'application/nquads' for N-Quads.
+ * [format] the format if output is a string:
+ * 'application/nquads' for N-Quads.
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the normalized output.
+ */
+ public function normalize($input, $options) {
+ global $jsonld_default_load_document;
+ self::setdefaults($options, array(
+ 'base' => is_string($input) ? $input : '',
+ 'documentLoader' => $jsonld_default_load_document));
+
+ if(isset($options['inputFormat'])) {
+ if($options['inputFormat'] != 'application/nquads') {
+ throw new JsonLdException(
+ 'Unknown normalization input format.', 'jsonld.NormalizeError');
+ }
+ $dataset = $this->parseNQuads($input);
+ } else {
+ try {
+ // convert to RDF dataset then do normalization
+ $opts = $options;
+ if(isset($opts['format'])) {
+ unset($opts['format']);
+ }
+ $opts['produceGeneralizedRdf'] = false;
+ $dataset = $this->toRDF($input, $opts);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not convert input to RDF dataset before normalization.',
+ 'jsonld.NormalizeError', null, null, $e);
+ }
+ }
+
+ // do normalization
+ return $this->_normalize($dataset, $options);
+ }
+
+ /**
+ * Converts an RDF dataset to JSON-LD.
+ *
+ * @param mixed $dataset a serialized string of RDF in a format specified
+ * by the format option or an RDF dataset to convert.
+ * @param assoc $options the options to use:
+ * [format] the format if input is a string:
+ * 'application/nquads' for N-Quads (default).
+ * [useRdfType] true to use rdf:type, false to use @type
+ * (default: false).
+ * [useNativeTypes] true to convert XSD types into native types
+ * (boolean, integer, double), false not to (default: false).
+ *
+ * @return array the JSON-LD output.
+ */
+ public function fromRDF($dataset, $options) {
+ global $jsonld_rdf_parsers;
+
+ self::setdefaults($options, array(
+ 'useRdfType' => false,
+ 'useNativeTypes' => false));
+
+ if(!isset($options['format']) && is_string($dataset)) {
+ // set default format to nquads
+ $options['format'] = 'application/nquads';
+ }
+
+ // handle special format
+ if(isset($options['format']) && $options['format']) {
+ // supported formats (processor-specific and global)
+ if(($this->rdfParsers !== null &&
+ !property_exists($this->rdfParsers, $options['format'])) ||
+ $this->rdfParsers === null &&
+ !property_exists($jsonld_rdf_parsers, $options['format'])) {
+ throw new JsonLdException(
+ 'Unknown input format.',
+ 'jsonld.UnknownFormat', null, array('format' => $options['format']));
+ }
+ if($this->rdfParsers !== null) {
+ $callable = $this->rdfParsers->{$options['format']};
+ } else {
+ $callable = $jsonld_rdf_parsers->{$options['format']};
+ }
+ $dataset = call_user_func($callable, $dataset);
+ }
+
+ // convert from RDF
+ return $this->_fromRDF($dataset, $options);
+ }
+
+ /**
+ * Outputs the RDF dataset found in the given JSON-LD object.
+ *
+ * @param mixed $input the JSON-LD object.
+ * @param assoc $options the options to use:
+ * [base] the base IRI to use.
+ * [expandContext] a context to expand with.
+ * [format] the format to use to output a string:
+ * 'application/nquads' for N-Quads.
+ * [produceGeneralizedRdf] true to output generalized RDF, false
+ * to produce only standard RDF (default: false).
+ * [documentLoader(url)] the document loader.
+ *
+ * @return mixed the resulting RDF dataset (or a serialization of it).
+ */
+ public function toRDF($input, $options) {
+ global $jsonld_default_load_document;
+ self::setdefaults($options, array(
+ 'base' => is_string($input) ? $input : '',
+ 'produceGeneralizedRdf' => false,
+ 'documentLoader' => $jsonld_default_load_document));
+
+ try {
+ // expand input
+ $expanded = $this->expand($input, $options);
+ } catch(JsonLdException $e) {
+ throw new JsonLdException(
+ 'Could not expand input before serialization to RDF.',
+ 'jsonld.RdfError', null, null, $e);
+ }
+
+ // create node map for default graph (and any named graphs)
+ $namer = new UniqueNamer('_:b');
+ $node_map = (object)array('@default' => new stdClass());
+ $this->_createNodeMap($expanded, $node_map, '@default', $namer);
+
+ // output RDF dataset
+ $dataset = new stdClass();
+ $graph_names = array_keys((array)$node_map);
+ sort($graph_names);
+ foreach($graph_names as $graph_name) {
+ $graph = $node_map->{$graph_name};
+ // skip relative IRIs
+ if($graph_name === '@default' || self::_isAbsoluteIri($graph_name)) {
+ $dataset->{$graph_name} = $this->_graphToRDF($graph, $namer, $options);
+ }
+ }
+
+ $rval = $dataset;
+
+ // convert to output format
+ if(isset($options['format']) && $options['format']) {
+ // supported formats
+ if($options['format'] === 'application/nquads') {
+ $rval = self::toNQuads($dataset);
+ } else {
+ throw new JsonLdException(
+ 'Unknown output format.', 'jsonld.UnknownFormat',
+ null, array('format' => $options['format']));
+ }
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Processes a local context, resolving any URLs as necessary, and returns a
+ * new active context in its callback.
+ *
+ * @param stdClass $active_ctx the current active context.
+ * @param mixed $local_ctx the local context to process.
+ * @param assoc $options the options to use:
+ * [documentLoader(url)] the document loader.
+ *
+ * @return stdClass the new active context.
+ */
+ public function processContext($active_ctx, $local_ctx, $options) {
+ global $jsonld_default_load_document;
+ self::setdefaults($options, array(
+ 'base' => '',
+ 'documentLoader' => $jsonld_default_load_document));
+
+ // return initial context early for null context
+ if($local_ctx === null) {
+ return $this->_getInitialContext($options);
+ }
+
+ // retrieve URLs in local_ctx
+ $local_ctx = self::copy($local_ctx);
+ if(is_string($local_ctx) or (
+ is_object($local_ctx) && !property_exists($local_ctx, '@context'))) {
+ $local_ctx = (object)array('@context' => $local_ctx);
+ }
+ try {
+ $this->_retrieveContextUrls(
+ $local_ctx, new stdClass(),
+ $options['documentLoader'], $options['base']);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not process JSON-LD context.',
+ 'jsonld.ContextError', null, null, $e);
+ }
+
+ // process context
+ return $this->_processContext($active_ctx, $local_ctx, $options);
+ }
+
+ /**
+ * Returns true if the given subject has the given property.
+ *
+ * @param stdClass $subject the subject to check.
+ * @param string $property the property to look for.
+ *
+ * @return bool true if the subject has the given property, false if not.
+ */
+ public static function hasProperty($subject, $property) {
+ $rval = false;
+ if(property_exists($subject, $property)) {
+ $value = $subject->{$property};
+ $rval = (!is_array($value) || count($value) > 0);
+ }
+ return $rval;
+ }
+
+ /**
+ * Determines if the given value is a property of the given subject.
+ *
+ * @param stdClass $subject the subject to check.
+ * @param string $property the property to check.
+ * @param mixed $value the value to check.
+ *
+ * @return bool true if the value exists, false if not.
+ */
+ public static function hasValue($subject, $property, $value) {
+ $rval = false;
+ if(self::hasProperty($subject, $property)) {
+ $val = $subject->{$property};
+ $is_list = self::_isList($val);
+ if(is_array($val) || $is_list) {
+ if($is_list) {
+ $val = $val->{'@list'};
+ }
+ foreach($val as $v) {
+ if(self::compareValues($value, $v)) {
+ $rval = true;
+ break;
+ }
+ }
+ } else if(!is_array($value)) {
+ // avoid matching the set of values with an array value parameter
+ $rval = self::compareValues($value, $val);
+ }
+ }
+ return $rval;
+ }
+
+ /**
+ * Adds a value to a subject. If the value is an array, all values in the
+ * array will be added.
+ *
+ * Note: If the value is a subject that already exists as a property of the
+ * given subject, this method makes no attempt to deeply merge properties.
+ * Instead, the value will not be added.
+ *
+ * @param stdClass $subject the subject to add the value to.
+ * @param string $property the property that relates the value to the subject.
+ * @param mixed $value the value to add.
+ * @param assoc [$options] the options to use:
+ * [propertyIsArray] true if the property is always an array, false
+ * if not (default: false).
+ * [allowDuplicate] true to allow duplicates, false not to (uses a
+ * simple shallow comparison of subject ID or value)
+ * (default: true).
+ */
+ public static function addValue(
+ $subject, $property, $value, $options=array()) {
+ self::setdefaults($options, array(
+ 'allowDuplicate' => true,
+ 'propertyIsArray' => false));
+
+ if(is_array($value)) {
+ if(count($value) === 0 && $options['propertyIsArray'] &&
+ !property_exists($subject, $property)) {
+ $subject->{$property} = array();
+ }
+ foreach($value as $v) {
+ self::addValue($subject, $property, $v, $options);
+ }
+ } else if(property_exists($subject, $property)) {
+ // check if subject already has value if duplicates not allowed
+ $has_value = (!$options['allowDuplicate'] &&
+ self::hasValue($subject, $property, $value));
+
+ // make property an array if value not present or always an array
+ if(!is_array($subject->{$property}) &&
+ (!$has_value || $options['propertyIsArray'])) {
+ $subject->{$property} = array($subject->{$property});
+ }
+
+ // add new value
+ if(!$has_value) {
+ $subject->{$property}[] = $value;
+ }
+ } else {
+ // add new value as set or single value
+ $subject->{$property} = ($options['propertyIsArray'] ?
+ array($value) : $value);
+ }
+ }
+
+ /**
+ * Gets all of the values for a subject's property as an array.
+ *
+ * @param stdClass $subject the subject.
+ * @param string $property the property.
+ *
+ * @return array all of the values for a subject's property as an array.
+ */
+ public static function getValues($subject, $property) {
+ $rval = (property_exists($subject, $property) ?
+ $subject->{$property} : array());
+ return self::arrayify($rval);
+ }
+
+ /**
+ * Removes a property from a subject.
+ *
+ * @param stdClass $subject the subject.
+ * @param string $property the property.
+ */
+ public static function removeProperty($subject, $property) {
+ unset($subject->{$property});
+ }
+
+ /**
+ * Removes a value from a subject.
+ *
+ * @param stdClass $subject the subject.
+ * @param string $property the property that relates the value to the subject.
+ * @param mixed $value the value to remove.
+ * @param assoc [$options] the options to use:
+ * [propertyIsArray] true if the property is always an array,
+ * false if not (default: false).
+ */
+ public static function removeValue(
+ $subject, $property, $value, $options=array()) {
+ self::setdefaults($options, array(
+ 'propertyIsArray' => false));
+
+ // filter out value
+ $filter = function($e) use ($value) {
+ return !self::compareValues($e, $value);
+ };
+ $values = self::getValues($subject, $property);
+ $values = array_values(array_filter($values, $filter));
+
+ if(count($values) === 0) {
+ self::removeProperty($subject, $property);
+ } else if(count($values) === 1 && !$options['propertyIsArray']) {
+ $subject->{$property} = $values[0];
+ } else {
+ $subject->{$property} = $values;
+ }
+ }
+
+ /**
+ * Compares two JSON-LD values for equality. Two JSON-LD values will be
+ * considered equal if:
+ *
+ * 1. They are both primitives of the same type and value.
+ * 2. They are both @values with the same @value, @type, @language,
+ * and @index, OR
+ * 3. They both have @ids that are the same.
+ *
+ * @param mixed $v1 the first value.
+ * @param mixed $v2 the second value.
+ *
+ * @return bool true if v1 and v2 are considered equal, false if not.
+ */
+ public static function compareValues($v1, $v2) {
+ // 1. equal primitives
+ if($v1 === $v2) {
+ return true;
+ }
+
+ // 2. equal @values
+ if(self::_isValue($v1) && self::_isValue($v2)) {
+ return (
+ self::_compareKeyValues($v1, $v2, '@value') &&
+ self::_compareKeyValues($v1, $v2, '@type') &&
+ self::_compareKeyValues($v1, $v2, '@language') &&
+ self::_compareKeyValues($v1, $v2, '@index'));
+ }
+
+ // 3. equal @ids
+ if(is_object($v1) && property_exists($v1, '@id') &&
+ is_object($v2) && property_exists($v2, '@id')) {
+ return $v1->{'@id'} === $v2->{'@id'};
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets the value for the given active context key and type, null if none is
+ * set.
+ *
+ * @param stdClass $ctx the active context.
+ * @param string $key the context key.
+ * @param string [$type] the type of value to get (eg: '@id', '@type'), if not
+ * specified gets the entire entry for a key, null if not found.
+ *
+ * @return mixed the value.
+ */
+ public static function getContextValue($ctx, $key, $type) {
+ $rval = null;
+
+ // return null for invalid key
+ if($key === null) {
+ return $rval;
+ }
+
+ // get default language
+ if($type === '@language' && property_exists($ctx, $type)) {
+ $rval = $ctx->{$type};
+ }
+
+ // get specific entry information
+ if(property_exists($ctx->mappings, $key)) {
+ $entry = $ctx->mappings->{$key};
+ if($entry === null) {
+ return null;
+ }
+
+ if($type === null) {
+ // return whole entry
+ $rval = $entry;
+ } else if(property_exists($entry, $type)) {
+ // return entry value for type
+ $rval = $entry->{$type};
+ }
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Parses RDF in the form of N-Quads.
+ *
+ * @param string $input the N-Quads input to parse.
+ *
+ * @return stdClass an RDF dataset.
+ */
+ public static function parseNQuads($input) {
+ // define partial regexes
+ $iri = '(?:<([^:]+:[^>]*)>)';
+ $bnode = '(_:(?:[A-Za-z][A-Za-z0-9]*))';
+ $plain = '"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"';
+ $datatype = "(?:\\^\\^$iri)";
+ $language = '(?:@([a-z]+(?:-[a-z0-9]+)*))';
+ $literal = "(?:$plain(?:$datatype|$language)?)";
+ $ws = '[ \\t]';
+ $eoln = '/(?:\r\n)|(?:\n)|(?:\r)/';
+ $empty = "/^$ws*$/";
+
+ // define quad part regexes
+ $subject = "(?:$iri|$bnode)$ws+";
+ $property = "$iri$ws+";
+ $object = "(?:$iri|$bnode|$literal)$ws*";
+ $graph_name = "(?:\\.|(?:(?:$iri|$bnode)$ws*\\.))";
+
+ // full quad regex
+ $quad = "/^$ws*$subject$property$object$graph_name$ws*$/";
+
+ // build RDF dataset
+ $dataset = new stdClass();
+
+ // split N-Quad input into lines
+ $lines = preg_split($eoln, $input);
+ $line_number = 0;
+ foreach($lines as $line) {
+ $line_number += 1;
+
+ // skip empty lines
+ if(preg_match($empty, $line)) {
+ continue;
+ }
+
+ // parse quad
+ if(!preg_match($quad, $line, $match)) {
+ throw new JsonLdException(
+ 'Error while parsing N-Quads; invalid quad.',
+ 'jsonld.ParseError', null, array('line' => $line_number));
+ }
+
+ // create RDF triple
+ $triple = (object)array(
+ 'subject' => new stdClass(),
+ 'predicate' => new stdClass(),
+ 'object' => new stdClass());
+
+ // get subject
+ if($match[1] !== '') {
+ $triple->subject->type = 'IRI';
+ $triple->subject->value = $match[1];
+ } else {
+ $triple->subject->type = 'blank node';
+ $triple->subject->value = $match[2];
+ }
+
+ // get predicate
+ $triple->predicate->type = 'IRI';
+ $triple->predicate->value = $match[3];
+
+ // get object
+ if($match[4] !== '') {
+ $triple->object->type = 'IRI';
+ $triple->object->value = $match[4];
+ } else if($match[5] !== '') {
+ $triple->object->type = 'blank node';
+ $triple->object->value = $match[5];
+ } else {
+ $triple->object->type = 'literal';
+ $unescaped = str_replace(
+ array('\"', '\t', '\n', '\r', '\\\\'),
+ array('"', "\t", "\n", "\r", '\\'),
+ $match[6]);
+ if(isset($match[7]) && $match[7] !== '') {
+ $triple->object->datatype = $match[7];
+ } else if(isset($match[8]) && $match[8] !== '') {
+ $triple->object->datatype = self::RDF_LANGSTRING;
+ $triple->object->language = $match[8];
+ } else {
+ $triple->object->datatype = self::XSD_STRING;
+ }
+ $triple->object->value = $unescaped;
+ }
+
+ // get graph name ('@default' is used for the default graph)
+ $name = '@default';
+ if(isset($match[9]) && $match[9] !== '') {
+ $name = $match[9];
+ } else if(isset($match[10]) && $match[10] !== '') {
+ $name = $match[10];
+ }
+
+ // initialize graph in dataset
+ if(!property_exists($dataset, $name)) {
+ $dataset->{$name} = array($triple);
+ } else {
+ // add triple if unique to its graph
+ $unique = true;
+ $triples = &$dataset->{$name};
+ foreach($triples as $t) {
+ if(self::_compareRDFTriples($t, $triple)) {
+ $unique = false;
+ break;
+ }
+ }
+ if($unique) {
+ $triples[] = $triple;
+ }
+ }
+ }
+
+ return $dataset;
+ }
+
+ /**
+ * Converts an RDF dataset to N-Quads.
+ *
+ * @param stdClass $dataset the RDF dataset to convert.
+ *
+ * @return string the N-Quads string.
+ */
+ public static function toNQuads($dataset) {
+ $quads = array();
+ foreach($dataset as $graph_name => $triples) {
+ foreach($triples as $triple) {
+ if($graph_name === '@default') {
+ $graph_name = null;
+ }
+ $quads[] = self::toNQuad($triple, $graph_name);
+ }
+ }
+ sort($quads);
+ return implode($quads);
+ }
+
+ /**
+ * Converts an RDF triple and graph name to an N-Quad string (a single quad).
+ *
+ * @param stdClass $triple the RDF triple to convert.
+ * @param mixed $graph_name the name of the graph containing the triple, null
+ * for the default graph.
+ * @param string $bnode the bnode the quad is mapped to (optional, for
+ * use during normalization only).
+ *
+ * @return string the N-Quad string.
+ */
+ public static function toNQuad($triple, $graph_name, $bnode=null) {
+ $s = $triple->subject;
+ $p = $triple->predicate;
+ $o = $triple->object;
+ $g = $graph_name;
+
+ $quad = '';
+
+ // subject is an IRI
+ if($s->type === 'IRI') {
+ $quad .= "<{$s->value}>";
+ } else if($bnode !== null) {
+ // bnode normalization mode
+ $quad .= ($s->value === $bnode) ? '_:a' : '_:z';
+ } else {
+ // bnode normal mode
+ $quad .= $s->value;
+ }
+ $quad .= ' ';
+
+ // predicate is an IRI
+ if($p->type === 'IRI') {
+ $quad .= "<{$p->value}>";
+ } else if($bnode !== null) {
+ // FIXME: TBD what to do with bnode predicates during normalization
+ // bnode normalization mode
+ $quad .= '_:p';
+ } else {
+ // bnode normal mode
+ $quad .= $p->value;
+ }
+ $quad .= ' ';
+
+ // object is IRI, bnode, or literal
+ if($o->type === 'IRI') {
+ $quad .= "<{$o->value}>";
+ } else if($o->type === 'blank node') {
+ if($bnode !== null) {
+ // normalization mode
+ $quad .= ($o->value === $bnode) ? '_:a' : '_:z';
+ } else {
+ // normal mode
+ $quad .= $o->value;
+ }
+ } else {
+ $escaped = str_replace(
+ array('\\', "\t", "\n", "\r", '"'),
+ array('\\\\', '\t', '\n', '\r', '\"'),
+ $o->value);
+ $quad .= '"' . $escaped . '"';
+ if($o->datatype === self::RDF_LANGSTRING) {
+ if($o->language) {
+ $quad .= "@{$o->language}";
+ }
+ } else if($o->datatype !== self::XSD_STRING) {
+ $quad .= "^^<{$o->datatype}>";
+ }
+ }
+
+ // graph
+ if($g !== null) {
+ if(strpos($g, '_:') !== 0) {
+ $quad .= " <$g>";
+ } else if($bnode) {
+ $quad .= ' _:g';
+ } else {
+ $quad .= " $g";
+ }
+ }
+
+ $quad .= " .\n";
+ return $quad;
+ }
+
+ /**
+ * Registers a processor-specific RDF dataset parser by content-type.
+ * Global parsers will no longer be used by this processor.
+ *
+ * @param string $content_type the content-type for the parser.
+ * @param callable $parser(input) the parser function (takes a string as
+ * a parameter and returns an RDF dataset).
+ */
+ public function registerRDFParser($content_type, $parser) {
+ if($this->rdfParsers === null) {
+ $this->rdfParsers = new stdClass();
+ }
+ $this->rdfParsers->{$content_type} = $parser;
+ }
+
+ /**
+ * Unregisters a process-specific RDF dataset parser by content-type. If
+ * there are no remaining processor-specific parsers, then the global
+ * parsers will be re-enabled.
+ *
+ * @param string $content_type the content-type for the parser.
+ */
+ public function unregisterRDFParser($content_type) {
+ if($this->rdfParsers !== null &&
+ property_exists($this->rdfParsers, $content_type)) {
+ unset($this->rdfParsers->{$content_type});
+ if(count(get_object_vars($content_type)) === 0) {
+ $this->rdfParsers = null;
+ }
+ }
+ }
+
+ /**
+ * If $value is an array, returns $value, otherwise returns an array
+ * containing $value as the only element.
+ *
+ * @param mixed $value the value.
+ *
+ * @return array an array.
+ */
+ public static function arrayify($value) {
+ return is_array($value) ? $value : array($value);
+ }
+
+ /**
+ * Clones an object, array, or string/number.
+ *
+ * @param mixed $value the value to clone.
+ *
+ * @return mixed the cloned value.
+ */
+ public static function copy($value) {
+ if(is_object($value) || is_array($value)) {
+ return unserialize(serialize($value));
+ }
+ return $value;
+ }
+
+ /**
+ * Sets the value of a key for the given array if that property
+ * has not already been set.
+ *
+ * @param &assoc $arr the object to update.
+ * @param string $key the key to update.
+ * @param mixed $value the value to set.
+ */
+ public static function setdefault(&$arr, $key, $value) {
+ isset($arr[$key]) or $arr[$key] = $value;
+ }
+
+ /**
+ * Sets default values for keys in the given array.
+ *
+ * @param &assoc $arr the object to update.
+ * @param assoc $defaults the default keys and values.
+ */
+ public static function setdefaults(&$arr, $defaults) {
+ foreach($defaults as $key => $value) {
+ self::setdefault($arr, $key, $value);
+ }
+ }
+
+ /**
+ * Recursively compacts an element using the given active context. All values
+ * must be in expanded form before this method is called.
+ *
+ * @param stdClass $active_ctx the active context to use.
+ * @param mixed $active_property the compacted property with the element
+ * to compact, null for none.
+ * @param mixed $element the element to compact.
+ * @param assoc $options the compaction options.
+ *
+ * @return mixed the compacted value.
+ */
+ protected function _compact(
+ $active_ctx, $active_property, $element, $options) {
+ // recursively compact array
+ if(is_array($element)) {
+ $rval = array();
+ foreach($element as $e) {
+ // compact, dropping any null values
+ $compacted = $this->_compact(
+ $active_ctx, $active_property, $e, $options);
+ if($compacted !== null) {
+ $rval[] = $compacted;
+ }
+ }
+ if($options['compactArrays'] && count($rval) === 1) {
+ // use single element if no container is specified
+ $container = self::getContextValue(
+ $active_ctx, $active_property, '@container');
+ if($container === null) {
+ $rval = $rval[0];
+ }
+ }
+ return $rval;
+ }
+
+ // recursively compact object
+ if(is_object($element)) {
+ if($options['link'] && property_exists($element, '@id') &&
+ isset($options['link'][$element->{'@id'}])) {
+ // check for a linked element to reuse
+ $linked = $options['link'][$element->{'@id'}];
+ foreach($linked as $link) {
+ if($link['expanded'] === $element) {
+ return $link['compacted'];
+ }
+ }
+ }
+
+ // do value compaction on @values and subject references
+ if(self::_isValue($element) || self::_isSubjectReference($element)) {
+ $rval = $this->_compactValue($active_ctx, $active_property, $element);
+ if($options['link'] && self::_isSubjectReference($element)) {
+ // store linked element
+ if(!isset($options['link'][$element->{'@id'}])) {
+ $options['link'][$element->{'@id'}] = array();
+ }
+ $options['link'][$element->{'@id'}][] = array(
+ 'expanded' => $element, 'compacted' => $rval);
+ }
+ return $rval;
+ }
+
+ // FIXME: avoid misuse of active property as an expanded property?
+ $inside_reverse = ($active_property === '@reverse');
+
+ $rval = new stdClass();
+
+ if($options['link'] && property_exists($element, '@id')) {
+ // store linked element
+ if(!isset($options['link'][$element->{'@id'}])) {
+ $options['link'][$element->{'@id'}] = array();
+ }
+ $options['link'][$element->{'@id'}][] = array(
+ 'expanded' => $element, 'compacted' => $rval);
+ }
+
+ // process element keys in order
+ $keys = array_keys((array)$element);
+ sort($keys);
+ foreach($keys as $expanded_property) {
+ $expanded_value = $element->{$expanded_property};
+
+ // compact @id and @type(s)
+ if($expanded_property === '@id' || $expanded_property === '@type') {
+ if(is_string($expanded_value)) {
+ // compact single @id
+ $compacted_value = $this->_compactIri(
+ $active_ctx, $expanded_value, null,
+ array('vocab' => ($expanded_property === '@type')));
+ } else {
+ // expanded value must be a @type array
+ $compacted_value = array();
+ foreach($expanded_value as $ev) {
+ $compacted_value[] = $this->_compactIri(
+ $active_ctx, $ev, null, array('vocab' => true));
+ }
+ }
+
+ // use keyword alias and add value
+ $alias = $this->_compactIri($active_ctx, $expanded_property);
+ $is_array = (is_array($compacted_value) &&
+ count($expanded_value) === 0);
+ self::addValue(
+ $rval, $alias, $compacted_value,
+ array('propertyIsArray' => $is_array));
+ continue;
+ }
+
+ // handle @reverse
+ if($expanded_property === '@reverse') {
+ // recursively compact expanded value
+ $compacted_value = $this->_compact(
+ $active_ctx, '@reverse', $expanded_value, $options);
+
+ // handle double-reversed properties
+ foreach($compacted_value as $compacted_property => $value) {
+ if(property_exists($active_ctx->mappings, $compacted_property) &&
+ $active_ctx->mappings->{$compacted_property} &&
+ $active_ctx->mappings->{$compacted_property}->reverse) {
+ $container = self::getContextValue(
+ $active_ctx, $compacted_property, '@container');
+ $use_array = ($container === '@set' ||
+ !$options['compactArrays']);
+ self::addValue(
+ $rval, $compacted_property, $value,
+ array('propertyIsArray' => $use_array));
+ unset($compacted_value->{$compacted_property});
+ }
+ }
+
+ if(count(array_keys((array)$compacted_value)) > 0) {
+ // use keyword alias and add value
+ $alias = $this->_compactIri($active_ctx, $expanded_property);
+ self::addValue($rval, $alias, $compacted_value);
+ }
+
+ continue;
+ }
+
+ // handle @index property
+ if($expanded_property === '@index') {
+ // drop @index if inside an @index container
+ $container = self::getContextValue(
+ $active_ctx, $active_property, '@container');
+ if($container === '@index') {
+ continue;
+ }
+
+ // use keyword alias and add value
+ $alias = $this->_compactIri($active_ctx, $expanded_property);
+ self::addValue($rval, $alias, $expanded_value);
+ continue;
+ }
+
+ // skip array processing for keywords that aren't @graph or @list
+ if($expanded_property !== '@graph' && $expanded_property !== '@list' &&
+ self::_isKeyword($expanded_property)) {
+ // use keyword alias and add value as is
+ $alias = $this->_compactIri($active_ctx, $expanded_property);
+ self::addValue($rval, $alias, $expanded_value);
+ continue;
+ }
+
+ // Note: expanded value must be an array due to expansion algorithm.
+
+ // preserve empty arrays
+ if(count($expanded_value) === 0) {
+ $item_active_property = $this->_compactIri(
+ $active_ctx, $expanded_property, $expanded_value,
+ array('vocab' => true), $inside_reverse);
+ self::addValue(
+ $rval, $item_active_property, array(),
+ array('propertyIsArray' => true));
+ }
+
+ // recusively process array values
+ foreach($expanded_value as $expanded_item) {
+ // compact property and get container type
+ $item_active_property = $this->_compactIri(
+ $active_ctx, $expanded_property, $expanded_item,
+ array('vocab' => true), $inside_reverse);
+ $container = self::getContextValue(
+ $active_ctx, $item_active_property, '@container');
+
+ // get @list value if appropriate
+ $is_list = self::_isList($expanded_item);
+ $list = null;
+ if($is_list) {
+ $list = $expanded_item->{'@list'};
+ }
+
+ // recursively compact expanded item
+ $compacted_item = $this->_compact(
+ $active_ctx, $item_active_property,
+ $is_list ? $list : $expanded_item, $options);
+
+ // handle @list
+ if($is_list) {
+ // ensure @list value is an array
+ $compacted_item = self::arrayify($compacted_item);
+
+ if($container !== '@list') {
+ // wrap using @list alias
+ $compacted_item = (object)array(
+ $this->_compactIri($active_ctx, '@list') => $compacted_item);
+
+ // include @index from expanded @list, if any
+ if(property_exists($expanded_item, '@index')) {
+ $compacted_item->{$this->_compactIri($active_ctx, '@index')} =
+ $expanded_item->{'@index'};
+ }
+ } else if(property_exists($rval, $item_active_property)) {
+ // can't use @list container for more than 1 list
+ throw new JsonLdException(
+ 'JSON-LD compact error; property has a "@list" @container ' .
+ 'rule but there is more than a single @list that matches ' .
+ 'the compacted term in the document. Compaction might mix ' .
+ 'unwanted items into the list.', 'jsonld.SyntaxError',
+ 'compaction to list of lists');
+ }
+ }
+
+ // handle language and index maps
+ if($container === '@language' || $container === '@index') {
+ // get or create the map object
+ if(property_exists($rval, $item_active_property)) {
+ $map_object = $rval->{$item_active_property};
+ } else {
+ $rval->{$item_active_property} = $map_object = new stdClass();
+ }
+
+ // if container is a language map, simplify compacted value to
+ // a simple string
+ if($container === '@language' && self::_isValue($compacted_item)) {
+ $compacted_item = $compacted_item->{'@value'};
+ }
+
+ // add compact value to map object using key from expanded value
+ // based on the container type
+ self::addValue(
+ $map_object, $expanded_item->{$container}, $compacted_item);
+ } else {
+ // use an array if: compactArrays flag is false,
+ // @container is @set or @list, value is an empty
+ // array, or key is @graph
+ $is_array = (!$options['compactArrays'] ||
+ $container === '@set' || $container === '@list' ||
+ (is_array($compacted_item) && count($compacted_item) === 0) ||
+ $expanded_property === '@list' ||
+ $expanded_property === '@graph');
+
+ // add compact value
+ self::addValue(
+ $rval, $item_active_property, $compacted_item,
+ array('propertyIsArray' => $is_array));
+ }
+ }
+ }
+
+ return $rval;
+ }
+
+ // only primitives remain which are already compact
+ return $element;
+ }
+
+ /**
+ * Recursively expands an element using the given context. Any context in
+ * the element will be removed. All context URLs must have been retrieved
+ * before calling this method.
+ *
+ * @param stdClass $active_ctx the active context to use.
+ * @param mixed $active_property the property for the element, null for none.
+ * @param mixed $element the element to expand.
+ * @param assoc $options the expansion options.
+ * @param bool $inside_list true if the property is a list, false if not.
+ *
+ * @return mixed the expanded value.
+ */
+ protected function _expand(
+ $active_ctx, $active_property, $element, $options, $inside_list) {
+ // nothing to expand
+ if($element === null) {
+ return $element;
+ }
+
+ // recursively expand array
+ if(is_array($element)) {
+ $rval = array();
+ $container = self::getContextValue(
+ $active_ctx, $active_property, '@container');
+ $inside_list = $inside_list || $container === '@list';
+ foreach($element as $e) {
+ // expand element
+ $e = $this->_expand(
+ $active_ctx, $active_property, $e, $options, $inside_list);
+ if($inside_list && (is_array($e) || self::_isList($e))) {
+ // lists of lists are illegal
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; lists of lists are not permitted.',
+ 'jsonld.SyntaxError', 'list of lists');
+ }
+ // drop null values
+ if($e !== null) {
+ if(is_array($e)) {
+ $rval = array_merge($rval, $e);
+ } else {
+ $rval[] = $e;
+ }
+ }
+ }
+ return $rval;
+ }
+
+ if(!is_object($element)) {
+ // drop free-floating scalars that are not in lists
+ if(!$inside_list &&
+ ($active_property === null ||
+ $this->_expandIri($active_ctx, $active_property,
+ array('vocab' => true)) === '@graph')) {
+ return null;
+ }
+
+ // expand element according to value expansion rules
+ return $this->_expandValue($active_ctx, $active_property, $element);
+ }
+
+ // recursively expand object:
+
+ // if element has a context, process it
+ if(property_exists($element, '@context')) {
+ $active_ctx = $this->_processContext(
+ $active_ctx, $element->{'@context'}, $options);
+ }
+
+ // expand the active property
+ $expanded_active_property = $this->_expandIri(
+ $active_ctx, $active_property, array('vocab' => true));
+
+ $rval = new stdClass();
+ $keys = array_keys((array)$element);
+ sort($keys);
+ foreach($keys as $key) {
+ $value = $element->{$key};
+
+ if($key === '@context') {
+ continue;
+ }
+
+ // expand key to IRI
+ $expanded_property = $this->_expandIri(
+ $active_ctx, $key, array('vocab' => true));
+
+ // drop non-absolute IRI keys that aren't keywords
+ if($expanded_property === null ||
+ !(self::_isAbsoluteIri($expanded_property) ||
+ self::_isKeyword($expanded_property))) {
+ continue;
+ }
+
+ if(self::_isKeyword($expanded_property)) {
+ if($expanded_active_property === '@reverse') {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; a keyword cannot be used as a @reverse ' .
+ 'property.', 'jsonld.SyntaxError', 'invalid reverse property map',
+ array('value' => $value));
+ }
+ if(property_exists($rval, $expanded_property)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; colliding keywords detected.',
+ 'jsonld.SyntaxError', 'colliding keywords',
+ array('keyword' => $expanded_property));
+ }
+ }
+
+ // syntax error if @id is not a string
+ if($expanded_property === '@id' && !is_string($value)) {
+ if(!isset($options['isFrame']) || !$options['isFrame']) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@id" value must a string.',
+ 'jsonld.SyntaxError', 'invalid @id value',
+ array('value' => $value));
+ }
+ if(!is_object($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@id" value must a string or an object.',
+ 'jsonld.SyntaxError', 'invalid @id value',
+ array('value' => $value));
+ }
+ }
+
+ // validate @type value
+ if($expanded_property === '@type') {
+ $this->_validateTypeValue($value);
+ }
+
+ // @graph must be an array or an object
+ if($expanded_property === '@graph' &&
+ !(is_object($value) || is_array($value))) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@graph" value must not be an ' .
+ 'object or an array.', 'jsonld.SyntaxError',
+ 'invalid @graph value', array('value' => $value));
+ }
+
+ // @value must not be an object or an array
+ if($expanded_property === '@value' &&
+ (is_object($value) || is_array($value))) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@value" value must not be an ' .
+ 'object or an array.', 'jsonld.SyntaxError',
+ 'invalid value object value', array('value' => $value));
+ }
+
+ // @language must be a string
+ if($expanded_property === '@language') {
+ if($value === null) {
+ // drop null @language values, they expand as if they didn't exist
+ continue;
+ }
+ if(!is_string($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@language" value must not be a string.',
+ 'jsonld.SyntaxError', 'invalid language-tagged string',
+ array('value' => $value));
+ }
+ // ensure language value is lowercase
+ $value = strtolower($value);
+ }
+
+ // @index must be a string
+ if($expanded_property === '@index') {
+ if(!is_string($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@index" value must be a string.',
+ 'jsonld.SyntaxError', 'invalid @index value',
+ array('value' => $value));
+ }
+ }
+
+ // @reverse must be an object
+ if($expanded_property === '@reverse') {
+ if(!is_object($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@reverse" value must be an object.',
+ 'jsonld.SyntaxError', 'invalid @reverse value',
+ array('value' => $value));
+ }
+
+ $expanded_value = $this->_expand(
+ $active_ctx, '@reverse', $value, $options, $inside_list);
+
+ // properties double-reversed
+ if(property_exists($expanded_value, '@reverse')) {
+ foreach($expanded_value->{'@reverse'} as $rproperty => $rvalue) {
+ self::addValue(
+ $rval, $rproperty, $rvalue, array('propertyIsArray' => true));
+ }
+ }
+
+ // FIXME: can this be merged with code below to simplify?
+ // merge in all reversed properties
+ if(property_exists($rval, '@reverse')) {
+ $reverse_map = $rval->{'@reverse'};
+ } else {
+ $reverse_map = null;
+ }
+ foreach($expanded_value as $property => $items) {
+ if($property === '@reverse') {
+ continue;
+ }
+ if($reverse_map === null) {
+ $reverse_map = $rval->{'@reverse'} = new stdClass();
+ }
+ self::addValue(
+ $reverse_map, $property, array(),
+ array('propertyIsArray' => true));
+ foreach($items as $item) {
+ if(self::_isValue($item) || self::_isList($item)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@reverse" value must not be a ' +
+ '@value or an @list.', 'jsonld.SyntaxError',
+ 'invalid reverse property value',
+ array('value' => $expanded_value));
+ }
+ self::addValue(
+ $reverse_map, $property, $item,
+ array('propertyIsArray' => true));
+ }
+ }
+
+ continue;
+ }
+
+ $container = self::getContextValue($active_ctx, $key, '@container');
+
+ if($container === '@language' && is_object($value)) {
+ // handle language map container (skip if value is not an object)
+ $expanded_value = $this->_expandLanguageMap($value);
+ } else if($container === '@index' && is_object($value)) {
+ // handle index container (skip if value is not an object)
+ $expanded_value = array();
+ $value_keys = array_keys((array)$value);
+ sort($value_keys);
+ foreach($value_keys as $value_key) {
+ $val = $value->{$value_key};
+ $val = self::arrayify($val);
+ $val = $this->_expand($active_ctx, $key, $val, $options, false);
+ foreach($val as $item) {
+ if(!property_exists($item, '@index')) {
+ $item->{'@index'} = $value_key;
+ }
+ $expanded_value[] = $item;
+ }
+ }
+ } else {
+ // recurse into @list or @set
+ $is_list = ($expanded_property === '@list');
+ if($is_list || $expanded_property === '@set') {
+ $next_active_property = $active_property;
+ if($is_list && $expanded_active_property === '@graph') {
+ $next_active_property = null;
+ }
+ $expanded_value = $this->_expand(
+ $active_ctx, $next_active_property, $value, $options, $is_list);
+ if($is_list && self::_isList($expanded_value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; lists of lists are not permitted.',
+ 'jsonld.SyntaxError', 'list of lists');
+ }
+ } else {
+ // recursively expand value with key as new active property
+ $expanded_value = $this->_expand(
+ $active_ctx, $key, $value, $options, false);
+ }
+ }
+
+ // drop null values if property is not @value
+ if($expanded_value === null && $expanded_property !== '@value') {
+ continue;
+ }
+
+ // convert expanded value to @list if container specifies it
+ if($expanded_property !== '@list' && !self::_isList($expanded_value) &&
+ $container === '@list') {
+ // ensure expanded value is an array
+ $expanded_value = (object)array(
+ '@list' => self::arrayify($expanded_value));
+ }
+
+ // FIXME: can this be merged with code above to simplify?
+ // merge in reverse properties
+ if(property_exists($active_ctx->mappings, $key) &&
+ $active_ctx->mappings->{$key} &&
+ $active_ctx->mappings->{$key}->reverse) {
+ if(property_exists($rval, '@reverse')) {
+ $reverse_map = $rval->{'@reverse'};
+ } else {
+ $reverse_map = $rval->{'@reverse'} = new stdClass();
+ }
+ $expanded_value = self::arrayify($expanded_value);
+ foreach($expanded_value as $item) {
+ if(self::_isValue($item) || self::_isList($item)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@reverse" value must not be a ' +
+ '@value or an @list.', 'jsonld.SyntaxError',
+ 'invalid reverse property value',
+ array('value' => $expanded_value));
+ }
+ self::addValue(
+ $reverse_map, $expanded_property, $item,
+ array('propertyIsArray' => true));
+ }
+ continue;
+ }
+
+ // add value for property
+ // use an array except for certain keywords
+ $use_array = (!in_array(
+ $expanded_property, array(
+ '@index', '@id', '@type', '@value', '@language')));
+ self::addValue(
+ $rval, $expanded_property, $expanded_value,
+ array('propertyIsArray' => $use_array));
+ }
+
+ // get property count on expanded output
+ $keys = array_keys((array)$rval);
+ $count = count($keys);
+
+ // @value must only have @language or @type
+ if(property_exists($rval, '@value')) {
+ // @value must only have @language or @type
+ if(property_exists($rval, '@type') &&
+ property_exists($rval, '@language')) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; an element containing "@value" may not ' .
+ 'contain both "@type" and "@language".',
+ 'jsonld.SyntaxError', 'invalid value object',
+ array('element' => $rval));
+ }
+ $valid_count = $count - 1;
+ if(property_exists($rval, '@type')) {
+ $valid_count -= 1;
+ }
+ if(property_exists($rval, '@index')) {
+ $valid_count -= 1;
+ }
+ if(property_exists($rval, '@language')) {
+ $valid_count -= 1;
+ }
+ if($valid_count !== 0) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; an element containing "@value" may only ' .
+ 'have an "@index" property and at most one other property ' .
+ 'which can be "@type" or "@language".',
+ 'jsonld.SyntaxError', 'invalid value object',
+ array('element' => $rval));
+ }
+ // drop null @values
+ if($rval->{'@value'} === null) {
+ $rval = null;
+ } else if(property_exists($rval, '@language') &&
+ !is_string($rval->{'@value'})) {
+ // if @language is present, @value must be a string
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; only strings may be language-tagged.',
+ 'jsonld.SyntaxError', 'invalid language-tagged value',
+ array('element' => $rval));
+ } else if(property_exists($rval, '@type') &&
+ (!self::_isAbsoluteIri($rval->{'@type'}) ||
+ strpos($rval->{'@type'}, '_:') === 0)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; an element containing "@value" ' .
+ 'and "@type" must have an absolute IRI for the value ' .
+ 'of "@type".', 'jsonld.SyntaxError', 'invalid typed value',
+ array('element' => $rval));
+ }
+ } else if(property_exists($rval, '@type') && !is_array($rval->{'@type'})) {
+ // convert @type to an array
+ $rval->{'@type'} = array($rval->{'@type'});
+ } else if(property_exists($rval, '@set') ||
+ property_exists($rval, '@list')) {
+ // handle @set and @list
+ if($count > 1 && !($count === 2 && property_exists($rval, '@index'))) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; if an element has the property "@set" ' .
+ 'or "@list", then it can have at most one other property that is ' .
+ '"@index".', 'jsonld.SyntaxError', 'invalid set or list object',
+ array('element' => $rval));
+ }
+ // optimize away @set
+ if(property_exists($rval, '@set')) {
+ $rval = $rval->{'@set'};
+ $keys = array_keys((array)$rval);
+ $count = count($keys);
+ }
+ } else if($count === 1 && property_exists($rval, '@language')) {
+ // drop objects with only @language
+ $rval = null;
+ }
+
+ // drop certain top-level objects that do not occur in lists
+ if(is_object($rval) &&
+ !$options['keepFreeFloatingNodes'] && !$inside_list &&
+ ($active_property === null || $expanded_active_property === '@graph')) {
+ // drop empty object or top-level @value/@list, or object with only @id
+ if($count === 0 || property_exists($rval, '@value') ||
+ property_exists($rval, '@list') ||
+ ($count === 1 && property_exists($rval, '@id'))) {
+ $rval = null;
+ }
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Performs JSON-LD flattening.
+ *
+ * @param array $input the expanded JSON-LD to flatten.
+ *
+ * @return array the flattened output.
+ */
+ protected function _flatten($input) {
+ // produce a map of all subjects and name each bnode
+ $namer = new UniqueNamer('_:b');
+ $graphs = (object)array('@default' => new stdClass());
+ $this->_createNodeMap($input, $graphs, '@default', $namer);
+
+ // add all non-default graphs to default graph
+ $default_graph = $graphs->{'@default'};
+ $graph_names = array_keys((array)$graphs);
+ foreach($graph_names as $graph_name) {
+ if($graph_name === '@default') {
+ continue;
+ }
+ $node_map = $graphs->{$graph_name};
+ if(!property_exists($default_graph, $graph_name)) {
+ $default_graph->{$graph_name} = (object)array(
+ '@id' => $graph_name, '@graph' => array());
+ }
+ $subject = $default_graph->{$graph_name};
+ if(!property_exists($subject, '@graph')) {
+ $subject->{'@graph'} = array();
+ }
+ $ids = array_keys((array)$node_map);
+ sort($ids);
+ foreach($ids as $id) {
+ $node = $node_map->{$id};
+ // only add full subjects
+ if(!self::_isSubjectReference($node)) {
+ $subject->{'@graph'}[] = $node;
+ }
+ }
+ }
+
+ // produce flattened output
+ $flattened = array();
+ $keys = array_keys((array)$default_graph);
+ sort($keys);
+ foreach($keys as $key) {
+ $node = $default_graph->{$key};
+ // only add full subjects to top-level
+ if(!self::_isSubjectReference($node)) {
+ $flattened[] = $node;
+ }
+ }
+ return $flattened;
+ }
+
+ /**
+ * Performs JSON-LD framing.
+ *
+ * @param array $input the expanded JSON-LD to frame.
+ * @param array $frame the expanded JSON-LD frame to use.
+ * @param assoc $options the framing options.
+ *
+ * @return array the framed output.
+ */
+ protected function _frame($input, $frame, $options) {
+ // create framing state
+ $state = (object)array(
+ 'options' => $options,
+ 'graphs' => (object)array(
+ '@default' => new stdClass(),
+ '@merged' => new stdClass()),
+ 'subjectStack' => array(),
+ 'link' => new stdClass());
+
+ // produce a map of all graphs and name each bnode
+ // FIXME: currently uses subjects from @merged graph only
+ $namer = new UniqueNamer('_:b');
+ $this->_createNodeMap($input, $state->graphs, '@merged', $namer);
+ $state->subjects = $state->graphs->{'@merged'};
+
+ // frame the subjects
+ $framed = new ArrayObject();
+ $keys = array_keys((array)$state->subjects);
+ sort($keys);
+ $this->_matchFrame($state, $keys, $frame, $framed, null);
+ return (array)$framed;
+ }
+
+ /**
+ * Performs normalization on the given RDF dataset.
+ *
+ * @param stdClass $dataset the RDF dataset to normalize.
+ * @param assoc $options the normalization options.
+ *
+ * @return mixed the normalized output.
+ */
+ protected function _normalize($dataset, $options) {
+ // create quads and map bnodes to their associated quads
+ $quads = array();
+ $bnodes = new stdClass();
+ foreach($dataset as $graph_name => $triples) {
+ if($graph_name === '@default') {
+ $graph_name = null;
+ }
+ foreach($triples as $triple) {
+ $quad = $triple;
+ if($graph_name !== null) {
+ if(strpos($graph_name, '_:') === 0) {
+ $quad->name = (object)array(
+ 'type' => 'blank node', 'value' => $graph_name);
+ } else {
+ $quad->name = (object)array(
+ 'type' => 'IRI', 'value' => $graph_name);
+ }
+ }
+ $quads[] = $quad;
+
+ foreach(array('subject', 'object', 'name') as $attr) {
+ if(property_exists($quad, $attr) &&
+ $quad->{$attr}->type === 'blank node') {
+ $id = $quad->{$attr}->value;
+ if(property_exists($bnodes, $id)) {
+ $bnodes->{$id}->quads[] = $quad;
+ } else {
+ $bnodes->{$id} = (object)array('quads' => array($quad));
+ }
+ }
+ }
+ }
+ }
+
+ // mapping complete, start canonical naming
+ $namer = new UniqueNamer('_:c14n');
+
+ // continue to hash bnode quads while bnodes are assigned names
+ $unnamed = null;
+ $nextUnnamed = array_keys((array)$bnodes);
+ $duplicates = null;
+ do {
+ $unnamed = $nextUnnamed;
+ $nextUnnamed = array();
+ $duplicates = new stdClass();
+ $unique = new stdClass();
+ foreach($unnamed as $bnode) {
+ // hash quads for each unnamed bnode
+ $hash = $this->_hashQuads($bnode, $bnodes, $namer);
+
+ // store hash as unique or a duplicate
+ if(property_exists($duplicates, $hash)) {
+ $duplicates->{$hash}[] = $bnode;
+ $nextUnnamed[] = $bnode;
+ } else if(property_exists($unique, $hash)) {
+ $duplicates->{$hash} = array($unique->{$hash}, $bnode);
+ $nextUnnamed[] = $unique->{$hash};
+ $nextUnnamed[] = $bnode;
+ unset($unique->{$hash});
+ } else {
+ $unique->{$hash} = $bnode;
+ }
+ }
+
+ // name unique bnodes in sorted hash order
+ $hashes = array_keys((array)$unique);
+ sort($hashes);
+ foreach($hashes as $hash) {
+ $namer->getName($unique->{$hash});
+ }
+ }
+ while(count($unnamed) > count($nextUnnamed));
+
+ // enumerate duplicate hash groups in sorted order
+ $hashes = array_keys((array)$duplicates);
+ sort($hashes);
+ foreach($hashes as $hash) {
+ // process group
+ $group = $duplicates->{$hash};
+ $results = array();
+ foreach($group as $bnode) {
+ // skip already-named bnodes
+ if($namer->isNamed($bnode)) {
+ continue;
+ }
+
+ // hash bnode paths
+ $path_namer = new UniqueNamer('_:b');
+ $path_namer->getName($bnode);
+ $results[] = $this->_hashPaths($bnode, $bnodes, $namer, $path_namer);
+ }
+
+ // name bnodes in hash order
+ usort($results, function($a, $b) {
+ $a = $a->hash;
+ $b = $b->hash;
+ return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
+ });
+ foreach($results as $result) {
+ // name all bnodes in path namer in key-entry order
+ foreach($result->pathNamer->order as $bnode) {
+ $namer->getName($bnode);
+ }
+ }
+ }
+
+ // create normalized array
+ $normalized = array();
+
+ /* Note: At this point all bnodes in the set of RDF quads have been
+ assigned canonical names, which have been stored in the 'namer' object.
+ Here each quad is updated by assigning each of its bnodes its new name
+ via the 'namer' object. */
+
+ // update bnode names in each quad and serialize
+ foreach($quads as $quad) {
+ foreach(array('subject', 'object', 'name') as $attr) {
+ if(property_exists($quad, $attr) &&
+ $quad->{$attr}->type === 'blank node' &&
+ strpos($quad->{$attr}->value, '_:c14n') !== 0) {
+ $quad->{$attr}->value = $namer->getName($quad->{$attr}->value);
+ }
+ }
+ $normalized[] = $this->toNQuad($quad, property_exists($quad, 'name') ?
+ $quad->name->value : null);
+ }
+
+ // sort normalized output
+ sort($normalized);
+
+ // handle output format
+ if(isset($options['format']) && $options['format']) {
+ if($options['format'] === 'application/nquads') {
+ return implode($normalized);
+ }
+ throw new JsonLdException(
+ 'Unknown output format.',
+ 'jsonld.UnknownFormat', null, array('format' => $options['format']));
+ }
+
+ // return RDF dataset
+ return $this->parseNQuads(implode($normalized));
+ }
+
+ /**
+ * Converts an RDF dataset to JSON-LD.
+ *
+ * @param stdClass $dataset the RDF dataset.
+ * @param assoc $options the RDF serialization options.
+ *
+ * @return array the JSON-LD output.
+ */
+ protected function _fromRDF($dataset, $options) {
+ $default_graph = new stdClass();
+ $graph_map = (object)array('@default' => $default_graph);
+ $referenced_once = (object)array();
+
+ foreach($dataset as $name => $graph) {
+ if(!property_exists($graph_map, $name)) {
+ $graph_map->{$name} = new stdClass();
+ }
+ if($name !== '@default' && !property_exists($default_graph, $name)) {
+ $default_graph->{$name} = (object)array('@id' => $name);
+ }
+ $node_map = $graph_map->{$name};
+ foreach($graph as $triple) {
+ // get subject, predicate, object
+ $s = $triple->subject->value;
+ $p = $triple->predicate->value;
+ $o = $triple->object;
+
+ if(!property_exists($node_map, $s)) {
+ $node_map->{$s} = (object)array('@id' => $s);
+ }
+ $node = $node_map->{$s};
+
+ $object_is_id = ($o->type === 'IRI' || $o->type === 'blank node');
+ if($object_is_id && !property_exists($node_map, $o->value)) {
+ $node_map->{$o->value} = (object)array('@id' => $o->value);
+ }
+
+ if($p === self::RDF_TYPE && !$options['useRdfType'] && $object_is_id) {
+ self::addValue(
+ $node, '@type', $o->value, array('propertyIsArray' => true));
+ continue;
+ }
+
+ $value = self::_RDFToObject($o, $options['useNativeTypes']);
+ self::addValue($node, $p, $value, array('propertyIsArray' => true));
+
+ // object may be an RDF list/partial list node but we can't know
+ // easily until all triples are read
+ if($object_is_id) {
+ if($o->value === self::RDF_NIL) {
+ $object = $node_map->{$o->value};
+ if(!property_exists($object, 'usages')) {
+ $object->usages = array();
+ }
+ $object->usages[] = (object)array(
+ 'node' => $node,
+ 'property' => $p,
+ 'value' => $value);
+ } else if(property_exists($referenced_once, $o->value)) {
+ // object referenced more than once
+ $referenced_once->{$o->value} = false;
+ } else {
+ // track single reference
+ $referenced_once->{$o->value} = (object)array(
+ 'node' => $node,
+ 'property' => $p,
+ 'value' => $value);
+ }
+ }
+ }
+ }
+
+ // convert linked lists to @list arrays
+ foreach($graph_map as $name => $graph_object) {
+ // no @lists to be converted, continue
+ if(!property_exists($graph_object, self::RDF_NIL)) {
+ continue;
+ }
+
+ // iterate backwards through each RDF list
+ $nil = $graph_object->{self::RDF_NIL};
+ foreach($nil->usages as $usage) {
+ $node = $usage->node;
+ $property = $usage->property;
+ $head = $usage->value;
+ $list = array();
+ $list_nodes = array();
+
+ // ensure node is a well-formed list node; it must:
+ // 1. Be referenced only once.
+ // 2. Have an array for rdf:first that has 1 item.
+ // 3. Have an array for rdf:rest that has 1 item.
+ // 4. Have no keys other than: @id, rdf:first, rdf:rest, and,
+ // optionally, @type where the value is rdf:List.
+ $node_key_count = count(array_keys((array)$node));
+ while($property === self::RDF_REST &&
+ property_exists($referenced_once, $node->{'@id'}) &&
+ is_object($referenced_once->{$node->{'@id'}}) &&
+ property_exists($node, self::RDF_FIRST) &&
+ property_exists($node, self::RDF_REST) &&
+ is_array($node->{self::RDF_FIRST}) &&
+ is_array($node->{self::RDF_REST}) &&
+ count($node->{self::RDF_FIRST}) === 1 &&
+ count($node->{self::RDF_REST}) === 1 &&
+ ($node_key_count === 3 || ($node_key_count === 4 &&
+ property_exists($node, '@type') && is_array($node->{'@type'}) &&
+ count($node->{'@type'}) === 1 &&
+ $node->{'@type'}[0] === self::RDF_LIST))) {
+ $list[] = $node->{self::RDF_FIRST}[0];
+ $list_nodes[] = $node->{'@id'};
+
+ // get next node, moving backwards through list
+ $usage = $referenced_once->{$node->{'@id'}};
+ $node = $usage->node;
+ $property = $usage->property;
+ $head = $usage->value;
+ $node_key_count = count(array_keys((array)$node));
+
+ // if node is not a blank node, then list head found
+ if(strpos($node->{'@id'}, '_:') !== 0) {
+ break;
+ }
+ }
+
+ // list is nested in another list
+ if($property === self::RDF_FIRST) {
+ // empty list
+ if($node->{'@id'} === self::RDF_NIL) {
+ // can't convert rdf:nil to a @list object because it would
+ // result in a list of lists which isn't supported
+ continue;
+ }
+
+ // preserve list head
+ $head = $graph_object->{$head->{'@id'}}->{self::RDF_REST}[0];
+ array_pop($list);
+ array_pop($list_nodes);
+ }
+
+ // transform list into @list object
+ unset($head->{'@id'});
+ $head->{'@list'} = array_reverse($list);
+ foreach($list_nodes as $list_node) {
+ unset($graph_object->{$list_node});
+ }
+ }
+
+ unset($nil->usages);
+ }
+
+ $result = array();
+ $subjects = array_keys((array)$default_graph);
+ sort($subjects);
+ foreach($subjects as $subject) {
+ $node = $default_graph->{$subject};
+ if(property_exists($graph_map, $subject)) {
+ $node->{'@graph'} = array();
+ $graph_object = $graph_map->{$subject};
+ $subjects_ = array_keys((array)$graph_object);
+ sort($subjects_);
+ foreach($subjects_ as $subject_) {
+ $node_ = $graph_object->{$subject_};
+ // only add full subjects to top-level
+ if(!self::_isSubjectReference($node_)) {
+ $node->{'@graph'}[] = $node_;
+ }
+ }
+ }
+ // only add full subjects to top-level
+ if(!self::_isSubjectReference($node)) {
+ $result[] = $node;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Processes a local context and returns a new active context.
+ *
+ * @param stdClass $active_ctx the current active context.
+ * @param mixed $local_ctx the local context to process.
+ * @param assoc $options the context processing options.
+ *
+ * @return stdClass the new active context.
+ */
+ protected function _processContext($active_ctx, $local_ctx, $options) {
+ global $jsonld_cache;
+
+ // normalize local context to an array
+ if(is_object($local_ctx) && property_exists($local_ctx, '@context') &&
+ is_array($local_ctx->{'@context'})) {
+ $local_ctx = $local_ctx->{'@context'};
+ }
+ $ctxs = self::arrayify($local_ctx);
+
+ // no contexts in array, clone existing context
+ if(count($ctxs) === 0) {
+ return self::_cloneActiveContext($active_ctx);
+ }
+
+ // process each context in order, update active context
+ // on each iteration to ensure proper caching
+ $rval = $active_ctx;
+ foreach($ctxs as $ctx) {
+ // reset to initial context
+ if($ctx === null) {
+ $rval = $active_ctx = $this->_getInitialContext($options);
+ continue;
+ }
+
+ // dereference @context key if present
+ if(is_object($ctx) && property_exists($ctx, '@context')) {
+ $ctx = $ctx->{'@context'};
+ }
+
+ // context must be an object by now, all URLs retrieved before this call
+ if(!is_object($ctx)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context must be an object.',
+ 'jsonld.SyntaxError', 'invalid local context',
+ array('context' => $ctx));
+ }
+
+ // get context from cache if available
+ if(property_exists($jsonld_cache, 'activeCtx')) {
+ $cached = $jsonld_cache->activeCtx->get($active_ctx, $ctx);
+ if($cached) {
+ $rval = $active_ctx = $cached;
+ $must_clone = true;
+ continue;
+ }
+ }
+
+ // update active context and clone new one before updating
+ $active_ctx = $rval;
+ $rval = self::_cloneActiveContext($rval);
+
+ // define context mappings for keys in local context
+ $defined = new stdClass();
+
+ // handle @base
+ if(property_exists($ctx, '@base')) {
+ $base = $ctx->{'@base'};
+ if($base === null) {
+ $base = null;
+ } else if(!is_string($base)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; the value of "@base" in a ' .
+ '@context must be a string or null.',
+ 'jsonld.SyntaxError', 'invalid base IRI', array('context' => $ctx));
+ } else if($base !== '' && !self::_isAbsoluteIri($base)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; the value of "@base" in a ' .
+ '@context must be an absolute IRI or the empty string.',
+ 'jsonld.SyntaxError', 'invalid base IRI', array('context' => $ctx));
+ }
+ if($base !== null) {
+ $base = jsonld_parse_url($base);
+ }
+ $rval->{'@base'} = $base;
+ $defined->{'@base'} = true;
+ }
+
+ // handle @vocab
+ if(property_exists($ctx, '@vocab')) {
+ $value = $ctx->{'@vocab'};
+ if($value === null) {
+ unset($rval->{'@vocab'});
+ } else if(!is_string($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; the value of "@vocab" in a ' .
+ '@context must be a string or null.',
+ 'jsonld.SyntaxError', 'invalid vocab mapping',
+ array('context' => $ctx));
+ } else if(!self::_isAbsoluteIri($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; the value of "@vocab" in a ' .
+ '@context must be an absolute IRI.',
+ 'jsonld.SyntaxError', 'invalid vocab mapping',
+ array('context' => $ctx));
+ } else {
+ $rval->{'@vocab'} = $value;
+ }
+ $defined->{'@vocab'} = true;
+ }
+
+ // handle @language
+ if(property_exists($ctx, '@language')) {
+ $value = $ctx->{'@language'};
+ if($value === null) {
+ unset($rval->{'@language'});
+ } else if(!is_string($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; the value of "@language" in a ' .
+ '@context must be a string or null.',
+ 'jsonld.SyntaxError', 'invalid default language',
+ array('context' => $ctx));
+ } else {
+ $rval->{'@language'} = strtolower($value);
+ }
+ $defined->{'@language'} = true;
+ }
+
+ // process all other keys
+ foreach($ctx as $k => $v) {
+ $this->_createTermDefinition($rval, $ctx, $k, $defined);
+ }
+
+ // cache result
+ if(property_exists($jsonld_cache, 'activeCtx')) {
+ $jsonld_cache->activeCtx->set($active_ctx, $ctx, $rval);
+ }
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Expands a language map.
+ *
+ * @param stdClass $language_map the language map to expand.
+ *
+ * @return array the expanded language map.
+ */
+ protected function _expandLanguageMap($language_map) {
+ $rval = array();
+ $keys = array_keys((array)$language_map);
+ sort($keys);
+ foreach($keys as $key) {
+ $values = $language_map->{$key};
+ $values = self::arrayify($values);
+ foreach($values as $item) {
+ if($item === null) {
+ continue;
+ }
+ if(!is_string($item)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; language map values must be strings.',
+ 'jsonld.SyntaxError', 'invalid language map value',
+ array('languageMap', $language_map));
+ }
+ $rval[] = (object)array(
+ '@value' => $item,
+ '@language' => strtolower($key));
+ }
+ }
+ return $rval;
+ }
+
+ /**
+ * Labels the blank nodes in the given value using the given UniqueNamer.
+ *
+ * @param UniqueNamer $namer the UniqueNamer to use.
+ * @param mixed $element the element with blank nodes to rename.
+ *
+ * @return mixed the element.
+ */
+ public function _labelBlankNodes($namer, $element) {
+ if(is_array($element)) {
+ $length = count($element);
+ for($i = 0; $i < $length; ++$i) {
+ $element[$i] = $this->_labelBlankNodes($namer, $element[$i]);
+ }
+ } else if(self::_isList($element)) {
+ $element->{'@list'} = $this->_labelBlankNodes(
+ $namer, $element->{'@list'});
+ } else if(is_object($element)) {
+ // rename blank node
+ if(self::_isBlankNode($element)) {
+ $name = null;
+ if(property_exists($element, '@id')) {
+ $name = $element->{'@id'};
+ }
+ $element->{'@id'} = $namer->getName($name);
+ }
+
+ // recursively apply to all keys
+ $keys = array_keys((array)$element);
+ sort($keys);
+ foreach($keys as $key) {
+ if($key !== '@id') {
+ $element->{$key} = $this->_labelBlankNodes($namer, $element->{$key});
+ }
+ }
+ }
+
+ return $element;
+ }
+
+ /**
+ * Expands the given value by using the coercion and keyword rules in the
+ * given context.
+ *
+ * @param stdClass $active_ctx the active context to use.
+ * @param string $active_property the property the value is associated with.
+ * @param mixed $value the value to expand.
+ *
+ * @return mixed the expanded value.
+ */
+ protected function _expandValue($active_ctx, $active_property, $value) {
+ // nothing to expand
+ if($value === null) {
+ return null;
+ }
+
+ // special-case expand @id and @type (skips '@id' expansion)
+ $expanded_property = $this->_expandIri(
+ $active_ctx, $active_property, array('vocab' => true));
+ if($expanded_property === '@id') {
+ return $this->_expandIri($active_ctx, $value, array('base' => true));
+ } else if($expanded_property === '@type') {
+ return $this->_expandIri(
+ $active_ctx, $value, array('vocab' => true, 'base' => true));
+ }
+
+ // get type definition from context
+ $type = self::getContextValue($active_ctx, $active_property, '@type');
+
+ // do @id expansion (automatic for @graph)
+ if($type === '@id' || ($expanded_property === '@graph' &&
+ is_string($value))) {
+ return (object)array('@id' => $this->_expandIri(
+ $active_ctx, $value, array('base' => true)));
+ }
+ // do @id expansion w/vocab
+ if($type === '@vocab') {
+ return (object)array('@id' => $this->_expandIri(
+ $active_ctx, $value, array('vocab' => true, 'base' => true)));
+ }
+
+ // do not expand keyword values
+ if(self::_isKeyword($expanded_property)) {
+ return $value;
+ }
+
+ $rval = new stdClass();
+
+ // other type
+ if($type !== null) {
+ $rval->{'@type'} = $type;
+ } else if(is_string($value)) {
+ // check for language tagging for strings
+ $language = self::getContextValue(
+ $active_ctx, $active_property, '@language');
+ if($language !== null) {
+ $rval->{'@language'} = $language;
+ }
+ }
+ $rval->{'@value'} = $value;
+
+ return $rval;
+ }
+
+ /**
+ * Creates an array of RDF triples for the given graph.
+ *
+ * @param stdClass $graph the graph to create RDF triples for.
+ * @param UniqueNamer $namer for assigning bnode names.
+ * @param assoc $options the RDF serialization options.
+ *
+ * @return array the array of RDF triples for the given graph.
+ */
+ protected function _graphToRDF($graph, $namer, $options) {
+ $rval = array();
+
+ $ids = array_keys((array)$graph);
+ sort($ids);
+ foreach($ids as $id) {
+ $node = $graph->{$id};
+ if($id === '"') {
+ $id = '';
+ }
+ $properties = array_keys((array)$node);
+ sort($properties);
+ foreach($properties as $property) {
+ $items = $node->{$property};
+ if($property === '@type') {
+ $property = self::RDF_TYPE;
+ } else if(self::_isKeyword($property)) {
+ continue;
+ }
+
+ foreach($items as $item) {
+ // skip relative IRI subjects and predicates
+ if(!(self::_isAbsoluteIri($id) && self::_isAbsoluteIri($property))) {
+ continue;
+ }
+
+ // RDF subject
+ $subject = new stdClass();
+ $subject->type = (strpos($id, '_:') === 0) ? 'blank node' : 'IRI';
+ $subject->value = $id;
+
+ // RDF predicate
+ $predicate = new stdClass();
+ $predicate->type = (strpos($property, '_:') === 0 ?
+ 'blank node' : 'IRI');
+ $predicate->value = $property;
+
+ // skip bnode predicates unless producing generalized RDF
+ if($predicate->type === 'blank node' &&
+ !$options['produceGeneralizedRdf']) {
+ continue;
+ }
+
+ if(self::_isList($item)) {
+ // convert @list to triples
+ $this->_listToRDF(
+ $item->{'@list'}, $namer, $subject, $predicate, $rval);
+ } else {
+ // convert value or node object to triple
+ $object = $this->_objectToRDF($item);
+ // skip null objects (they are relative IRIs)
+ if($object) {
+ $rval[] = (object)array(
+ 'subject' => $subject,
+ 'predicate' => $predicate,
+ 'object' => $object);
+ }
+ }
+ }
+ }
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Converts a @list value into linked list of blank node RDF triples
+ * (an RDF collection).
+ *
+ * @param array $list the @list value.
+ * @param UniqueNamer $namer for assigning blank node names.
+ * @param stdClass $subject the subject for the head of the list.
+ * @param stdClass $predicate the predicate for the head of the list.
+ * @param &array $triples the array of triples to append to.
+ */
+ protected function _listToRDF(
+ $list, $namer, $subject, $predicate, &$triples) {
+ $first = (object)array('type' => 'IRI', 'value' => self::RDF_FIRST);
+ $rest = (object)array('type' => 'IRI', 'value' => self::RDF_REST);
+ $nil = (object)array('type' => 'IRI', 'value' => self::RDF_NIL);
+
+ foreach($list as $item) {
+ $blank_node = (object)array(
+ 'type' => 'blank node', 'value' => $namer->getName());
+ $triples[] = (object)array(
+ 'subject' => $subject,
+ 'predicate' => $predicate,
+ 'object' => $blank_node);
+
+ $subject = $blank_node;
+ $predicate = $first;
+ $object = $this->_objectToRDF($item);
+ // skip null objects (they are relative IRIs)
+ if($object) {
+ $triples[] = (object)array(
+ 'subject' => $subject,
+ 'predicate' => $predicate,
+ 'object' => $object);
+ }
+
+ $predicate = $rest;
+ }
+
+ $triples[] = (object)array(
+ 'subject' => $subject, 'predicate' => $predicate, 'object' => $nil);
+ }
+
+ /**
+ * Converts a JSON-LD value object to an RDF literal or a JSON-LD string or
+ * node object to an RDF resource.
+ *
+ * @param mixed $item the JSON-LD value or node object.
+ *
+ * @return stdClass the RDF literal or RDF resource.
+ */
+ protected function _objectToRDF($item) {
+ $object = new stdClass();
+
+ if(self::_isValue($item)) {
+ $object->type = 'literal';
+ $value = $item->{'@value'};
+ $datatype = property_exists($item, '@type') ? $item->{'@type'} : null;
+
+ // convert to XSD datatypes as appropriate
+ if(is_bool($value)) {
+ $object->value = ($value ? 'true' : 'false');
+ $object->datatype = $datatype ? $datatype : self::XSD_BOOLEAN;
+ } else if(is_double($value) || $datatype == self::XSD_DOUBLE) {
+ // canonical double representation
+ $object->value = preg_replace(
+ '/(\d)0*E\+?/', '$1E', sprintf('%1.15E', $value));
+ $object->datatype = $datatype ? $datatype : self::XSD_DOUBLE;
+ } else if(is_integer($value)) {
+ $object->value = strval($value);
+ $object->datatype = $datatype ? $datatype : self::XSD_INTEGER;
+ } else if(property_exists($item, '@language')) {
+ $object->value = $value;
+ $object->datatype = $datatype ? $datatype : self::RDF_LANGSTRING;
+ $object->language = $item->{'@language'};
+ } else {
+ $object->value = $value;
+ $object->datatype = $datatype ? $datatype : self::XSD_STRING;
+ }
+ } else {
+ // convert string/node object to RDF
+ $id = is_object($item) ? $item->{'@id'} : $item;
+ $object->type = (strpos($id, '_:') === 0) ? 'blank node' : 'IRI';
+ $object->value = $id;
+ }
+
+ // skip relative IRIs
+ if($object->type === 'IRI' && !self::_isAbsoluteIri($object->value)) {
+ return null;
+ }
+
+ return $object;
+ }
+
+ /**
+ * Converts an RDF triple object to a JSON-LD object.
+ *
+ * @param stdClass $o the RDF triple object to convert.
+ * @param bool $use_native_types true to output native types, false not to.
+ *
+ * @return stdClass the JSON-LD object.
+ */
+ protected function _RDFToObject($o, $use_native_types) {
+ // convert IRI/blank node object to JSON-LD
+ if($o->type === 'IRI' || $o->type === 'blank node') {
+ return (object)array('@id' => $o->value);
+ }
+
+ // convert literal object to JSON-LD
+ $rval = (object)array('@value' => $o->value);
+
+ if(property_exists($o, 'language')) {
+ // add language
+ $rval->{'@language'} = $o->language;
+ } else {
+ // add datatype
+ $type = $o->datatype;
+ // use native types for certain xsd types
+ if($use_native_types) {
+ if($type === self::XSD_BOOLEAN) {
+ if($rval->{'@value'} === 'true') {
+ $rval->{'@value'} = true;
+ } else if($rval->{'@value'} === 'false') {
+ $rval->{'@value'} = false;
+ }
+ } else if(is_numeric($rval->{'@value'})) {
+ if($type === self::XSD_INTEGER) {
+ $i = intval($rval->{'@value'});
+ if(strval($i) === $rval->{'@value'}) {
+ $rval->{'@value'} = $i;
+ }
+ } else if($type === self::XSD_DOUBLE) {
+ $rval->{'@value'} = doubleval($rval->{'@value'});
+ }
+ }
+ // do not add native type
+ if(!in_array($type, array(
+ self::XSD_BOOLEAN, self::XSD_INTEGER, self::XSD_DOUBLE,
+ self::XSD_STRING))) {
+ $rval->{'@type'} = $type;
+ }
+ } else if($type !== self::XSD_STRING) {
+ $rval->{'@type'} = $type;
+ }
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Recursively flattens the subjects in the given JSON-LD expanded input
+ * into a node map.
+ *
+ * @param mixed $input the JSON-LD expanded input.
+ * @param stdClass $graphs a map of graph name to subject map.
+ * @param string $graph the name of the current graph.
+ * @param UniqueNamer $namer the blank node namer.
+ * @param mixed $name the name assigned to the current input if it is a bnode.
+ * @param mixed $list the list to append to, null for none.
+ */
+ protected function _createNodeMap(
+ $input, $graphs, $graph, $namer, $name=null, $list=null) {
+ // recurse through array
+ if(is_array($input)) {
+ foreach($input as $e) {
+ $this->_createNodeMap($e, $graphs, $graph, $namer, null, $list);
+ }
+ return;
+ }
+
+ // add non-object to list
+ if(!is_object($input)) {
+ if($list !== null) {
+ $list[] = $input;
+ }
+ return;
+ }
+
+ // add values to list
+ if(self::_isValue($input)) {
+ if(property_exists($input, '@type')) {
+ $type = $input->{'@type'};
+ // rename @type blank node
+ if(strpos($type, '_:') === 0) {
+ $type = $input->{'@type'} = $namer->getName($type);
+ }
+ }
+ if($list !== null) {
+ $list[] = $input;
+ }
+ return;
+ }
+
+ // Note: At this point, input must be a subject.
+
+ // spec requires @type to be named first, so assign names early
+ if(property_exists($input, '@type')) {
+ foreach($input->{'@type'} as $type) {
+ if(strpos($type, '_:') === 0) {
+ $namer->getName($type);
+ }
+ }
+ }
+
+ // get name for subject
+ if($name === null) {
+ if(property_exists($input, '@id')) {
+ $name = $input->{'@id'};
+ }
+ if(self::_isBlankNode($input)) {
+ $name = $namer->getName($name);
+ }
+ }
+
+ // add subject reference to list
+ if($list !== null) {
+ $list[] = (object)array('@id' => $name);
+ }
+
+ // create new subject or merge into existing one
+ if(!property_exists($graphs, $graph)) {
+ $graphs->{$graph} = new stdClass();
+ }
+ $subjects = $graphs->{$graph};
+ if(!property_exists($subjects, $name)) {
+ if($name === '') {
+ $subjects->{'"'} = new stdClass();
+ } else {
+ $subjects->{$name} = new stdClass();
+ }
+ }
+ if($name === '') {
+ $subject = $subjects->{'"'};
+ } else {
+ $subject = $subjects->{$name};
+ }
+ $subject->{'@id'} = $name;
+ $properties = array_keys((array)$input);
+ sort($properties);
+ foreach($properties as $property) {
+ // skip @id
+ if($property === '@id') {
+ continue;
+ }
+
+ // handle reverse properties
+ if($property === '@reverse') {
+ $referenced_node = (object)array('@id' => $name);
+ $reverse_map = $input->{'@reverse'};
+ foreach($reverse_map as $reverse_property => $items) {
+ foreach($items as $item) {
+ $item_name = null;
+ if(property_exists($item, '@id')) {
+ $item_name = $item->{'@id'};
+ }
+ if(self::_isBlankNode($item)) {
+ $item_name = $namer->getName($item_name);
+ }
+ $this->_createNodeMap($item, $graphs, $graph, $namer, $item_name);
+ if($item_name === '') {
+ $item_name = '"';
+ }
+ self::addValue(
+ $subjects->{$item_name}, $reverse_property, $referenced_node,
+ array('propertyIsArray' => true, 'allowDuplicate' => false));
+ }
+ }
+ continue;
+ }
+
+ // recurse into graph
+ if($property === '@graph') {
+ // add graph subjects map entry
+ if(!property_exists($graphs, $name)) {
+ // FIXME: temporary hack to avoid empty property bug
+ if(!$name) {
+ $name = '"';
+ }
+ $graphs->{$name} = new stdClass();
+ }
+ $g = ($graph === '@merged') ? $graph : $name;
+ $this->_createNodeMap(
+ $input->{$property}, $graphs, $g, $namer, null, null);
+ continue;
+ }
+
+ // copy non-@type keywords
+ if($property !== '@type' && self::_isKeyword($property)) {
+ if($property === '@index' && property_exists($subject, '@index') &&
+ ($input->{'@index'} !== $subject->{'@index'} ||
+ $input->{'@index'}->{'@id'} !== $subject->{'@index'}->{'@id'})) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; conflicting @index property detected.',
+ 'jsonld.SyntaxError', 'conflicting indexes',
+ array('subject' => $subject));
+ }
+ $subject->{$property} = $input->{$property};
+ continue;
+ }
+
+ // iterate over objects
+ $objects = $input->{$property};
+
+ // if property is a bnode, assign it a new id
+ if(strpos($property, '_:') === 0) {
+ $property = $namer->getName($property);
+ }
+
+ // ensure property is added for empty arrays
+ if(count($objects) === 0) {
+ self::addValue(
+ $subject, $property, array(), array('propertyIsArray' => true));
+ continue;
+ }
+ foreach($objects as $o) {
+ if($property === '@type') {
+ // rename @type blank nodes
+ $o = (strpos($o, '_:') === 0) ? $namer->getName($o) : $o;
+ }
+
+ // handle embedded subject or subject reference
+ if(self::_isSubject($o) || self::_isSubjectReference($o)) {
+ // rename blank node @id
+ $id = property_exists($o, '@id') ? $o->{'@id'} : null;
+ if(self::_isBlankNode($o)) {
+ $id = $namer->getName($id);
+ }
+
+ // add reference and recurse
+ self::addValue(
+ $subject, $property, (object)array('@id' => $id),
+ array('propertyIsArray' => true, 'allowDuplicate' => false));
+ $this->_createNodeMap($o, $graphs, $graph, $namer, $id, null);
+ } else if(self::_isList($o)) {
+ // handle @list
+ $_list = new ArrayObject();
+ $this->_createNodeMap(
+ $o->{'@list'}, $graphs, $graph, $namer, $name, $_list);
+ $o = (object)array('@list' => (array)$_list);
+ self::addValue(
+ $subject, $property, $o,
+ array('propertyIsArray' => true, 'allowDuplicate' => false));
+ } else {
+ // handle @value
+ $this->_createNodeMap($o, $graphs, $graph, $namer, $name, null);
+ self::addValue(
+ $subject, $property, $o,
+ array('propertyIsArray' => true, 'allowDuplicate' => false));
+ }
+ }
+ }
+ }
+
+ /**
+ * Frames subjects according to the given frame.
+ *
+ * @param stdClass $state the current framing state.
+ * @param array $subjects the subjects to filter.
+ * @param array $frame the frame.
+ * @param mixed $parent the parent subject or top-level array.
+ * @param mixed $property the parent property, initialized to null.
+ */
+ protected function _matchFrame(
+ $state, $subjects, $frame, $parent, $property) {
+ // validate the frame
+ $this->_validateFrame($frame);
+ $frame = $frame[0];
+
+ // get flags for current frame
+ $options = $state->options;
+ $flags = array(
+ 'embed' => $this->_getFrameFlag($frame, $options, 'embed'),
+ 'explicit' => $this->_getFrameFlag($frame, $options, 'explicit'),
+ 'requireAll' => $this->_getFrameFlag($frame, $options, 'requireAll'));
+
+ // filter out subjects that match the frame
+ $matches = $this->_filterSubjects($state, $subjects, $frame, $flags);
+
+ // add matches to output
+ foreach($matches as $id => $subject) {
+ if($flags['embed'] === '@link' && property_exists($state->link, $id)) {
+ // TODO: may want to also match an existing linked subject against
+ // the current frame ... so different frames could produce different
+ // subjects that are only shared in-memory when the frames are the same
+
+ // add existing linked subject
+ $this->_addFrameOutput($parent, $property, $state->link->{$id});
+ continue;
+ }
+
+ /* Note: In order to treat each top-level match as a compartmentalized
+ result, clear the unique embedded subjects map when the property is null,
+ which only occurs at the top-level. */
+ if($property === null) {
+ $state->uniqueEmbeds = new stdClass();
+ }
+
+ // start output for subject
+ $output = new stdClass();
+ $output->{'@id'} = $id;
+ $state->link->{$id} = $output;
+
+ // if embed is @never or if a circular reference would be created by an
+ // embed, the subject cannot be embedded, just add the reference;
+ // note that a circular reference won't occur when the embed flag is
+ // `@link` as the above check will short-circuit before reaching this point
+ if($flags['embed'] === '@never' ||
+ $this->_createsCircularReference($subject, $state->subjectStack)) {
+ $this->_addFrameOutput($parent, $property, $output);
+ continue;
+ }
+
+ // if only the last match should be embedded
+ if($flags['embed'] === '@last') {
+ // remove any existing embed
+ if(property_exists($state->uniqueEmbeds, $id)) {
+ $this->_removeEmbed($state, $id);
+ }
+ $state->uniqueEmbeds->{$id} = array(
+ 'parent' => $parent, 'property' => $property);
+ }
+
+ // push matching subject onto stack to enable circular embed checks
+ $state->subjectStack[] = $subject;
+
+ // iterate over subject properties
+ $props = array_keys((array)$subject);
+ sort($props);
+ foreach($props as $prop) {
+ // copy keywords to output
+ if(self::_isKeyword($prop)) {
+ $output->{$prop} = self::copy($subject->{$prop});
+ continue;
+ }
+
+ // explicit is on and property isn't in the frame, skip processing
+ if($flags['explicit'] && !property_exists($frame, $prop)) {
+ continue;
+ }
+
+ // add objects
+ $objects = $subject->{$prop};
+ foreach($objects as $o) {
+ // recurse into list
+ if(self::_isList($o)) {
+ // add empty list
+ $list = (object)array('@list' => array());
+ $this->_addFrameOutput($output, $prop, $list);
+
+ // add list objects
+ $src = $o->{'@list'};
+ foreach($src as $o) {
+ if(self::_isSubjectReference($o)) {
+ // recurse into subject reference
+ $subframe = (property_exists($frame, $prop) ?
+ $frame->{$prop}[0]->{'@list'} :
+ $this->_createImplicitFrame($flags));
+ $this->_matchFrame(
+ $state, array($o->{'@id'}), $subframe, $list, '@list');
+ } else {
+ // include other values automatically
+ $this->_addFrameOutput($list, '@list', self::copy($o));
+ }
+ }
+ continue;
+ }
+
+ if(self::_isSubjectReference($o)) {
+ // recurse into subject reference
+ $subframe = (property_exists($frame, $prop) ?
+ $frame->{$prop} : $this->_createImplicitFrame($flags));
+ $this->_matchFrame(
+ $state, array($o->{'@id'}), $subframe, $output, $prop);
+ } else {
+ // include other values automatically
+ $this->_addFrameOutput($output, $prop, self::copy($o));
+ }
+ }
+ }
+
+ // handle defaults
+ $props = array_keys((array)$frame);
+ sort($props);
+ foreach($props as $prop) {
+ // skip keywords
+ if(self::_isKeyword($prop)) {
+ continue;
+ }
+
+ // if omit default is off, then include default values for properties
+ // that appear in the next frame but are not in the matching subject
+ $next = $frame->{$prop}[0];
+ $omit_default_on = $this->_getFrameFlag(
+ $next, $options, 'omitDefault');
+ if(!$omit_default_on && !property_exists($output, $prop)) {
+ $preserve = '@null';
+ if(property_exists($next, '@default')) {
+ $preserve = self::copy($next->{'@default'});
+ }
+ $preserve = self::arrayify($preserve);
+ $output->{$prop} = array((object)array('@preserve' => $preserve));
+ }
+ }
+
+ // add output to parent
+ $this->_addFrameOutput($parent, $property, $output);
+
+ // pop matching subject from circular ref-checking stack
+ array_pop($state->subjectStack);
+ }
+ }
+
+ /**
+ * Creates an implicit frame when recursing through subject matches. If
+ * a frame doesn't have an explicit frame for a particular property, then
+ * a wildcard child frame will be created that uses the same flags that the
+ * parent frame used.
+ *
+ * @param assoc flags the current framing flags.
+ *
+ * @return array the implicit frame.
+ */
+ function _createImplicitFrame($flags) {
+ $frame = new stdClass();
+ foreach($flags as $key => $value) {
+ $frame->{'@' . $key} = array($flags[$key]);
+ }
+ return array($frame);
+ }
+
+ /**
+ * Checks the current subject stack to see if embedding the given subject
+ * would cause a circular reference.
+ *
+ * @param stdClass subject_to_embed the subject to embed.
+ * @param assoc subject_stack the current stack of subjects.
+ *
+ * @return bool true if a circular reference would be created, false if not.
+ */
+ function _createsCircularReference($subject_to_embed, $subject_stack) {
+ for($i = count($subject_stack) - 1; $i >= 0; --$i) {
+ if($subject_stack[$i]->{'@id'} === $subject_to_embed->{'@id'}) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the frame flag value for the given flag name.
+ *
+ * @param stdClass $frame the frame.
+ * @param stdClass $options the framing options.
+ * @param string $name the flag name.
+ *
+ * @return mixed $the flag value.
+ */
+ protected function _getFrameFlag($frame, $options, $name) {
+ $flag = "@$name";
+ $rval = (property_exists($frame, $flag) ?
+ $frame->{$flag}[0] : $options[$name]);
+ if($name === 'embed') {
+ // default is "@last"
+ // backwards-compatibility support for "embed" maps:
+ // true => "@last"
+ // false => "@never"
+ if($rval === true) {
+ $rval = '@last';
+ } else if($rval === false) {
+ $rval = '@never';
+ } else if($rval !== '@always' && $rval !== '@never' &&
+ $rval !== '@link') {
+ $rval = '@last';
+ }
+ }
+ return $rval;
+ }
+
+ /**
+ * Validates a JSON-LD frame, throwing an exception if the frame is invalid.
+ *
+ * @param array $frame the frame to validate.
+ */
+ protected function _validateFrame($frame) {
+ if(!is_array($frame) || count($frame) !== 1 || !is_object($frame[0])) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; a JSON-LD frame must be a single object.',
+ 'jsonld.SyntaxError', null, array('frame' => $frame));
+ }
+ }
+
+ /**
+ * Returns a map of all of the subjects that match a parsed frame.
+ *
+ * @param stdClass $state the current framing state.
+ * @param array $subjects the set of subjects to filter.
+ * @param stdClass $frame the parsed frame.
+ * @param assoc $flags the frame flags.
+ *
+ * @return stdClass all of the matched subjects.
+ */
+ protected function _filterSubjects($state, $subjects, $frame, $flags) {
+ $rval = new stdClass();
+ sort($subjects);
+ foreach($subjects as $id) {
+ $subject = $state->subjects->{$id};
+ if($this->_filterSubject($subject, $frame, $flags)) {
+ $rval->{$id} = $subject;
+ }
+ }
+ return $rval;
+ }
+
+ /**
+ * Returns true if the given subject matches the given frame.
+ *
+ * @param stdClass $subject the subject to check.
+ * @param stdClass $frame the frame to check.
+ * @param assoc $flags the frame flags.
+ *
+ * @return bool true if the subject matches, false if not.
+ */
+ protected function _filterSubject($subject, $frame, $flags) {
+ // check @type (object value means 'any' type, fall through to ducktyping)
+ if(property_exists($frame, '@type') &&
+ !(count($frame->{'@type'}) === 1 && is_object($frame->{'@type'}[0]))) {
+ $types = $frame->{'@type'};
+ foreach($types as $type) {
+ // any matching @type is a match
+ if(self::hasValue($subject, '@type', $type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // check ducktype
+ $wildcard = true;
+ $matches_some = false;
+ foreach($frame as $k => $v) {
+ if(self::_isKeyword($k)) {
+ // skip non-@id and non-@type
+ if($k !== '@id' && $k !== '@type') {
+ continue;
+ }
+ $wildcard = false;
+
+ // check @id for a specific @id value
+ if($k === '@id' && is_string($v)) {
+ if(!property_exists($subject, $k) || $subject->{$k} !== $v) {
+ return false;
+ }
+ $matches_some = true;
+ continue;
+ }
+ }
+
+ $wildcard = false;
+
+ if(property_exists($subject, $k)) {
+ // $v === [] means do not match if property is present
+ if(is_array($v) && count($v) === 0) {
+ return false;
+ }
+ $matches_some = true;
+ continue;
+ }
+
+ // all properties must match to be a duck unless a @default is specified
+ $has_default = (is_array($v) && count($v) === 1 && is_object($v[0]) &&
+ property_exists($v[0], '@default'));
+ if($flags['requireAll'] && !$has_default) {
+ return false;
+ }
+ }
+
+ // return true if wildcard or subject matches some properties
+ return $wildcard || $matches_some;
+ }
+
+ /**
+ * Removes an existing embed.
+ *
+ * @param stdClass $state the current framing state.
+ * @param string $id the @id of the embed to remove.
+ */
+ protected function _removeEmbed($state, $id) {
+ // get existing embed
+ $embeds = $state->uniqueEmbeds;
+ $embed = $embeds->{$id};
+ $property = $embed['property'];
+
+ // create reference to replace embed
+ $subject = (object)array('@id' => $id);
+
+ // remove existing embed
+ if(is_array($embed->parent)) {
+ // replace subject with reference
+ foreach($embed->parent as $i => $parent) {
+ if(self::compareValues($parent, $subject)) {
+ $embed->parent[$i] = $subject;
+ break;
+ }
+ }
+ } else {
+ // replace subject with reference
+ $use_array = is_array($embed->parent->{$property});
+ self::removeValue($embed->parent, $property, $subject,
+ array('propertyIsArray' => $use_array));
+ self::addValue($embed->parent, $property, $subject,
+ array('propertyIsArray' => $use_array));
+ }
+
+ // recursively remove dependent dangling embeds
+ $removeDependents = function($id) {
+ // get embed keys as a separate array to enable deleting keys in map
+ $ids = array_keys((array)$embeds);
+ foreach($ids as $next) {
+ if(property_exists($embeds, $next) &&
+ is_object($embeds->{$next}->parent) &&
+ $embeds->{$next}->parent->{'@id'} === $id) {
+ unset($embeds->{$next});
+ $removeDependents($next);
+ }
+ }
+ };
+ $removeDependents($id);
+ }
+
+ /**
+ * Adds framing output to the given parent.
+ *
+ * @param mixed $parent the parent to add to.
+ * @param string $property the parent property.
+ * @param mixed $output the output to add.
+ */
+ protected function _addFrameOutput($parent, $property, $output) {
+ if(is_object($parent) && !($parent instanceof ArrayObject)) {
+ self::addValue(
+ $parent, $property, $output, array('propertyIsArray' => true));
+ } else {
+ $parent[] = $output;
+ }
+ }
+
+ /**
+ * Removes the @preserve keywords as the last step of the framing algorithm.
+ *
+ * @param stdClass $ctx the active context used to compact the input.
+ * @param mixed $input the framed, compacted output.
+ * @param assoc $options the compaction options used.
+ *
+ * @return mixed the resulting output.
+ */
+ protected function _removePreserve($ctx, $input, $options) {
+ // recurse through arrays
+ if(is_array($input)) {
+ $output = array();
+ foreach($input as $e) {
+ $result = $this->_removePreserve($ctx, $e, $options);
+ // drop nulls from arrays
+ if($result !== null) {
+ $output[] = $result;
+ }
+ }
+ $input = $output;
+ } else if(is_object($input)) {
+ // remove @preserve
+ if(property_exists($input, '@preserve')) {
+ if($input->{'@preserve'} === '@null') {
+ return null;
+ }
+ return $input->{'@preserve'};
+ }
+
+ // skip @values
+ if(self::_isValue($input)) {
+ return $input;
+ }
+
+ // recurse through @lists
+ if(self::_isList($input)) {
+ $input->{'@list'} = $this->_removePreserve(
+ $ctx, $input->{'@list'}, $options);
+ return $input;
+ }
+
+ // handle in-memory linked nodes
+ $id_alias = $this->_compactIri($ctx, '@id');
+ if(property_exists($input, $id_alias)) {
+ $id = $input->{$id_alias};
+ if(isset($options['link'][$id])) {
+ $idx = array_search($input, $options['link'][$id]);
+ if($idx === false) {
+ // prevent circular visitation
+ $options['link'][$id][] = $input;
+ } else {
+ // already visited
+ return $options['link'][$id][$idx];
+ }
+ } else {
+ // prevent circular visitation
+ $options['link'][$id] = array($input);
+ }
+ }
+
+ // recurse through properties
+ foreach($input as $prop => $v) {
+ $result = $this->_removePreserve($ctx, $v, $options);
+ $container = self::getContextValue($ctx, $prop, '@container');
+ if($options['compactArrays'] &&
+ is_array($result) && count($result) === 1 &&
+ $container !== '@set' && $container !== '@list') {
+ $result = $result[0];
+ }
+ $input->{$prop} = $result;
+ }
+ }
+ return $input;
+ }
+
+ /**
+ * Compares two RDF triples for equality.
+ *
+ * @param stdClass $t1 the first triple.
+ * @param stdClass $t2 the second triple.
+ *
+ * @return true if the triples are the same, false if not.
+ */
+ protected static function _compareRDFTriples($t1, $t2) {
+ foreach(array('subject', 'predicate', 'object') as $attr) {
+ if($t1->{$attr}->type !== $t2->{$attr}->type ||
+ $t1->{$attr}->value !== $t2->{$attr}->value) {
+ return false;
+ }
+ }
+ if(property_exists($t1->object, 'language') !==
+ property_exists($t1->object, 'language')) {
+ return false;
+ }
+ if(property_exists($t1->object, 'language') &&
+ $t1->object->language !== $t2->object->language) {
+ return false;
+ }
+ if(property_exists($t1->object, 'datatype') &&
+ $t1->object->datatype !== $t2->object->datatype) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Hashes all of the quads about a blank node.
+ *
+ * @param string $id the ID of the bnode to hash quads for.
+ * @param stdClass $bnodes the mapping of bnodes to quads.
+ * @param UniqueNamer $namer the canonical bnode namer.
+ *
+ * @return string the new hash.
+ */
+ protected function _hashQuads($id, $bnodes, $namer) {
+ // return cached hash
+ if(property_exists($bnodes->{$id}, 'hash')) {
+ return $bnodes->{$id}->hash;
+ }
+
+ // serialize all of bnode's quads
+ $quads = $bnodes->{$id}->quads;
+ $nquads = array();
+ foreach($quads as $quad) {
+ $nquads[] = $this->toNQuad($quad, property_exists($quad, 'name') ?
+ $quad->name->value : null, $id);
+ }
+
+ // sort serialized quads
+ sort($nquads);
+
+ // cache and return hashed quads
+ $hash = $bnodes->{$id}->hash = sha1(implode($nquads));
+ return $hash;
+ }
+
+ /**
+ * Produces a hash for the paths of adjacent bnodes for a bnode,
+ * incorporating all information about its subgraph of bnodes. This
+ * method will recursively pick adjacent bnode permutations that produce the
+ * lexicographically-least 'path' serializations.
+ *
+ * @param string $id the ID of the bnode to hash paths for.
+ * @param stdClass $bnodes the map of bnode quads.
+ * @param UniqueNamer $namer the canonical bnode namer.
+ * @param UniqueNamer $path_namer the namer used to assign names to adjacent
+ * bnodes.
+ *
+ * @return stdClass the hash and path namer used.
+ */
+ protected function _hashPaths($id, $bnodes, $namer, $path_namer) {
+ // create SHA-1 digest
+ $md = hash_init('sha1');
+
+ // group adjacent bnodes by hash, keep properties and references separate
+ $groups = new stdClass();
+ $quads = $bnodes->{$id}->quads;
+ foreach($quads as $quad) {
+ // get adjacent bnode
+ $bnode = $this->_getAdjacentBlankNodeName($quad->subject, $id);
+ if($bnode !== null) {
+ // normal property
+ $direction = 'p';
+ } else {
+ $bnode = $this->_getAdjacentBlankNodeName($quad->object, $id);
+ if($bnode !== null) {
+ // reverse property
+ $direction = 'r';
+ }
+ }
+ if($bnode !== null) {
+ // get bnode name (try canonical, path, then hash)
+ if($namer->isNamed($bnode)) {
+ $name = $namer->getName($bnode);
+ } else if($path_namer->isNamed($bnode)) {
+ $name = $path_namer->getName($bnode);
+ } else {
+ $name = $this->_hashQuads($bnode, $bnodes, $namer);
+ }
+
+ // hash direction, property, and bnode name/hash
+ $group_md = hash_init('sha1');
+ hash_update($group_md, $direction);
+ hash_update($group_md, $quad->predicate->value);
+ hash_update($group_md, $name);
+ $group_hash = hash_final($group_md);
+
+ // add bnode to hash group
+ if(property_exists($groups, $group_hash)) {
+ $groups->{$group_hash}[] = $bnode;
+ } else {
+ $groups->{$group_hash} = array($bnode);
+ }
+ }
+ }
+
+ // iterate over groups in sorted hash order
+ $group_hashes = array_keys((array)$groups);
+ sort($group_hashes);
+ foreach($group_hashes as $group_hash) {
+ // digest group hash
+ hash_update($md, $group_hash);
+
+ // choose a path and namer from the permutations
+ $chosen_path = null;
+ $chosen_namer = null;
+ $permutator = new Permutator($groups->{$group_hash});
+ while($permutator->hasNext()) {
+ $permutation = $permutator->next();
+ $path_namer_copy = clone $path_namer;
+
+ // build adjacent path
+ $path = '';
+ $skipped = false;
+ $recurse = array();
+ foreach($permutation as $bnode) {
+ // use canonical name if available
+ if($namer->isNamed($bnode)) {
+ $path .= $namer->getName($bnode);
+ } else {
+ // recurse if bnode isn't named in the path yet
+ if(!$path_namer_copy->isNamed($bnode)) {
+ $recurse[] = $bnode;
+ }
+ $path .= $path_namer_copy->getName($bnode);
+ }
+
+ // skip permutation if path is already >= chosen path
+ if($chosen_path !== null && strlen($path) >= strlen($chosen_path) &&
+ $path > $chosen_path) {
+ $skipped = true;
+ break;
+ }
+ }
+
+ // recurse
+ if(!$skipped) {
+ foreach($recurse as $bnode) {
+ $result = $this->_hashPaths(
+ $bnode, $bnodes, $namer, $path_namer_copy);
+ $path .= $path_namer_copy->getName($bnode);
+ $path .= "<{$result->hash}>";
+ $path_namer_copy = $result->pathNamer;
+
+ // skip permutation if path is already >= chosen path
+ if($chosen_path !== null &&
+ strlen($path) >= strlen($chosen_path) && $path > $chosen_path) {
+ $skipped = true;
+ break;
+ }
+ }
+ }
+
+ if(!$skipped && ($chosen_path === null || $path < $chosen_path)) {
+ $chosen_path = $path;
+ $chosen_namer = $path_namer_copy;
+ }
+ }
+
+ // digest chosen path and update namer
+ hash_update($md, $chosen_path);
+ $path_namer = $chosen_namer;
+ }
+
+ // return SHA-1 hash and path namer
+ return (object)array(
+ 'hash' => hash_final($md), 'pathNamer' => $path_namer);
+ }
+
+ /**
+ * A helper function that gets the blank node name from an RDF quad
+ * node (subject or object). If the node is not a blank node or its
+ * value does not match the given blank node ID, it will be returned.
+ *
+ * @param stdClass $node the RDF quad node.
+ * @param string $id the ID of the blank node to look next to.
+ *
+ * @return mixed the adjacent blank node name or null if none was found.
+ */
+ protected function _getAdjacentBlankNodeName($node, $id) {
+ if($node->type === 'blank node' && $node->value !== $id) {
+ return $node->value;
+ }
+ return null;
+ }
+
+ /**
+ * Compares two strings first based on length and then lexicographically.
+ *
+ * @param string $a the first string.
+ * @param string $b the second string.
+ *
+ * @return integer -1 if a < b, 1 if a > b, 0 if a == b.
+ */
+ protected function _compareShortestLeast($a, $b) {
+ $len_a = strlen($a);
+ $len_b = strlen($b);
+ if($len_a < $len_b) {
+ return -1;
+ }
+ if($len_b < $len_a) {
+ return 1;
+ }
+ if($a === $b) {
+ return 0;
+ }
+ return ($a < $b) ? -1 : 1;
+ }
+
+ /**
+ * Picks the preferred compaction term from the given inverse context entry.
+ *
+ * @param active_ctx the active context.
+ * @param iri the IRI to pick the term for.
+ * @param value the value to pick the term for.
+ * @param containers the preferred containers.
+ * @param type_or_language either '@type' or '@language'.
+ * @param type_or_language_value the preferred value for '@type' or
+ * '@language'.
+ *
+ * @return mixed the preferred term.
+ */
+ protected function _selectTerm(
+ $active_ctx, $iri, $value, $containers,
+ $type_or_language, $type_or_language_value) {
+ if($type_or_language_value === null) {
+ $type_or_language_value = '@null';
+ }
+
+ // options for the value of @type or @language
+ $prefs = array();
+
+ // determine prefs for @id based on whether or not value compacts to a term
+ if(($type_or_language_value === '@id' ||
+ $type_or_language_value === '@reverse') &&
+ self::_isSubjectReference($value)) {
+ // prefer @reverse first
+ if($type_or_language_value === '@reverse') {
+ $prefs[] = '@reverse';
+ }
+ // try to compact value to a term
+ $term = $this->_compactIri(
+ $active_ctx, $value->{'@id'}, null, array('vocab' => true));
+ if(property_exists($active_ctx->mappings, $term) &&
+ $active_ctx->mappings->{$term} &&
+ $active_ctx->mappings->{$term}->{'@id'} === $value->{'@id'}) {
+ // prefer @vocab
+ array_push($prefs, '@vocab', '@id');
+ } else {
+ // prefer @id
+ array_push($prefs, '@id', '@vocab');
+ }
+ } else {
+ $prefs[] = $type_or_language_value;
+ }
+ $prefs[] = '@none';
+
+ $container_map = $active_ctx->inverse->{$iri};
+ foreach($containers as $container) {
+ // if container not available in the map, continue
+ if(!property_exists($container_map, $container)) {
+ continue;
+ }
+
+ $type_or_language_value_map =
+ $container_map->{$container}->{$type_or_language};
+ foreach($prefs as $pref) {
+ // if type/language option not available in the map, continue
+ if(!property_exists($type_or_language_value_map, $pref)) {
+ continue;
+ }
+
+ // select term
+ return $type_or_language_value_map->{$pref};
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Compacts an IRI or keyword into a term or prefix if it can be. If the
+ * IRI has an associated value it may be passed.
+ *
+ * @param stdClass $active_ctx the active context to use.
+ * @param string $iri the IRI to compact.
+ * @param mixed $value the value to check or null.
+ * @param assoc $relative_to options for how to compact IRIs:
+ * vocab: true to split after @vocab, false not to.
+ * @param bool $reverse true if a reverse property is being compacted, false
+ * if not.
+ *
+ * @return string the compacted term, prefix, keyword alias, or original IRI.
+ */
+ protected function _compactIri(
+ $active_ctx, $iri, $value=null, $relative_to=array(), $reverse=false) {
+ // can't compact null
+ if($iri === null) {
+ return $iri;
+ }
+
+ $inverse_ctx = $this->_getInverseContext($active_ctx);
+
+ if(self::_isKeyword($iri)) {
+ // a keyword can only be compacted to simple alias
+ if(property_exists($inverse_ctx, $iri)) {
+ return $inverse_ctx->$iri->{'@none'}->{'@type'}->{'@none'};
+ }
+ return $iri;
+ }
+
+ if(!isset($relative_to['vocab'])) {
+ $relative_to['vocab'] = false;
+ }
+
+ // use inverse context to pick a term if iri is relative to vocab
+ if($relative_to['vocab'] && property_exists($inverse_ctx, $iri)) {
+ $default_language = '@none';
+ if(property_exists($active_ctx, '@language')) {
+ $default_language = $active_ctx->{'@language'};
+ }
+
+ // prefer @index if available in value
+ $containers = array();
+ if(is_object($value) && property_exists($value, '@index')) {
+ $containers[] = '@index';
+ }
+
+ // defaults for term selection based on type/language
+ $type_or_language = '@language';
+ $type_or_language_value = '@null';
+
+ if($reverse) {
+ $type_or_language = '@type';
+ $type_or_language_value = '@reverse';
+ $containers[] = '@set';
+ } else if(self::_isList($value)) {
+ // choose the most specific term that works for all elements in @list
+ // only select @list containers if @index is NOT in value
+ if(!property_exists($value, '@index')) {
+ $containers[] = '@list';
+ }
+ $list = $value->{'@list'};
+ $common_language = (count($list) === 0) ? $default_language : null;
+ $common_type = null;
+ foreach($list as $item) {
+ $item_language = '@none';
+ $item_type = '@none';
+ if(self::_isValue($item)) {
+ if(property_exists($item, '@language')) {
+ $item_language = $item->{'@language'};
+ } else if(property_exists($item, '@type')) {
+ $item_type = $item->{'@type'};
+ } else {
+ // plain literal
+ $item_language = '@null';
+ }
+ } else {
+ $item_type = '@id';
+ }
+ if($common_language === null) {
+ $common_language = $item_language;
+ } else if($item_language !== $common_language &&
+ self::_isValue($item)) {
+ $common_language = '@none';
+ }
+ if($common_type === null) {
+ $common_type = $item_type;
+ } else if($item_type !== $common_type) {
+ $common_type = '@none';
+ }
+ // there are different languages and types in the list, so choose
+ // the most generic term, no need to keep iterating the list
+ if($common_language === '@none' && $common_type === '@none') {
+ break;
+ }
+ }
+ if($common_language === null) {
+ $common_language = '@none';
+ }
+ if($common_type === null) {
+ $common_type = '@none';
+ }
+ if($common_type !== '@none') {
+ $type_or_language = '@type';
+ $type_or_language_value = $common_type;
+ } else {
+ $type_or_language_value = $common_language;
+ }
+ } else {
+ if(self::_isValue($value)) {
+ if(property_exists($value, '@language') &&
+ !property_exists($value, '@index')) {
+ $containers[] = '@language';
+ $type_or_language_value = $value->{'@language'};
+ } else if(property_exists($value, '@type')) {
+ $type_or_language = '@type';
+ $type_or_language_value = $value->{'@type'};
+ }
+ } else {
+ $type_or_language = '@type';
+ $type_or_language_value = '@id';
+ }
+ $containers[] = '@set';
+ }
+
+ // do term selection
+ $containers[] = '@none';
+ $term = $this->_selectTerm(
+ $active_ctx, $iri, $value,
+ $containers, $type_or_language, $type_or_language_value);
+ if($term !== null) {
+ return $term;
+ }
+ }
+
+ // no term match, use @vocab if available
+ if($relative_to['vocab']) {
+ if(property_exists($active_ctx, '@vocab')) {
+ // determine if vocab is a prefix of the iri
+ $vocab = $active_ctx->{'@vocab'};
+ if(strpos($iri, $vocab) === 0 && $iri !== $vocab) {
+ // use suffix as relative iri if it is not a term in the active
+ // context
+ $suffix = substr($iri, strlen($vocab));
+ if(!property_exists($active_ctx->mappings, $suffix)) {
+ return $suffix;
+ }
+ }
+ }
+ }
+
+ // no term or @vocab match, check for possible CURIEs
+ $choice = null;
+ $idx = 0;
+ $partial_matches = array();
+ $iri_map = $active_ctx->fast_curie_map;
+ // check for partial matches of against `iri`, which means look until
+ // iri.length - 1, not full length
+ $max_partial_length = strlen($iri) - 1;
+ for(; $idx < $max_partial_length && isset($iri_map[$iri[$idx]]); ++$idx) {
+ $iri_map = $iri_map[$iri[$idx]];
+ if(isset($iri_map[''])) {
+ $entry = $iri_map[''][0];
+ $entry->iri_length = $idx + 1;
+ $partial_matches[] = $entry;
+ }
+ }
+ // check partial matches in reverse order to prefer longest ones first
+ $partial_matches = array_reverse($partial_matches);
+ foreach($partial_matches as $entry) {
+ $terms = $entry->terms;
+ foreach($terms as $term) {
+ // a CURIE is usable if:
+ // 1. it has no mapping, OR
+ // 2. value is null, which means we're not compacting an @value, AND
+ // the mapping matches the IRI
+ $curie = $term . ':' . substr($iri, $entry->iri_length);
+ $is_usable_curie = (!property_exists($active_ctx->mappings, $curie) ||
+ ($value === null &&
+ $active_ctx->mappings->{$curie}->{'@id'} === $iri));
+
+ // select curie if it is shorter or the same length but
+ // lexicographically less than the current choice
+ if($is_usable_curie && ($choice === null ||
+ self::_compareShortestLeast($curie, $choice) < 0)) {
+ $choice = $curie;
+ }
+ }
+ }
+
+ // return chosen curie
+ if($choice !== null) {
+ return $choice;
+ }
+
+ // compact IRI relative to base
+ if(!$relative_to['vocab']) {
+ return jsonld_remove_base($active_ctx->{'@base'}, $iri);
+ }
+
+ // return IRI as is
+ return $iri;
+ }
+
+ /**
+ * Performs value compaction on an object with '@value' or '@id' as the only
+ * property.
+ *
+ * @param stdClass $active_ctx the active context.
+ * @param string $active_property the active property that points to the
+ * value.
+ * @param mixed $value the value to compact.
+ *
+ * @return mixed the compaction result.
+ */
+ protected function _compactValue($active_ctx, $active_property, $value) {
+ // value is a @value
+ if(self::_isValue($value)) {
+ // get context rules
+ $type = self::getContextValue($active_ctx, $active_property, '@type');
+ $language = self::getContextValue(
+ $active_ctx, $active_property, '@language');
+ $container = self::getContextValue(
+ $active_ctx, $active_property, '@container');
+
+ // whether or not the value has an @index that must be preserved
+ $preserve_index = (property_exists($value, '@index') &&
+ $container !== '@index');
+
+ // if there's no @index to preserve
+ if(!$preserve_index) {
+ // matching @type or @language specified in context, compact value
+ if(self::_hasKeyValue($value, '@type', $type) ||
+ self::_hasKeyValue($value, '@language', $language)) {
+ return $value->{'@value'};
+ }
+ }
+
+ // return just the value of @value if all are true:
+ // 1. @value is the only key or @index isn't being preserved
+ // 2. there is no default language or @value is not a string or
+ // the key has a mapping with a null @language
+ $key_count = count(array_keys((array)$value));
+ $is_value_only_key = ($key_count === 1 ||
+ ($key_count === 2 && property_exists($value, '@index') &&
+ !$preserve_index));
+ $has_default_language = property_exists($active_ctx, '@language');
+ $is_value_string = is_string($value->{'@value'});
+ $has_null_mapping = (
+ property_exists($active_ctx->mappings, $active_property) &&
+ $active_ctx->mappings->{$active_property} !== null &&
+ self::_hasKeyValue(
+ $active_ctx->mappings->{$active_property}, '@language', null));
+ if($is_value_only_key &&
+ (!$has_default_language || !$is_value_string || $has_null_mapping)) {
+ return $value->{'@value'};
+ }
+
+ $rval = new stdClass();
+
+ // preserve @index
+ if($preserve_index) {
+ $rval->{$this->_compactIri($active_ctx, '@index')} = $value->{'@index'};
+ }
+
+ // compact @type IRI
+ if(property_exists($value, '@type')) {
+ $rval->{$this->_compactIri($active_ctx, '@type')} = $this->_compactIri(
+ $active_ctx, $value->{'@type'}, null, array('vocab' => true));
+ } else if(property_exists($value, '@language')) {
+ // alias @language
+ $rval->{$this->_compactIri($active_ctx, '@language')} =
+ $value->{'@language'};
+ }
+
+ // alias @value
+ $rval->{$this->_compactIri($active_ctx, '@value')} = $value->{'@value'};
+
+ return $rval;
+ }
+
+ // value is a subject reference
+ $expanded_property = $this->_expandIri(
+ $active_ctx, $active_property, array('vocab' => true));
+ $type = self::getContextValue($active_ctx, $active_property, '@type');
+ $compacted = $this->_compactIri(
+ $active_ctx, $value->{'@id'}, null,
+ array('vocab' => ($type === '@vocab')));
+
+ // compact to scalar
+ if($type === '@id' || $type === '@vocab' ||
+ $expanded_property === '@graph') {
+ return $compacted;
+ }
+
+ $rval = (object)array(
+ $this->_compactIri($active_ctx, '@id') => $compacted);
+ return $rval;
+ }
+
+ /**
+ * Creates a term definition during context processing.
+ *
+ * @param stdClass $active_ctx the current active context.
+ * @param stdClass $local_ctx the local context being processed.
+ * @param string $term the key in the local context to define the mapping for.
+ * @param stdClass $defined a map of defining/defined keys to detect cycles
+ * and prevent double definitions.
+ */
+ protected function _createTermDefinition(
+ $active_ctx, $local_ctx, $term, $defined) {
+ if(property_exists($defined, $term)) {
+ // term already defined
+ if($defined->{$term}) {
+ return;
+ }
+ // cycle detected
+ throw new JsonLdException(
+ 'Cyclical context definition detected.',
+ 'jsonld.CyclicalContext', 'cyclic IRI mapping',
+ array('context' => $local_ctx, 'term' => $term));
+ }
+
+ // now defining term
+ $defined->{$term} = false;
+
+ if(self::_isKeyword($term)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; keywords cannot be overridden.',
+ 'jsonld.SyntaxError', 'keyword redefinition',
+ array('context' => $local_ctx, 'term' => $term));
+ }
+
+ // remove old mapping
+ if(property_exists($active_ctx->mappings, $term)) {
+ unset($active_ctx->mappings->{$term});
+ }
+
+ // get context term value
+ $value = $local_ctx->{$term};
+
+ // clear context entry
+ if($value === null || (is_object($value) &&
+ self::_hasKeyValue($value, '@id', null))) {
+ $active_ctx->mappings->{$term} = null;
+ $defined->{$term} = true;
+ return;
+ }
+
+ // convert short-hand value to object w/@id
+ if(is_string($value)) {
+ $value = (object)array('@id' => $value);
+ }
+
+ if(!is_object($value)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context property values must be ' .
+ 'strings or objects.', 'jsonld.SyntaxError', 'invalid term definition',
+ array('context' => $local_ctx));
+ }
+
+ // create new mapping
+ $mapping = $active_ctx->mappings->{$term} = new stdClass();
+ $mapping->reverse = false;
+
+ if(property_exists($value, '@reverse')) {
+ if(property_exists($value, '@id')) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; a @reverse term definition must not ' +
+ 'contain @id.', 'jsonld.SyntaxError', 'invalid reverse property',
+ array('context' => $local_ctx));
+ }
+ $reverse = $value->{'@reverse'};
+ if(!is_string($reverse)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; a @context @reverse value must be a string.',
+ 'jsonld.SyntaxError', 'invalid IRI mapping',
+ array('context' => $local_ctx));
+ }
+
+ // expand and add @id mapping
+ $id = $this->_expandIri(
+ $active_ctx, $reverse, array('vocab' => true, 'base' => false),
+ $local_ctx, $defined);
+ if(!self::_isAbsoluteIri($id)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @reverse value must be ' .
+ 'an absolute IRI or a blank node identifier.',
+ 'jsonld.SyntaxError', 'invalid IRI mapping',
+ array('context' => $local_ctx));
+ }
+ $mapping->{'@id'} = $id;
+ $mapping->reverse = true;
+ } else if(property_exists($value, '@id')) {
+ $id = $value->{'@id'};
+ if(!is_string($id)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @id value must be a string.',
+ 'jsonld.SyntaxError', 'invalid IRI mapping',
+ array('context' => $local_ctx));
+ }
+ if($id !== $term) {
+ // add @id to mapping
+ $id = $this->_expandIri(
+ $active_ctx, $id, array('vocab' => true, 'base' => false),
+ $local_ctx, $defined);
+ if(!self::_isAbsoluteIri($id) && !self::_isKeyword($id)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @id value must be an ' .
+ 'absolute IRI, a blank node identifier, or a keyword.',
+ 'jsonld.SyntaxError', 'invalid IRI mapping',
+ array('context' => $local_ctx));
+ }
+ $mapping->{'@id'} = $id;
+ }
+ }
+
+ // always compute whether term has a colon as an optimization for
+ // _compactIri
+ $colon = strpos($term, ':');
+ $mapping->_term_has_colon = ($colon !== false);
+
+ if(!property_exists($mapping, '@id')) {
+ // see if the term has a prefix
+ if($mapping->_term_has_colon) {
+ $prefix = substr($term, 0, $colon);
+ if(property_exists($local_ctx, $prefix)) {
+ // define parent prefix
+ $this->_createTermDefinition(
+ $active_ctx, $local_ctx, $prefix, $defined);
+ }
+
+ if(property_exists($active_ctx->mappings, $prefix) &&
+ $active_ctx->mappings->{$prefix}) {
+ // set @id based on prefix parent
+ $suffix = substr($term, $colon + 1);
+ $mapping->{'@id'} = $active_ctx->mappings->{$prefix}->{'@id'} .
+ $suffix;
+ } else {
+ // term is an absolute IRI
+ $mapping->{'@id'} = $term;
+ }
+ } else {
+ // non-IRIs *must* define @ids if @vocab is not available
+ if(!property_exists($active_ctx, '@vocab')) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context terms must define an @id.',
+ 'jsonld.SyntaxError', 'invalid IRI mapping',
+ array('context' => $local_ctx, 'term' => $term));
+ }
+ // prepend vocab to term
+ $mapping->{'@id'} = $active_ctx->{'@vocab'} . $term;
+ }
+ }
+
+ // optimization to store length of @id once for _compactIri
+ $mapping->_id_length = strlen($mapping->{'@id'});
+
+ // IRI mapping now defined
+ $defined->{$term} = true;
+
+ if(property_exists($value, '@type')) {
+ $type = $value->{'@type'};
+ if(!is_string($type)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @type values must be strings.',
+ 'jsonld.SyntaxError', 'invalid type mapping',
+ array('context' => $local_ctx));
+ }
+
+ if($type !== '@id' && $type !== '@vocab') {
+ // expand @type to full IRI
+ $type = $this->_expandIri(
+ $active_ctx, $type, array('vocab' => true), $local_ctx, $defined);
+ if(!self::_isAbsoluteIri($type)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; an @context @type value must ' .
+ 'be an absolute IRI.', 'jsonld.SyntaxError',
+ 'invalid type mapping', array('context' => $local_ctx));
+ }
+ if(strpos($type, '_:') === 0) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; an @context @type values must ' .
+ 'be an IRI, not a blank node identifier.',
+ 'jsonld.SyntaxError', 'invalid type mapping',
+ array('context' => $local_ctx));
+ }
+ }
+
+ // add @type to mapping
+ $mapping->{'@type'} = $type;
+ }
+
+ if(property_exists($value, '@container')) {
+ $container = $value->{'@container'};
+ if($container !== '@list' && $container !== '@set' &&
+ $container !== '@index' && $container !== '@language') {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @container value must be ' .
+ 'one of the following: @list, @set, @index, or @language.',
+ 'jsonld.SyntaxError', 'invalid container mapping',
+ array('context' => $local_ctx));
+ }
+ if($mapping->reverse && $container !== '@index' &&
+ $container !== '@set' && $container !== null) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @container value for a @reverse ' +
+ 'type definition must be @index or @set.',
+ 'jsonld.SyntaxError', 'invalid reverse property',
+ array('context' => $local_ctx));
+ }
+
+ // add @container to mapping
+ $mapping->{'@container'} = $container;
+ }
+
+ if(property_exists($value, '@language') &&
+ !property_exists($value, '@type')) {
+ $language = $value->{'@language'};
+ if($language !== null && !is_string($language)) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context @language value must be ' .
+ 'a string or null.', 'jsonld.SyntaxError',
+ 'invalid language mapping', array('context' => $local_ctx));
+ }
+
+ // add @language to mapping
+ if($language !== null) {
+ $language = strtolower($language);
+ }
+ $mapping->{'@language'} = $language;
+ }
+
+ // disallow aliasing @context and @preserve
+ $id = $mapping->{'@id'};
+ if($id === '@context' || $id === '@preserve') {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; @context and @preserve cannot be aliased.',
+ 'jsonld.SyntaxError', 'invalid keyword alias',
+ array('context' => $local_ctx));
+ }
+ }
+
+ /**
+ * Expands a string to a full IRI. The string may be a term, a prefix, a
+ * relative IRI, or an absolute IRI. The associated absolute IRI will be
+ * returned.
+ *
+ * @param stdClass $active_ctx the current active context.
+ * @param string $value the string to expand.
+ * @param assoc $relative_to options for how to resolve relative IRIs:
+ * base: true to resolve against the base IRI, false not to.
+ * vocab: true to concatenate after @vocab, false not to.
+ * @param stdClass $local_ctx the local context being processed (only given
+ * if called during document processing).
+ * @param defined a map for tracking cycles in context definitions (only given
+ * if called during document processing).
+ *
+ * @return mixed the expanded value.
+ */
+ function _expandIri(
+ $active_ctx, $value, $relative_to=array(), $local_ctx=null, $defined=null) {
+ // already expanded
+ if($value === null || self::_isKeyword($value)) {
+ return $value;
+ }
+
+ // define term dependency if not defined
+ if($local_ctx !== null && property_exists($local_ctx, $value) &&
+ !self::_hasKeyValue($defined, $value, true)) {
+ $this->_createTermDefinition($active_ctx, $local_ctx, $value, $defined);
+ }
+
+ if(isset($relative_to['vocab']) && $relative_to['vocab']) {
+ if(property_exists($active_ctx->mappings, $value)) {
+ $mapping = $active_ctx->mappings->{$value};
+
+ // value is explicitly ignored with a null mapping
+ if($mapping === null) {
+ return null;
+ }
+
+ // value is a term
+ return $mapping->{'@id'};
+ }
+ }
+
+ // split value into prefix:suffix
+ $colon = strpos($value, ':');
+ if($colon !== false) {
+ $prefix = substr($value, 0, $colon);
+ $suffix = substr($value, $colon + 1);
+
+ // do not expand blank nodes (prefix of '_') or already-absolute
+ // IRIs (suffix of '//')
+ if($prefix === '_' || strpos($suffix, '//') === 0) {
+ return $value;
+ }
+
+ // prefix dependency not defined, define it
+ if($local_ctx !== null && property_exists($local_ctx, $prefix)) {
+ $this->_createTermDefinition(
+ $active_ctx, $local_ctx, $prefix, $defined);
+ }
+
+ // use mapping if prefix is defined
+ if(property_exists($active_ctx->mappings, $prefix)) {
+ $mapping = $active_ctx->mappings->{$prefix};
+ if($mapping) {
+ return $mapping->{'@id'} . $suffix;
+ }
+ }
+
+ // already absolute IRI
+ return $value;
+ }
+
+ // prepend vocab
+ if(isset($relative_to['vocab']) && $relative_to['vocab'] &&
+ property_exists($active_ctx, '@vocab')) {
+ return $active_ctx->{'@vocab'} . $value;
+ }
+
+ // prepend base
+ $rval = $value;
+ if(isset($relative_to['base']) && $relative_to['base']) {
+ $rval = jsonld_prepend_base($active_ctx->{'@base'}, $rval);
+ }
+
+ return $rval;
+ }
+
+ /**
+ * Finds all @context URLs in the given JSON-LD input.
+ *
+ * @param mixed $input the JSON-LD input.
+ * @param stdClass $urls a map of URLs (url => false/@contexts).
+ * @param bool $replace true to replace the URLs in the given input with
+ * the @contexts from the urls map, false not to.
+ * @param string $base the base URL to resolve relative URLs with.
+ */
+ protected function _findContextUrls($input, $urls, $replace, $base) {
+ if(is_array($input)) {
+ foreach($input as $e) {
+ $this->_findContextUrls($e, $urls, $replace, $base);
+ }
+ } else if(is_object($input)) {
+ foreach($input as $k => &$v) {
+ if($k !== '@context') {
+ $this->_findContextUrls($v, $urls, $replace, $base);
+ continue;
+ }
+
+ // array @context
+ if(is_array($v)) {
+ $length = count($v);
+ for($i = 0; $i < $length; ++$i) {
+ if(is_string($v[$i])) {
+ $url = jsonld_prepend_base($base, $v[$i]);
+ // replace w/@context if requested
+ if($replace) {
+ $ctx = $urls->{$url};
+ if(is_array($ctx)) {
+ // add flattened context
+ array_splice($v, $i, 1, $ctx);
+ $i += count($ctx) - 1;
+ $length = count($v);
+ } else {
+ $v[$i] = $ctx;
+ }
+ } else if(!property_exists($urls, $url)) {
+ // @context URL found
+ $urls->{$url} = false;
+ }
+ }
+ }
+ } else if(is_string($v)) {
+ // string @context
+ $v = jsonld_prepend_base($base, $v);
+ // replace w/@context if requested
+ if($replace) {
+ $input->{$k} = $urls->{$v};
+ } else if(!property_exists($urls, $v)) {
+ // @context URL found
+ $urls->{$v} = false;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves external @context URLs using the given document loader. Each
+ * instance of @context in the input that refers to a URL will be replaced
+ * with the JSON @context found at that URL.
+ *
+ * @param mixed $input the JSON-LD input with possible contexts.
+ * @param stdClass $cycles an object for tracking context cycles.
+ * @param callable $load_document(url) the document loader.
+ * @param base $base the base URL to resolve relative URLs against.
+ *
+ * @return mixed the result.
+ */
+ protected function _retrieveContextUrls(
+ &$input, $cycles, $load_document, $base='') {
+ if(count(get_object_vars($cycles)) > self::MAX_CONTEXT_URLS) {
+ throw new JsonLdException(
+ 'Maximum number of @context URLs exceeded.',
+ 'jsonld.ContextUrlError', 'loading remote context failed',
+ array('max' => self::MAX_CONTEXT_URLS));
+ }
+
+ // for tracking the URLs to retrieve
+ $urls = new stdClass();
+
+ // regex for validating URLs
+ $regex = '/(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/';
+
+ // find all URLs in the given input
+ $this->_findContextUrls($input, $urls, false, $base);
+
+ // queue all unretrieved URLs
+ $queue = array();
+ foreach($urls as $url => $ctx) {
+ if($ctx === false) {
+ // validate URL
+ if(!preg_match($regex, $url)) {
+ throw new JsonLdException(
+ 'Malformed or unsupported URL.', 'jsonld.InvalidUrl',
+ 'loading remote context failed', array('url' => $url));
+ }
+ $queue[] = $url;
+ }
+ }
+
+ // retrieve URLs in queue
+ foreach($queue as $url) {
+ // check for context URL cycle
+ if(property_exists($cycles, $url)) {
+ throw new JsonLdException(
+ 'Cyclical @context URLs detected.',
+ 'jsonld.ContextUrlError', 'recursive context inclusion',
+ array('url' => $url));
+ }
+ $_cycles = self::copy($cycles);
+ $_cycles->{$url} = true;
+
+ // retrieve URL
+ $remote_doc = call_user_func($load_document, $url);
+ $ctx = $remote_doc->document;
+
+ // parse string context as JSON
+ if(is_string($ctx)) {
+ try {
+ $ctx = self::_parse_json($ctx);
+ } catch(Exception $e) {
+ throw new JsonLdException(
+ 'Could not parse JSON from URL.',
+ 'jsonld.ParseError', 'loading remote context failed',
+ array('url' => $url), $e);
+ }
+ }
+
+ // ensure ctx is an object
+ if(!is_object($ctx)) {
+ throw new JsonLdException(
+ 'Derefencing a URL did not result in a valid JSON-LD object.',
+ 'jsonld.InvalidUrl', 'invalid remote context', array('url' => $url));
+ }
+
+ // use empty context if no @context key is present
+ if(!property_exists($ctx, '@context')) {
+ $ctx = (object)array('@context' => new stdClass());
+ } else {
+ $ctx = (object)array('@context' => $ctx->{'@context'});
+ }
+
+ // append context URL to context if given
+ if($remote_doc->contextUrl !== null) {
+ $ctx->{'@context'} = self::arrayify($ctx->{'@context'});
+ $ctx->{'@context'}[] = $remote_doc->contextUrl;
+ }
+
+ // recurse
+ $this->_retrieveContextUrls($ctx, $_cycles, $load_document, $url);
+ $urls->{$url} = $ctx->{'@context'};
+ }
+
+ // replace all URLS in the input
+ $this->_findContextUrls($input, $urls, true, $base);
+ }
+
+ /**
+ * Gets the initial context.
+ *
+ * @param assoc $options the options to use.
+ * base the document base IRI.
+ *
+ * @return stdClass the initial context.
+ */
+ protected function _getInitialContext($options) {
+ return (object)array(
+ '@base' => jsonld_parse_url($options['base']),
+ 'mappings' => new stdClass(),
+ 'inverse' => null);
+ }
+
+ /**
+ * Generates an inverse context for use in the compaction algorithm, if
+ * not already generated for the given active context.
+ *
+ * @param stdClass $active_ctx the active context to use.
+ *
+ * @return stdClass the inverse context.
+ */
+ protected function _getInverseContext($active_ctx) {
+ // inverse context already generated
+ if($active_ctx->inverse) {
+ return $active_ctx->inverse;
+ }
+
+ $inverse = $active_ctx->inverse = new stdClass();
+
+ // variables for building fast CURIE map
+ $fast_curie_map = $active_ctx->fast_curie_map = new ArrayObject();
+ $iris_to_terms = array();
+
+ // handle default language
+ $default_language = '@none';
+ if(property_exists($active_ctx, '@language')) {
+ $default_language = $active_ctx->{'@language'};
+ }
+
+ // create term selections for each mapping in the context, ordered by
+ // shortest and then lexicographically least
+ $mappings = $active_ctx->mappings;
+ $terms = array_keys((array)$mappings);
+ usort($terms, array($this, '_compareShortestLeast'));
+ foreach($terms as $term) {
+ $mapping = $mappings->{$term};
+ if($mapping === null) {
+ continue;
+ }
+
+ // add term selection where it applies
+ if(property_exists($mapping, '@container')) {
+ $container = $mapping->{'@container'};
+ } else {
+ $container = '@none';
+ }
+
+ // iterate over every IRI in the mapping
+ $iris = $mapping->{'@id'};
+ $iris = self::arrayify($iris);
+ foreach($iris as $iri) {
+ $is_keyword = self::_isKeyword($iri);
+
+ // initialize container map
+ if(!property_exists($inverse, $iri)) {
+ $inverse->{$iri} = new stdClass();
+ if(!$is_keyword && !$mapping->_term_has_colon) {
+ // init IRI to term map and fast CURIE map
+ $iris_to_terms[$iri] = new ArrayObject();
+ $iris_to_terms[$iri][] = $term;
+ $fast_curie_entry = (object)array(
+ 'iri' => $iri, 'terms' => $iris_to_terms[$iri]);
+ if(!array_key_exists($iri[0], (array)$fast_curie_map)) {
+ $fast_curie_map[$iri[0]] = new ArrayObject();
+ }
+ $fast_curie_map[$iri[0]][] = $fast_curie_entry;
+ }
+ } else if(!$is_keyword && !$mapping->_term_has_colon) {
+ // add IRI to term match
+ $iris_to_terms[$iri][] = $term;
+ }
+ $container_map = $inverse->{$iri};
+
+ // add new entry
+ if(!property_exists($container_map, $container)) {
+ $container_map->{$container} = (object)array(
+ '@language' => new stdClass(),
+ '@type' => new stdClass());
+ }
+ $entry = $container_map->{$container};
+
+ if($mapping->reverse) {
+ // term is preferred for values using @reverse
+ $this->_addPreferredTerm(
+ $mapping, $term, $entry->{'@type'}, '@reverse');
+ } else if(property_exists($mapping, '@type')) {
+ // term is preferred for values using specific type
+ $this->_addPreferredTerm(
+ $mapping, $term, $entry->{'@type'}, $mapping->{'@type'});
+ } else if(property_exists($mapping, '@language')) {
+ // term is preferred for values using specific language
+ $language = $mapping->{'@language'};
+ if($language === null) {
+ $language = '@null';
+ }
+ $this->_addPreferredTerm(
+ $mapping, $term, $entry->{'@language'}, $language);
+ } else {
+ // term is preferred for values w/default language or no type and
+ // no language
+ // add an entry for the default language
+ $this->_addPreferredTerm(
+ $mapping, $term, $entry->{'@language'}, $default_language);
+
+ // add entries for no type and no language
+ $this->_addPreferredTerm(
+ $mapping, $term, $entry->{'@type'}, '@none');
+ $this->_addPreferredTerm(
+ $mapping, $term, $entry->{'@language'}, '@none');
+ }
+ }
+ }
+
+ // build fast CURIE map
+ foreach($fast_curie_map as $key => $value) {
+ $this->_buildIriMap($fast_curie_map, $key, 1);
+ }
+
+ return $inverse;
+ }
+
+ /**
+ * Runs a recursive algorithm to build a lookup map for quickly finding
+ * potential CURIEs.
+ *
+ * @param ArrayObject $iri_map the map to build.
+ * @param string $key the current key in the map to work on.
+ * @param int $idx the index into the IRI to compare.
+ */
+ function _buildIriMap($iri_map, $key, $idx) {
+ $entries = $iri_map[$key];
+ $next = $iri_map[$key] = new ArrayObject();
+
+ foreach($entries as $entry) {
+ $iri = $entry->iri;
+ if($idx >= strlen($iri)) {
+ $letter = '';
+ } else {
+ $letter = $iri[$idx];
+ }
+ if(!isset($next[$letter])) {
+ $next[$letter] = new ArrayObject();
+ }
+ $next[$letter][] = $entry;
+ }
+
+ foreach($next as $key => $value) {
+ if($key === '') {
+ continue;
+ }
+ $this->_buildIriMap($next, $key, $idx + 1);
+ }
+ }
+
+ /**
+ * Adds the term for the given entry if not already added.
+ *
+ * @param stdClass $mapping the term mapping.
+ * @param string $term the term to add.
+ * @param stdClass $entry the inverse context type_or_language entry to
+ * add to.
+ * @param string $type_or_language_value the key in the entry to add to.
+ */
+ function _addPreferredTerm($mapping, $term, $entry, $type_or_language_value) {
+ if(!property_exists($entry, $type_or_language_value)) {
+ $entry->{$type_or_language_value} = $term;
+ }
+ }
+
+ /**
+ * Clones an active context, creating a child active context.
+ *
+ * @return stdClass a clone (child) of the active context.
+ */
+ protected function _cloneActiveContext($active_ctx) {
+ $child = new stdClass();
+ $child->{'@base'} = $active_ctx->{'@base'};
+ $child->mappings = self::copy($active_ctx->mappings);
+ $child->inverse = null;
+ if(property_exists($active_ctx, '@language')) {
+ $child->{'@language'} = $active_ctx->{'@language'};
+ }
+ if(property_exists($active_ctx, '@vocab')) {
+ $child->{'@vocab'} = $active_ctx->{'@vocab'};
+ }
+ return $child;
+ }
+
+ /**
+ * Returns whether or not the given value is a keyword.
+ *
+ * @param string $v the value to check.
+ *
+ * @return bool true if the value is a keyword, false if not.
+ */
+ protected static function _isKeyword($v) {
+ if(!is_string($v)) {
+ return false;
+ }
+ switch($v) {
+ case '@base':
+ case '@context':
+ case '@container':
+ case '@default':
+ case '@embed':
+ case '@explicit':
+ case '@graph':
+ case '@id':
+ case '@index':
+ case '@language':
+ case '@list':
+ case '@omitDefault':
+ case '@preserve':
+ case '@requireAll':
+ case '@reverse':
+ case '@set':
+ case '@type':
+ case '@value':
+ case '@vocab':
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the given value is an empty Object.
+ *
+ * @param mixed $v the value to check.
+ *
+ * @return bool true if the value is an empty Object, false if not.
+ */
+ protected static function _isEmptyObject($v) {
+ return is_object($v) && count(get_object_vars($v)) === 0;
+ }
+
+ /**
+ * Throws an exception if the given value is not a valid @type value.
+ *
+ * @param mixed $v the value to check.
+ */
+ protected static function _validateTypeValue($v) {
+ // must be a string or empty object
+ if(is_string($v) || self::_isEmptyObject($v)) {
+ return;
+ }
+
+ // must be an array
+ $is_valid = false;
+ if(is_array($v)) {
+ // must contain only strings
+ $is_valid = true;
+ foreach($v as $e) {
+ if(!(is_string($e))) {
+ $is_valid = false;
+ break;
+ }
+ }
+ }
+
+ if(!$is_valid) {
+ throw new JsonLdException(
+ 'Invalid JSON-LD syntax; "@type" value must a string, an array ' .
+ 'of strings, or an empty object.',
+ 'jsonld.SyntaxError', 'invalid type value', array('value' => $v));
+ }
+ }
+
+ /**
+ * Returns true if the given value is a subject with properties.
+ *
+ * @param mixed $v the value to check.
+ *
+ * @return bool true if the value is a subject with properties, false if not.
+ */
+ protected static function _isSubject($v) {
+ // Note: A value is a subject if all of these hold true:
+ // 1. It is an Object.
+ // 2. It is not a @value, @set, or @list.
+ // 3. It has more than 1 key OR any existing key is not @id.
+ $rval = false;
+ if(is_object($v) &&
+ !property_exists($v, '@value') &&
+ !property_exists($v, '@set') &&
+ !property_exists($v, '@list')) {
+ $count = count(get_object_vars($v));
+ $rval = ($count > 1 || !property_exists($v, '@id'));
+ }
+ return $rval;
+ }
+
+ /**
+ * Returns true if the given value is a subject reference.
+ *
+ * @param mixed $v the value to check.
+ *
+ * @return bool true if the value is a subject reference, false if not.
+ */
+ protected static function _isSubjectReference($v) {
+ // Note: A value is a subject reference if all of these hold true:
+ // 1. It is an Object.
+ // 2. It has a single key: @id.
+ return (is_object($v) && count(get_object_vars($v)) === 1 &&
+ property_exists($v, '@id'));
+ }
+
+ /**
+ * Returns true if the given value is a @value.
+ *
+ * @param mixed $v the value to check.
+ *
+ * @return bool true if the value is a @value, false if not.
+ */
+ protected static function _isValue($v) {
+ // Note: A value is a @value if all of these hold true:
+ // 1. It is an Object.
+ // 2. It has the @value property.
+ return is_object($v) && property_exists($v, '@value');
+ }
+
+ /**
+ * Returns true if the given value is a @list.
+ *
+ * @param mixed $v the value to check.
+ *
+ * @return bool true if the value is a @list, false if not.
+ */
+ protected static function _isList($v) {
+ // Note: A value is a @list if all of these hold true:
+ // 1. It is an Object.
+ // 2. It has the @list property.
+ return is_object($v) && property_exists($v, '@list');
+ }
+
+ /**
+ * Returns true if the given value is a blank node.
+ *
+ * @param mixed $v the value to check.
+ *
+ * @return bool true if the value is a blank node, false if not.
+ */
+ protected static function _isBlankNode($v) {
+ // Note: A value is a blank node if all of these hold true:
+ // 1. It is an Object.
+ // 2. If it has an @id key its value begins with '_:'.
+ // 3. It has no keys OR is not a @value, @set, or @list.
+ $rval = false;
+ if(is_object($v)) {
+ if(property_exists($v, '@id')) {
+ $rval = (strpos($v->{'@id'}, '_:') === 0);
+ } else {
+ $rval = (count(get_object_vars($v)) === 0 ||
+ !(property_exists($v, '@value') ||
+ property_exists($v, '@set') ||
+ property_exists($v, '@list')));
+ }
+ }
+ return $rval;
+ }
+
+ /**
+ * Returns true if the given value is an absolute IRI, false if not.
+ *
+ * @param string $v the value to check.
+ *
+ * @return bool true if the value is an absolute IRI, false if not.
+ */
+ protected static function _isAbsoluteIri($v) {
+ return strpos($v, ':') !== false;
+ }
+
+ /**
+ * Returns true if the given target has the given key and its
+ * value equals is the given value.
+ *
+ * @param stdClass $target the target object.
+ * @param string key the key to check.
+ * @param mixed $value the value to check.
+ *
+ * @return bool true if the target has the given key and its value matches.
+ */
+ protected static function _hasKeyValue($target, $key, $value) {
+ return (property_exists($target, $key) && $target->{$key} === $value);
+ }
+
+ /**
+ * Returns true if both of the given objects have the same value for the
+ * given key or if neither of the objects contain the given key.
+ *
+ * @param stdClass $o1 the first object.
+ * @param stdClass $o2 the second object.
+ * @param string key the key to check.
+ *
+ * @return bool true if both objects have the same value for the key or
+ * neither has the key.
+ */
+ protected static function _compareKeyValues($o1, $o2, $key) {
+ if(property_exists($o1, $key)) {
+ return property_exists($o2, $key) && $o1->{$key} === $o2->{$key};
+ }
+ return !property_exists($o2, $key);
+ }
+
+ /**
+ * Parses JSON and sets an appropriate exception message on error.
+ *
+ * @param string $json the JSON to parse.
+ *
+ * @return mixed the parsed JSON object or array.
+ */
+ protected static function _parse_json($json) {
+ $rval = json_decode($json);
+ $error = json_last_error();
+ if($error === JSON_ERROR_NONE && $rval === null) {
+ $error = JSON_ERROR_SYNTAX;
+ }
+ switch($error) {
+ case JSON_ERROR_NONE:
+ break;
+ case JSON_ERROR_DEPTH:
+ throw new JsonLdException(
+ 'Could not parse JSON; the maximum stack depth has been exceeded.',
+ 'jsonld.ParseError');
+ case JSON_ERROR_STATE_MISMATCH:
+ throw new JsonLdException(
+ 'Could not parse JSON; invalid or malformed JSON.',
+ 'jsonld.ParseError');
+ case JSON_ERROR_CTRL_CHAR:
+ case JSON_ERROR_SYNTAX:
+ throw new JsonLdException(
+ 'Could not parse JSON; syntax error, malformed JSON.',
+ 'jsonld.ParseError');
+ case JSON_ERROR_UTF8:
+ throw new JsonLdException(
+ 'Could not parse JSON from URL; malformed UTF-8 characters.',
+ 'jsonld.ParseError');
+ default:
+ throw new JsonLdException(
+ 'Could not parse JSON from URL; unknown error.',
+ 'jsonld.ParseError');
+ }
+ return $rval;
+ }
+}
+
+// register the N-Quads RDF parser
+jsonld_register_rdf_parser(
+ 'application/nquads', array('JsonLdProcessor', 'parseNQuads'));
+
+/**
+ * A JSON-LD Exception.
+ */
+class JsonLdException extends Exception {
+ public function __construct(
+ $msg, $type, $code='error', $details=null, $previous=null) {
+ $this->type = $type;
+ $this->code = $code;
+ $this->details = $details;
+ $this->cause = $previous;
+ parent::__construct($msg, 0, $previous);
+ }
+ public function __toString() {
+ $rval = __CLASS__ . ": [{$this->type}]: {$this->message}\n";
+ if($this->code) {
+ $rval .= 'Code: ' . $this->code . "\n";
+ }
+ if($this->details) {
+ $rval .= 'Details: ' . print_r($this->details, true) . "\n";
+ }
+ if($this->cause) {
+ $rval .= 'Cause: ' . $this->cause;
+ }
+ $rval .= $this->getTraceAsString() . "\n";
+ return $rval;
+ }
+};
+
+/**
+ * A UniqueNamer issues unique names, keeping track of any previously issued
+ * names.
+ */
+class UniqueNamer {
+ /**
+ * Constructs a new UniqueNamer.
+ *
+ * @param prefix the prefix to use ('<prefix><counter>').
+ */
+ public function __construct($prefix) {
+ $this->prefix = $prefix;
+ $this->counter = 0;
+ $this->existing = new stdClass();
+ $this->order = array();
+ }
+
+ /**
+ * Clones this UniqueNamer.
+ */
+ public function __clone() {
+ $this->existing = clone $this->existing;
+ }
+
+ /**
+ * Gets the new name for the given old name, where if no old name is given
+ * a new name will be generated.
+ *
+ * @param mixed [$old_name] the old name to get the new name for.
+ *
+ * @return string the new name.
+ */
+ public function getName($old_name=null) {
+ // return existing old name
+ if($old_name && property_exists($this->existing, $old_name)) {
+ return $this->existing->{$old_name};
+ }
+
+ // get next name
+ $name = $this->prefix . $this->counter;
+ $this->counter += 1;
+
+ // save mapping
+ if($old_name !== null) {
+ $this->existing->{$old_name} = $name;
+ $this->order[] = $old_name;
+ }
+
+ return $name;
+ }
+
+ /**
+ * Returns true if the given old name has already been assigned a new name.
+ *
+ * @param string $old_name the old name to check.
+ *
+ * @return true if the old name has been assigned a new name, false if not.
+ */
+ public function isNamed($old_name) {
+ return property_exists($this->existing, $old_name);
+ }
+}
+
+/**
+ * A Permutator iterates over all possible permutations of the given array
+ * of elements.
+ */
+class Permutator {
+ /**
+ * Constructs a new Permutator.
+ *
+ * @param array $list the array of elements to iterate over.
+ */
+ public function __construct($list) {
+ // original array
+ $this->list = $list;
+ sort($this->list);
+ // indicates whether there are more permutations
+ $this->done = false;
+ // directional info for permutation algorithm
+ $this->left = new stdClass();
+ foreach($list as $v) {
+ $this->left->{$v} = true;
+ }
+ }
+
+ /**
+ * Returns true if there is another permutation.
+ *
+ * @return bool true if there is another permutation, false if not.
+ */
+ public function hasNext() {
+ return !$this->done;
+ }
+
+ /**
+ * Gets the next permutation. Call hasNext() to ensure there is another one
+ * first.
+ *
+ * @return array the next permutation.
+ */
+ public function next() {
+ // copy current permutation
+ $rval = $this->list;
+
+ /* Calculate the next permutation using the Steinhaus-Johnson-Trotter
+ permutation algorithm. */
+
+ // get largest mobile element k
+ // (mobile: element is greater than the one it is looking at)
+ $k = null;
+ $pos = 0;
+ $length = count($this->list);
+ for($i = 0; $i < $length; ++$i) {
+ $element = $this->list[$i];
+ $left = $this->left->{$element};
+ if(($k === null || $element > $k) &&
+ (($left && $i > 0 && $element > $this->list[$i - 1]) ||
+ (!$left && $i < ($length - 1) && $element > $this->list[$i + 1]))) {
+ $k = $element;
+ $pos = $i;
+ }
+ }
+
+ // no more permutations
+ if($k === null) {
+ $this->done = true;
+ } else {
+ // swap k and the element it is looking at
+ $swap = $this->left->{$k} ? $pos - 1 : $pos + 1;
+ $this->list[$pos] = $this->list[$swap];
+ $this->list[$swap] = $k;
+
+ // reverse the direction of all elements larger than k
+ for($i = 0; $i < $length; ++$i) {
+ if($this->list[$i] > $k) {
+ $this->left->{$this->list[$i]} = !$this->left->{$this->list[$i]};
+ }
+ }
+ }
+
+ return $rval;
+ }
+}
+
+/**
+ * An ActiveContextCache caches active contexts so they can be reused without
+ * the overhead of recomputing them.
+ */
+class ActiveContextCache {
+ /**
+ * Constructs a new ActiveContextCache.
+ *
+ * @param int size the maximum size of the cache, defaults to 100.
+ */
+ public function __construct($size=100) {
+ $this->order = array();
+ $this->cache = new stdClass();
+ $this->size = $size;
+ }
+
+ /**
+ * Gets an active context from the cache based on the current active
+ * context and the new local context.
+ *
+ * @param stdClass $active_ctx the current active context.
+ * @param stdClass $local_ctx the new local context.
+ *
+ * @return mixed a shared copy of the cached active context or null.
+ */
+ public function get($active_ctx, $local_ctx) {
+ $key1 = serialize($active_ctx);
+ $key2 = serialize($local_ctx);
+ if(property_exists($this->cache, $key1)) {
+ $level1 = $this->cache->{$key1};
+ if(property_exists($level1, $key2)) {
+ return $level1->{$key2};
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets an active context in the cache based on the previous active
+ * context and the just-processed local context.
+ *
+ * @param stdClass $active_ctx the previous active context.
+ * @param stdClass $local_ctx the just-processed local context.
+ * @param stdClass $result the resulting active context.
+ */
+ public function set($active_ctx, $local_ctx, $result) {
+ if(count($this->order) === $this->size) {
+ $entry = array_shift($this->order);
+ unset($this->cache->{$entry->activeCtx}->{$entry->localCtx});
+ }
+ $key1 = serialize($active_ctx);
+ $key2 = serialize($local_ctx);
+ $this->order[] = (object)array(
+ 'activeCtx' => $key1, 'localCtx' => $key2);
+ if(!property_exists($this->cache, $key1)) {
+ $this->cache->{$key1} = new stdClass();
+ }
+ $this->cache->{$key1}->{$key2} = JsonLdProcessor::copy($result);
+ }
+}
+
+/* end of file, omit ?> */
diff --git a/library/jsonld/test.php b/library/jsonld/test.php
new file mode 100644
index 000000000..11b72ac09
--- /dev/null
+++ b/library/jsonld/test.php
@@ -0,0 +1,765 @@
+<?php
+/**
+ * PHP unit tests for JSON-LD.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2013-2014 Digital Bazaar, Inc. All rights reserved.
+ */
+require_once('jsonld.php');
+
+class JsonLdTestCase extends PHPUnit_Framework_TestCase {
+ /**
+ * Runs this test case. Overridden to attach to EARL report w/o need for
+ * an external XML configuration file.
+ *
+ * @param PHPUnit_Framework_TestResult $result the test result.
+ */
+ public function run(PHPUnit_Framework_TestResult $result = NULL) {
+ global $EARL;
+ $EARL->attach($result);
+ $this->result = $result;
+ parent::run($result);
+ }
+
+ /**
+ * Tests expansion.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group expand
+ * @group json-ld.org
+ * @dataProvider expandProvider
+ */
+ public function testExpand($test) {
+ $this->test = $test;
+ $input = $test->readUrl('input');
+ $options = $test->createOptions();
+ $test->run('jsonld_expand', array($input, $options));
+ }
+
+ /**
+ * Tests compaction.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group compact
+ * @group json-ld.org
+ * @dataProvider compactProvider
+ */
+ public function testCompact($test) {
+ $this->test = $test;
+ $input = $test->readUrl('input');
+ $context = $test->readProperty('context');
+ $options = $test->createOptions();
+ $test->run('jsonld_compact', array($input, $context, $options));
+ }
+
+ /**
+ * Tests flatten.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group flatten
+ * @group json-ld.org
+ * @dataProvider flattenProvider
+ */
+ public function testFlatten($test) {
+ $this->test = $test;
+ $input = $test->readUrl('input');
+ $context = $test->readProperty('context');
+ $options = $test->createOptions();
+ $test->run('jsonld_flatten', array($input, $context, $options));
+ }
+
+ /**
+ * Tests serialization to RDF.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group toRdf
+ * @group json-ld.org
+ * @dataProvider toRdfProvider
+ */
+ public function testToRdf($test) {
+ $this->test = $test;
+ $input = $test->readUrl('input');
+ $options = $test->createOptions(array('format' => 'application/nquads'));
+ $test->run('jsonld_to_rdf', array($input, $options));
+ }
+
+ /**
+ * Tests deserialization from RDF.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group fromRdf
+ * @group json-ld.org
+ * @dataProvider fromRdfProvider
+ */
+ public function testFromRdf($test) {
+ $this->test = $test;
+ $input = $test->readProperty('input');
+ $options = $test->createOptions(array('format' => 'application/nquads'));
+ $test->run('jsonld_from_rdf', array($input, $options));
+ }
+
+ /**
+ * Tests framing.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group frame
+ * @group json-ld.org
+ * @dataProvider frameProvider
+ */
+ public function testFrame($test) {
+ $this->test = $test;
+ $input = $test->readUrl('input');
+ $frame = $test->readProperty('frame');
+ $options = $test->createOptions();
+ $test->run('jsonld_frame', array($input, $frame, $options));
+ }
+
+ /**
+ * Tests normalization.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group normalize
+ * @group json-ld.org
+ * @dataProvider normalizeProvider
+ */
+ public function testNormalize($test) {
+ $this->test = $test;
+ $input = $test->readUrl('input');
+ $options = $test->createOptions(array('format' => 'application/nquads'));
+ $test->run('jsonld_normalize', array($input, $options));
+ }
+
+ /**
+ * Tests URGNA2012 normalization.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group normalize
+ * @group normalization
+ * @dataProvider urgna2012Provider
+ */
+ public function testUrgna2012($test) {
+ $this->test = $test;
+ $input = $test->readProperty('action');
+ $options = $test->createOptions(array(
+ 'algorithm' => 'URGNA2012',
+ 'inputFormat' => 'application/nquads',
+ 'format' => 'application/nquads'));
+ $test->run('jsonld_normalize', array($input, $options));
+ }
+
+ /**
+ * Tests URDNA2015 normalization.
+ *
+ * @param JsonLdTest $test the test to run.
+ *
+ * @group normalize
+ * @group normalization
+ * @dataProvider urdna2015Provider
+ */
+ public function testUrdna2015($test) {
+ $this->test = $test;
+ $input = $test->readProperty('action');
+ $options = $test->createOptions(array(
+ 'algorithm' => 'URDNA2015',
+ 'inputFormat' => 'application/nquads',
+ 'format' => 'application/nquads'));
+ $test->run('jsonld_normalize', array($input, $options));
+ }
+
+ public function expandProvider() {
+ return new JsonLdTestIterator('jld:ExpandTest');
+ }
+
+ public function compactProvider() {
+ return new JsonLdTestIterator('jld:CompactTest');
+ }
+
+ public function flattenProvider() {
+ return new JsonLdTestIterator('jld:FlattenTest');
+ }
+
+ public function toRdfProvider() {
+ return new JsonLdTestIterator('jld:ToRDFTest');
+ }
+
+ public function fromRdfProvider() {
+ return new JsonLdTestIterator('jld:FromRDFTest');
+ }
+
+ public function normalizeProvider() {
+ return new JsonLdTestIterator('jld:NormalizeTest');
+ }
+
+ public function frameProvider() {
+ return new JsonLdTestIterator('jld:FrameTest');
+ }
+
+ public function urgna2012Provider() {
+ return new JsonLdTestIterator('rdfn:Urgna2012EvalTest');
+ }
+
+ public function urdna2015Provider() {
+ return new JsonLdTestIterator('rdfn:Urdna2015EvalTest');
+ }
+}
+
+class JsonLdManifest {
+ public function __construct($data, $filename) {
+ $this->data = $data;
+ $this->filename = $filename;
+ $this->dirname = dirname($filename);
+ }
+
+ public function load(&$tests) {
+ $entries = array_merge(
+ JsonLdProcessor::getValues($this->data, 'sequence'),
+ JsonLdProcessor::getValues($this->data, 'entries'));
+ $includes = JsonLdProcessor::getValues($this->data, 'include');
+ foreach($includes as $include) {
+ array_push($entries, $include . '.jsonld');
+ }
+ foreach($entries as $entry) {
+ if(is_string($entry)) {
+ $filename = join(
+ DIRECTORY_SEPARATOR, array($this->dirname, $entry));
+ $entry = Util::readJson($filename);
+ } else {
+ $filename = $this->filename;
+ }
+
+ if(JsonLdProcessor::hasValue($entry, '@type', 'mf:Manifest') ||
+ JsonLdProcessor::hasValue($entry, 'type', 'mf:Manifest')) {
+ // entry is another manifest
+ $manifest = new JsonLdManifest($entry, $filename);
+ $manifest->load($tests);
+ } else {
+ // assume entry is a test
+ $test = new JsonLdTest($this, $entry, $filename);
+ $types = array_merge(
+ JsonLdProcessor::getValues($test->data, '@type'),
+ JsonLdProcessor::getValues($test->data, 'type'));
+ foreach($types as $type) {
+ if(!isset($tests[$type])) {
+ $tests[$type] = array();
+ }
+ $tests[$type][] = $test;
+ }
+ }
+ }
+ }
+}
+
+class JsonLdTest {
+ public function __construct($manifest, $data, $filename) {
+ $this->manifest = $manifest;
+ $this->data = $data;
+ $this->filename = $filename;
+ $this->dirname = dirname($filename);
+ $this->isPositive =
+ JsonLdProcessor::hasValue(
+ $data, '@type', 'jld:PositiveEvaluationTest') ||
+ JsonLdProcessor::hasValue(
+ $data, 'type', 'jld:PositiveEvaluationTest');
+ $this->isNegative =
+ JsonLdProcessor::hasValue(
+ $data, '@type', 'jld:NegativeEvaluationTest') ||
+ JsonLdProcessor::hasValue(
+ $data, 'type', 'jld:NegativeEvaluationTest');
+
+ // generate test name
+ if(isset($manifest->data->name)) {
+ $manifestLabel = $manifest->data->name;
+ } else if(isset($manifest->data->label)) {
+ $manifestLabel = $manifest->data->label;
+ } else {
+ $manifestLabel = 'UNNAMED';
+ }
+ if(isset($this->data->id)) {
+ $testId = $this->data->id;
+ } else {
+ $testId = $this->data->{'@id'};
+ }
+ if(isset($this->data->name)) {
+ $testLabel = $this->data->name;
+ } else if(isset($this->data->label)) {
+ $testLabel = $this->data->label;
+ } else {
+ $testLabel = 'UNNAMED';
+ }
+
+ $this->name = $manifestLabel . ' ' . $testId . ' - ' . $testLabel;
+
+ // expand @id and input base
+ if(isset($manifest->data->baseIri)) {
+ $data->{'@id'} = ($manifest->data->baseIri .
+ basename($manifest->filename) . $data->{'@id'});
+ $this->base = $manifest->data->baseIri . $data->input;
+ }
+ }
+
+ private function _getResultProperty() {
+ if(isset($this->data->expect)) {
+ return 'expect';
+ } else if(isset($this->data->result)) {
+ return 'result';
+ } else {
+ throw new Exception('No test result property found.');
+ }
+ }
+
+ public function run($fn, $params) {
+ // read expected data
+ if($this->isNegative) {
+ $this->expected = $this->data->expect;
+ } else {
+ $this->expected = $this->readProperty($this->_getResultProperty());
+ }
+
+ try {
+ $this->actual = call_user_func_array($fn, $params);
+ if($this->isNegative) {
+ throw new Exception('Expected an error; one was not raised.');
+ }
+ PHPUnit_Framework_TestCase::assertEquals($this->expected, $this->actual);
+ } catch(Exception $e) {
+ // assume positive test
+ if($this->isNegative) {
+ $this->actual = $this->getJsonLdErrorCode($e);
+ PHPUnit_Framework_TestCase::assertEquals(
+ $this->expected, $this->actual);
+ } else {
+ throw $e;
+ }
+ }
+ }
+
+ public function readUrl($property) {
+ if(!property_exists($this->data, $property)) {
+ return null;
+ }
+ return $this->manifest->data->baseIri . $this->data->{$property};
+ }
+
+ public function readProperty($property) {
+ $data = $this->data;
+ if(!property_exists($data, $property)) {
+ return null;
+ }
+ $filename = join(
+ DIRECTORY_SEPARATOR, array($this->dirname, $data->{$property}));
+ $extension = pathinfo($filename, PATHINFO_EXTENSION);
+ if($extension === 'jsonld') {
+ return Util::readJson($filename);
+ }
+ return Util::readFile($filename);
+ }
+
+ public function createOptions($opts=array()) {
+ $http_options = array(
+ 'contentType', 'httpLink', 'httpStatus', 'redirectTo');
+ $test_options = (property_exists($this->data, 'option') ?
+ $this->data->option : array());
+ $options = array();
+ foreach($test_options as $k => $v) {
+ if(!in_array($k, $http_options)) {
+ $options[$k] = $v;
+ }
+ }
+ $options['documentLoader'] = $this->createDocumentLoader();
+ $options = array_merge($options, $opts);
+ if(isset($options['expandContext'])) {
+ $filename = join(
+ DIRECTORY_SEPARATOR, array($this->dirname, $options['expandContext']));
+ $options['expandContext'] = Util::readJson($filename);
+ }
+ return $options;
+ }
+
+ public function createDocumentLoader() {
+ $base = 'http://json-ld.org/test-suite';
+ $test = $this;
+
+ $load_locally = function($url) use ($test, $base) {
+ $doc = (object)array(
+ 'contextUrl' => null, 'documentUrl' => $url, 'document' => null);
+ $options = (property_exists($test->data, 'option') ?
+ $test->data->option : null);
+ if($options and $url === $test->base) {
+ if(property_exists($options, 'redirectTo') &&
+ property_exists($options, 'httpStatus') &&
+ $options->httpStatus >= '300') {
+ $doc->documentUrl = ($test->manifest->data->baseIri .
+ $options->redirectTo);
+ } else if(property_exists($options, 'httpLink')) {
+ $content_type = (property_exists($options, 'contentType') ?
+ $options->contentType : null);
+ $extension = pathinfo($url, PATHINFO_EXTENSION);
+ if(!$content_type && $extension === 'jsonld') {
+ $content_type = 'application/ld+json';
+ }
+ $link_header = $options->httpLink;
+ if(is_array($link_header)) {
+ $link_header = join(',', $link_header);
+ }
+ $link_header = jsonld_parse_link_header($link_header);
+ if(isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
+ $link_header = $link_header['http://www.w3.org/ns/json-ld#context'];
+ } else {
+ $link_header = null;
+ }
+ if($link_header && $content_type !== 'application/ld+json') {
+ if(is_array($link_header)) {
+ throw new Exception('multiple context link headers');
+ }
+ $doc->contextUrl = $link_header->target;
+ }
+ }
+ }
+ global $ROOT_MANIFEST_DIR;
+ if(strpos($doc->documentUrl, ':') === false) {
+ $filename = join(
+ DIRECTORY_SEPARATOR, array(
+ $ROOT_MANIFEST_DIR, $doc->documentUrl));
+ $doc->documentUrl = 'file://' . $filename;
+ } else {
+ $filename = join(
+ DIRECTORY_SEPARATOR, array(
+ $ROOT_MANIFEST_DIR, substr($doc->documentUrl, strlen($base))));
+ }
+ try {
+ $doc->document = Util::readJson($filename);
+ } catch(Exception $e) {
+ throw new Exception('loading document failed');
+ }
+ return $doc;
+ };
+
+ $local_loader = function($url) use ($test, $base, $load_locally) {
+ // always load remote-doc and non-base tests remotely
+ if((strpos($url, $base) !== 0 && strpos($url, ':') !== false) ||
+ $test->manifest->data->name === 'Remote document') {
+ return call_user_func('jsonld_default_document_loader', $url);
+ }
+
+ // attempt to load locally
+ return call_user_func($load_locally, $url);
+ };
+
+ return $local_loader;
+ }
+
+ public function getJsonLdErrorCode($err) {
+ if($err instanceof JsonLdException) {
+ if($err->getCode()) {
+ return $err->getCode();
+ }
+ if($err->cause) {
+ return $this->getJsonLdErrorCode($err->cause);
+ }
+ }
+ return $err->getMessage();
+ }
+}
+
+class JsonLdTestIterator implements Iterator {
+ /**
+ * The current test index.
+ */
+ protected $index = 0;
+
+ /**
+ * The total number of tests.
+ */
+ protected $count = 0;
+
+ /**
+ * Creates a TestIterator.
+ *
+ * @param string $type the type of tests to iterate over.
+ */
+ public function __construct($type) {
+ global $TESTS;
+ if(isset($TESTS[$type])) {
+ $this->tests = $TESTS[$type];
+ } else {
+ $this->tests = array();
+ }
+ $this->count = count($this->tests);
+ }
+
+ /**
+ * Gets the parameters for the next test.
+ *
+ * @return assoc the parameters for the next test.
+ */
+ public function current() {
+ return array('test' => $this->tests[$this->index]);
+ }
+
+ /**
+ * Gets the current test number.
+ *
+ * @return int the current test number.
+ */
+ public function key() {
+ return $this->index;
+ }
+
+ /**
+ * Proceeds to the next test.
+ */
+ public function next() {
+ $this->index += 1;
+ }
+
+ /**
+ * Rewinds to the first test.
+ */
+ public function rewind() {
+ $this->index = 0;
+ }
+
+ /**
+ * Returns true if there are more tests to be run.
+ *
+ * @return bool true if there are more tests to be run.
+ */
+ public function valid() {
+ return $this->index < $this->count;
+ }
+}
+
+class EarlReport extends PHPUnit_Util_Printer
+ implements PHPUnit_Framework_TestListener {
+ public function __construct() {
+ $this->filename = null;
+ $this->attached = false;
+ $this->report = (object)array(
+ '@context' => (object)array(
+ 'doap' => 'http://usefulinc.com/ns/doap#',
+ 'foaf' => 'http://xmlns.com/foaf/0.1/',
+ 'dc' => 'http://purl.org/dc/terms/',
+ 'earl' => 'http://www.w3.org/ns/earl#',
+ 'xsd' => 'http://www.w3.org/2001/XMLSchema#',
+ 'doap:homepage' => (object)array('@type' => '@id'),
+ 'doap:license' => (object)array('@type' => '@id'),
+ 'dc:creator' => (object)array('@type' => '@id'),
+ 'foaf:homepage' => (object)array('@type' => '@id'),
+ 'subjectOf' => (object)array('@reverse' => 'earl:subject'),
+ 'earl:assertedBy' => (object)array('@type' => '@id'),
+ 'earl:mode' => (object)array('@type' => '@id'),
+ 'earl:test' => (object)array('@type' => '@id'),
+ 'earl:outcome' => (object)array('@type' => '@id'),
+ 'dc:date' => (object)array('@type' => 'xsd:date')
+ ),
+ '@id' => 'https://github.com/digitalbazaar/php-json-ld',
+ '@type' => array('doap:Project', 'earl:TestSubject', 'earl:Software'),
+ 'doap:name' => 'php-json-ld',
+ 'dc:title' => 'php-json-ld',
+ 'doap:homepage' => 'https://github.com/digitalbazaar/php-json-ld',
+ 'doap:license' => 'https://github.com/digitalbazaar/php-json-ld/blob/master/LICENSE',
+ 'doap:description' => 'A JSON-LD processor for PHP',
+ 'doap:programming-language' => 'PHP',
+ 'dc:creator' => 'https://github.com/dlongley',
+ 'doap:developer' => (object)array(
+ '@id' => 'https://github.com/dlongley',
+ '@type' => array('foaf:Person', 'earl:Assertor'),
+ 'foaf:name' => 'Dave Longley',
+ 'foaf:homepage' => 'https://github.com/dlongley'
+ ),
+ 'dc:date' => array(
+ '@value' => gmdate('Y-m-d'),
+ '@type' => 'xsd:date'
+ ),
+ 'subjectOf' => array()
+ );
+ }
+
+ /**
+ * Attaches to the given test result, if not yet attached.
+ *
+ * @param PHPUnit_Framework_Test $result the result to attach to.
+ */
+ public function attach(PHPUnit_Framework_TestResult $result) {
+ if(!$this->attached && $this->filename) {
+ $this->attached = true;
+ $result->addListener($this);
+ }
+ }
+
+ /**
+ * Adds an assertion to this EARL report.
+ *
+ * @param JsonLdTest $test the JsonLdTest for the assertion is for.
+ * @param bool $passed whether or not the test passed.
+ */
+ public function addAssertion($test, $passed) {
+ $this->report->{'subjectOf'}[] = (object)array(
+ '@type' => 'earl:Assertion',
+ 'earl:assertedBy' => $this->report->{'doap:developer'}->{'@id'},
+ 'earl:mode' => 'earl:automatic',
+ 'earl:test' => $test->data->{'@id'},
+ 'earl:result' => (object)array(
+ '@type' => 'earl:TestResult',
+ 'dc:date' => gmdate(DateTime::ISO8601),
+ 'earl:outcome' => $passed ? 'earl:passed' : 'earl:failed'
+ )
+ );
+ return $this;
+ }
+
+ /**
+ * Writes this EARL report to a file.
+ */
+ public function flush() {
+ if($this->filename) {
+ printf("\nWriting EARL report to: %s\n", $this->filename);
+ $fd = fopen($this->filename, 'w');
+ fwrite($fd, Util::jsonldEncode($this->report));
+ fclose($fd);
+ }
+ }
+
+ public function endTest(PHPUnit_Framework_Test $test, $time) {
+ $this->addAssertion($test->test, true);
+ }
+
+ public function addError(
+ PHPUnit_Framework_Test $test, Exception $e, $time) {
+ $this->addAssertion($test->test, false);
+ }
+
+ public function addFailure(
+ PHPUnit_Framework_Test $test,
+ PHPUnit_Framework_AssertionFailedError $e, $time) {
+ $this->addAssertion($test->test, false);
+ if($test->result->shouldStop()) {
+ if(isset($test->test->name)) {
+ $name = $test->test->name;
+ } else if(isset($test->test->label)) {
+ $name = $test->test->label;
+ } else {
+ $name = 'UNNAMED';
+ }
+ printf("\n\nFAILED\n");
+ printf("Test: %s\n", $name);
+ printf("Purpose: %s\n", $test->test->data->purpose);
+ printf("EXPECTED: %s\n", Util::jsonldEncode($test->test->expected));
+ printf("ACTUAL: %s\n", Util::jsonldEncode($test->test->actual));
+ }
+ }
+
+ public function addIncompleteTest(
+ PHPUnit_Framework_Test $test, Exception $e, $time) {
+ $this->addAssertion($test->test, false);
+ }
+
+ public function addRiskyTest(
+ PHPUnit_Framework_Test $test, Exception $e, $time) {}
+ public function addSkippedTest(
+ PHPUnit_Framework_Test $test, Exception $e, $time) {}
+ public function startTest(PHPUnit_Framework_Test $test) {}
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {}
+}
+
+class Util {
+ public static function readFile($filename) {
+ $rval = @file_get_contents($filename);
+ if($rval === false) {
+ throw new Exception('File read error: ' . $filename);
+ }
+ return $rval;
+ }
+
+ public static function readJson($filename) {
+ $rval = json_decode(self::readFile($filename));
+ if($rval === null) {
+ throw new Exception('JSON parse error');
+ }
+ return $rval;
+ }
+
+ public static function readNQuads($filename) {
+ return self::readFile($filename);
+ }
+
+ public static function jsonldEncode($input) {
+ // newer PHP has a flag to avoid escaped '/'
+ if(defined('JSON_UNESCAPED_SLASHES')) {
+ $options = JSON_UNESCAPED_SLASHES;
+ if(defined('JSON_PRETTY_PRINT')) {
+ $options |= JSON_PRETTY_PRINT;
+ }
+ $json = json_encode($input, $options);
+ } else {
+ // use a simple string replacement of '\/' to '/'.
+ $json = str_replace('\\/', '/', json_encode($input));
+ }
+ return $json;
+ }
+}
+
+// tests to skip
+$SKIP_TESTS = array();
+
+// root manifest directory
+$ROOT_MANIFEST_DIR;
+
+// parsed tests; keyed by type
+$TESTS = array();
+
+// parsed command line options
+$OPTIONS = array();
+
+// parse command line options
+global $argv;
+$args = $argv;
+$total = count($args);
+$start = false;
+for($i = 0; $i < $total; ++$i) {
+ $arg = $args[$i];
+ if(!$start) {
+ if(realpath($arg) === realpath(__FILE__)) {
+ $start = true;
+ }
+ continue;
+ }
+ if($arg[0] !== '-') {
+ break;
+ }
+ $i += 1;
+ $OPTIONS[$arg] = $args[$i];
+}
+if(!isset($OPTIONS['-d'])) {
+ $dvar = 'path to json-ld.org/test-suite';
+ $evar = 'file to write EARL report to';
+ echo "php-json-ld Tests\n";
+ echo "Usage: phpunit test.php -d <$dvar> [-e <$evar>]\n\n";
+ exit(0);
+}
+
+// EARL Report
+$EARL = new EarlReport();
+if(isset($OPTIONS['-e'])) {
+ $EARL->filename = $OPTIONS['-e'];
+}
+
+// load root manifest
+$ROOT_MANIFEST_DIR = realpath($OPTIONS['-d']);
+$filename = join(
+ DIRECTORY_SEPARATOR, array($ROOT_MANIFEST_DIR, 'manifest.jsonld'));
+$root_manifest = Util::readJson($filename);
+$manifest = new JsonLdManifest($root_manifest, $filename);
+$manifest->load($TESTS);
+
+/* end of file, omit ?> */
diff --git a/library/sticky-kit/sticky-kit.js b/library/sticky-kit/sticky-kit.js
index eb2ea8a26..00b1ea2ff 100644
--- a/library/sticky-kit/sticky-kit.js
+++ b/library/sticky-kit/sticky-kit.js
@@ -77,7 +77,7 @@
padding_top = parseInt(parent.css("padding-top"), 10);
padding_bottom = parseInt(parent.css("padding-bottom"), 10);
parent_top = parent.offset().top + border_top + padding_top;
- parent_height = parent.outerHeight(true);
+ parent_height = parent.height();
if (fixed) {
fixed = false;
bottomed = false;