diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /browser/base/content/newtab | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'browser/base/content/newtab')
19 files changed, 6710 insertions, 0 deletions
diff --git a/browser/base/content/newtab/alternativeDefaultSites.json b/browser/base/content/newtab/alternativeDefaultSites.json new file mode 100644 index 000000000..018d3edcc --- /dev/null +++ b/browser/base/content/newtab/alternativeDefaultSites.json @@ -0,0 +1,50 @@ +{ + "directory": [ + { + "bgColor": "#ffffff", + "directoryId": 10000000, + "imageURI": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAIX0lEQVR4nO1afXDbZh1+sxbWsbGvsrLbuBt36766sR3sOEgiuU4kx2vTpFGKm0SKsyyOlI8laUZTulLojQEHx4CyAaNnt72yrivklo1YSpxcW7YVbKXHgCvduCvroDt6o6OtLSWN1MRWX/5I4imyJMu2bPNHnrvfSZbf6P09z+/jfV+3ACxhCUsoAEouNWGlMRLri5GuZwUSe1Ug8bcFEjsjUPgHAoX/UyCxkyKJjQoU/iOhCd8WJSvW/8vpXFFsx7PGeaJsVaytul+g8KMiic+IFA7NTNB/rggk/jeRcu26RGFris3JEqIUtkFsdo2KFK6kI52pCSR2UmirZgY9YFmxeaYg1uyqFSj8lMXI5mofRb3rOwEAJcXmDS72ex8QvO43skjv3MeT2DtRX2150cgLzVUDIonF8xBh64KQ2FWRwncPejyFK4tYq/Nmoa1mPE/pnZ1ILe4TFxpdd+SdvOhxrhabXaeLTVjfsHfzSj5K4V8QKfyjnGs3P2UiCt/v/1LeyAsNlXebkbeRSOZ/Q+JCrBH/Yt7IX+qlbhS96dM+Y+dJLC6S+BWRwmazFizf5AEAQPRtHLEhukqMwo8KTfg2oamy8t+e0uvUczwDwDWxhopHBBLvElrc+wUSm/r/IN9a3ZtrbYqUe9eHTc7PZDLvoAcsi/lq2wQSO1M88k93r7ayjzdI76sxEv/5hdryT+foRknM696q9qMg5AEAQHx83WhWtU5i56PtG1E7fYkO+B4SKNd7AoWLBSEv9G3Asol8zOs+ddFTeWc+fDrXgq2MNmEP5+PdKZB/XR4RvRnWe8tjJ8+1ECsL4mA+EQ+vcCg8gHHuPjjZvc5S6guU6/3/POG5rdi+24IEv+KwEgFQ4QFU/rAKTu90pal5fCY60PlQsf22BefHwfUKD2YVfl6ACIAKfx2UX0QNBYj6arqL7bdtSIQBnYy+2iIAzg49Cid9bm3qv11sn21FggeHUwSILGQCgIljd8Kpreq+4Koqts+2QuGBmBL9FLsFSj+sgAKFnyq2v7ZCDoO7dKOvvc7fS3vcW4rts61I8OBxXdI6/UCJADgVvmGVHfMSAelgfUA6VBeQDqmv2nuzZ3rf6Y2r3SM7DB1RwmCnOsLaiGuenbWD/JwAcqI+IEPCL8H6gAwX7omADIn5z/Wqe0Iz1sgI3edSv6EjcR78OEk4jQiJCNhnlwD1ATlBmDqdCUHzcYRf3mXoSCIMXjdK95SeEAbfslUAk4iaEc1EsLnx0vNmAhw3q3mNAFvtFCBd2i8i4bdWAgYCDBkLwIMJw8antTDos12ARSTNM8JK5Amd8URAPmosQAS8Zdr41N/lIQOS0Z2/pkZe0iWoS35+rIY8rPfLvzcWgAdDljMgAr5tlwCEWgArjUxnnJXSmX8eMnQkPrH8OasCJMJgfyEE0KZxMrJ+i2WgyZq6gHTQ0BGFv+abujs//ab4gV0CqEvAar1n0gQXizj9PUNHEmHgTSFt1At4O3eCH+8D0tW2YUmoMkKvfyQzwC/vMHREegN8Tpe4zjIY55fD14+X9tgiwC8mV2ZrHS/LGOGXRd3y8aeKVee/3GrqjBIGMcMsmL/GJu6AW4+1QZRl/mKHADmipD4gz1jJHCIgw9oXJ+8zfVsi/ImXU7bDquvfI2ugZ7wHoiwDUY6BTrbTWRie+ti0d7rGrHTU5UD45StpX5iILPMZrQTD4Q0QG+2GKMckzcF28AXgaYiv7ZNesdokCb90PO0LP2TBpxQezKpFkPmb4Q/ebFlEHOWYZBY8FtriKwDXFDAHhLvr/bJi9TzQtPeycQNUI3HihkML5M9NfB4+caRTl/jHRsvtb333gTzzTUHDPonNZFkkfjVl7Zfr+MRtiMID+McICqtDvfrE2cWfHRxz2j321K155pxE84HLLWb7BK0odX75TEYTBI43hB1ch0HE554h7GJhKrkn/4wPMjfliXMSO4alcsIvx/UanZE17pcGMppkU2iL2zDt9QSZtwqu66/Okdbb88Qd7PydVFYfkGVthLWHHk1JyOteuHRjxpNh3JNjRtE3EgDlGIiwzLlqrq/MbvKtLwkewi/PalPc8PyQPDdIP8lqQvdw92qEZWbNyBqJgnD0VTTI7H74Je/1uRL/8mDr7VWjfYeJkecgdfC/6c8JavPL0+6fitn3JtdIzxbdqLPqiNMmwtAxR7BzZ+mgL2MnHGzHPQhL/wzlmCsLc64PbYPU4Xd1zwN6WdCwT/p61uQXsH6sL5TS/dOUgY4QCZRlxsuD9EDZsM/x4KDnk5ppSsqGfHehw3QjytHfQVj6HaP+U8F1wZbhMf1Dkerzpr3Sn4Ad/5/Yc2T7TQ6OOW2YBWZimAsVRzlaRoL0DMLRV/WW19SVh05+v5nbAxsPCPpZ4Jclwj95b87kF+BgO+5Bg8yFFHKZZkO68XqimoyvGd0FqUNnU/pA1yvTdbaRXwDCtj2CsszFjFLfSjZoRbEiqEqoqtE+6H2VT5LfvPfydtvJJ0V4jb537QhzxjLRdMSsimIhq5q538DNgSnbfqs0FoHrusUd6j2ScdTNyGZTNovfHa8Z6/fmnbwaTq5r+1wj0ziZrjlmvIKYP3ew9Hvk+NOPFpT8AjxHd6ypGOl607Z+YDUL5s4iChpkdq8b7b22KOTVQIPtGxet3bmQtVA6eKhnyBP6hvnPW8UAwvpq1nIdoeS6bpWY0fK3aMfJTCMc/csqtuf+YvNMi68Mt38WDTJPoSx9DGHpmaxLI8hcqBzpCiDB9ibnM87lxeaVLUrKg+1fRVimby3LPItw9G9Rlj6Bssw/EI4+i3DM+0iQPomy9JiD69hdztI7SoPtTWWvddry7w5LWIIx/gdCuvcjoZqlLQAAAABJRU5ErkJggg==", + "type": "affiliate", + "title": "Google", + "url": "https://www.google.com/" + }, + { + "bgColor": "#E62117", + "directoryId": 10000001, + "imageURI": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACd0lEQVR4nO3av0tVYRzH8WOIBEVDUUIGCa32FwQ5OrS42NKeRUOTdGmoqUGiLVBqa5UG+7WUREGiEqhTDkVJEA2iYZBkxKvhHumo96o3uM9z0ucNZ7zn834+XO59znm+WZZIJBKJRCKRSGwCx3AWfbiIaxjEPYzgOd7gLWbwIb8WsFjjWrU9q3U+u1C4/0yeOZ47jOROg6jkrn25e3uji27NbzC9A9n/hWnVNbVut/hOTMZ1bSqT6Ky3+A58jOsXhE/o2Lj4FryILBaSF2gpFtAT2ygCPcUCHse2icDTtcW3YSW2TQRW0JahK7ZJRLoy9Ma2iEhvhoHYFhEZyDAU2yIiwxkeBgj6FSDjXxjN8CpA0A1cxucAWY0wkeFdgKBK/pe7H1fxNUDmTpjLMB8gqLJh631A9dF6IUD2Vsxn+BYgaF0BhSIO4WYgh1osZVgOEFSzgEIRh3EL3wO4FFnOAgVtWUChiKO4gx+BvJSqgEIRx3EXP5stVsoCCkWcxH1N3EeUuoBCEafwshlipS8AJzTxW1DaAnAEtzX5B7F0BeAgrmMphFhpClB9M3UFXwI5oQQFYB8u4H0gl3VE3QninOoxVyyWozwL4AxeB8jdjqWgT4M4jUcB8nbKfKj3AUN4gN8BshphLtQbobIyEeqdYFkZzTAc2yIiw+lcQDoZSmeDe/t0ON+gPIltE4Fnxa3pnp8QacFYbKOAjCnOCOUldKhOUO12Nk+JFUroxFRkwWYypd6cYKGEVvTbfZOil2w3KVqjjHZ047xqKRWbZ4XHVed2Z/2d5a0167vYgHC9z6/df9bWs8L9uXO3RmeFE4lEIpFIJPYKfwAcall+TY3q/wAAAABJRU5ErkJggg==", + "type": "affiliate", + "title": "YouTube", + "url": "https://www.youtube.com/" + }, + { + "directoryId": 10000002, + "imageURI": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACT0lEQVR4nO3bzWsTQRgG8PlPHMVLqRVMoQiFehJyUBAFtRCFHspExaaSoFg8CMGPSg+R3kQhh3osIbOlMVrbGKtrsbYESTbB0gqNVGu1WCkWutnXg+ihQjqxi7Oz8w481533+V32iyGEEEIZ76Rho0SZ4ewOG+D3UMatXSx9nPwqb5ySPZC0dPMzhDJelD6IrDCjQijjtvRBJIWGeY3IHkJ2EED2ALKDALIHkB2lAJoujkIwnoNQwoQL96aha3AKQgkTTtyZhKM383D4+gQEoll/AQRiWbg1XILZ+VWwaw6IrE3bgZbejNoAe8+PwEC6DOsbm0Klt679lxQGCESzMD335Z+KKw+wrzcDpcVvOyqvNMCwubjj8soCnBx44Up5ZQGeFZf1BWi7/BhqjthtzpcAseRsQwVrjgNzS9/hZWUFJq3Pf6U5MqoWQHJ8Qbh8aqoKB688cWVfzwBMvP0kVN6srMCec+7t6xmAmfmvQgCRBzOu7usZgHJ1TQjgyI283gCHrj3VG6C9bwwBEAABEEAtgAPRR9A3VKibpdUfQgD9KWvba529+8pbAMF4TqicW+th/r3eAP0pS2+Anvtv9AY4dvu53gCtMfGfI74DWN+wG5rNdwDl6preAGOFj3oDJMcXvAfQHMlAKGHWTeWD2KPw1aFC3et0NPi9wDPvAq/fif0LDMZzru6LALKLIwACIAACIAACIAACIAACIAACIAACIAACIMD/BvDKwUkZAJRx2zNHZyUBFAntTp+WXV4iQCf5c3yecUsXAMq49bv8Txsl6ZCTabOAAAAAAElFTkSuQmCC", + "title": "Facebook", + "type": "affiliate", + "url": "https://www.facebook.com/" + }, + { + "bgColor": "#ffffff", + "directoryId": 10000003, + "imageURI": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAYAAABQHc7KAAAEyUlEQVRoge2abYhVRRjHf7veVqSFhIQwa7E3ojXNpSiNgl0S+tAHKUjohbQohUgqKKwMSXpbsATdqOgFU4zKpAzMioploxI/WFBZmhRlL/S2lbpsudvd24dzjve5c58558ycey9B84MB95x5/vM/d+bMzDNHCAQCgUAgEAgE/rf0AOd6lJmKVrdDfI8SfxawIKNcEscfa8TOzBFrlj6Aimd5XnmAbx01TD5xiL3IiH3Y8zm4DXgaGALGMip/DDwHDACXKQ9wA/AiMJyi8T3wAnCvEr841t9tiR2JY/uB443YC4BHgA3ApyntTxD90FuAlaaBU4EfLIHvKIZtnAT8ZtE5O6fGCiNuFJiTM7YNWKe0PQScmRV8ncX4+zkbT1hq0bkyZ/wyI+5ux/bPM+I/ACbnCSyhv8tloMvBQAnYr+h8mDP+PRHzGXCMQ9sA14j4ceAMl+A70HtvjaMJ22jqy4i70Kh/sWO7AO+K+Jdcg6cCh6k3/gfQ6aBTAr5UdN7OiHtL1H3NxXhMN9Fkl2jM89BgPXrv3eKoc71Fx2ZqvqhTJv+kKRkQGjs94gE4PTZgGt9DNMvmpQR8pehst9R/Q9TZ5OG7EzgkNK720DjKNvTeu9RRZ4miMUH9bnCeuH8EOMXD881C40egw0PjKL3oP8Cbjjq2FWGrUW+HuDfg6XmP0NA2W060GYKy97odtbQVoQzMiu+fL66PACd4+O0VGqPANA+NOm5CHwVPOeqUgH2Kzub4/uvi2gOeXrcU8GdlCtG7ZBr/C5juqHWtojMOXCX+HgaO8/B5YqyV6GjZpjf3oI+C+x11JgF7FZ1/xL/v9PS4WmgMempYsW2MfsdtYwTRsqT9mBXgANGIc6WD2lG60EMjk0fRTS931GkHPrdo3ejpbZHQ2B+30XC6iNZm0/Q3RBOcC7Yc4WRPb0NC43ZPjVxsRDe+yFHHHLJJWevhabaIP0z0ujaNWdQmGUn5yEPrPkVnBPe1+0kRv97DhzPb0UdBr6OObZv9kIOGnJzLRPlL0+lFN+6yPe5BH0kV4E/yD+PlIm6bQ/uF2Um98Qlgbs74V5V4WVbl0GgDvsB/BBbiCnTj2lG5yVyqvX+QaOSYOsNk7y8WiPo+c1Ah2tFPesbJTmFfEfX7qU2BZVmRoSPnkMU+D1EUW5L0WErMHKq9P0o149NeqZ+w7wq7qO77f02p11Qmo6/laWnoVlFvnbh+uaJTAW616Dwo6rjmIw3lLnTjminZ+38DM8Q92yv1HfUnOh3AL/H9I7hnpA1lKrXnb0nRkqSXxf0nFC3bK7XMqCdT6o2NeIiirEE3Lvfks6kesI6hf1m2nTt8TW2uIeeLhub8vsxAT5IOUB2+8qTm2RStlYqOnOV7xLXBRj5EUWxJ0hKic/2k98dJ367azh32Es0Tz4hrTcn5fZE9I8s+ajc6m20CgrUWrdVEK0yFJub8RRhEN56UMjk+TVO7xtuK6yFMS1hIuuk82+SETSk6h3A/hmsJ7egfPypEh555ej/hHOzZYktyfl9keiqLzzc+LUlqWc7vSyfRJ3Rpegw4zUOrj/ofoKU5vy/91Jp+vIDWLkPL/J9h/0mmAT9TXa58vvIkzKe6ydpQ3FogEAgEAoFAIBCI+Rf5q90lTPZHIwAAAABJRU5ErkJggg==", + "title": "Wikipedia", + "type": "affiliate", + "url": "https://www.wikipedia.org/" + }, + { + "bgColor": "#400090", + "directoryId": 10000004, + "imageURI": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAD3ElEQVR4nO3bS4xecxjH8WmLBVVEx63JBIkEISkhJqURpAsZQioWqpeNSxraYlKpSwiDIRY0aSakqgS9qHRBIhKxYdFFFxWXIGkilYZEUCKj4zLzsfi/LN55zpiZc/lr+/6Ss3nfc57ze77n9r88/y7swxaswgLM7jpMhTlYiNWtnPd1Ga9RfIVtuB+L0J3b/FSFeejDQ9iBvRhrTzYCUKTv8RFewlrcgAtxQsYk5+Ii3IQHsBm78ONkk5oKgIn0Mz7BO3gBA7gHS3EtLsXZre1UnITj2pI5vvV7D85BL67DCvRjEJvwHj7HcBXGqwJwyKoDILeB3OoAyG0gt8oAGMYj0qdnJ17DY3i2vK1CPYmn8Cq2YwgP4rfpBix7BwwE3+aZ2F0ybqQPMSM439NlgpYFMILzAlO9glZXCf2F+cF5LsafZQJX8Q4oujKbK4j9j4aC+EdhT9nAVb0Ebw8MduNABbF/wMlB/LUVxK4MwE84JTC5uoLYK4O48/BrBbEr/Qy+XnCbfloi5h7MCuJurcSxagGMYWFg9qr/SbxQVTeEiq7Ym9OI9UYQ52ipJ1iZ6mgJ3h0Y7zG17usvOC2I01+12ToAHBCMIGHdFGKsCo4/QwJTqerqC2wMEjgGX0zi2KLH6JU6jNYFYBSXBElc/R/HjaE3OO4y1bYs/1WdvcFd4hbilgmOie6cGa1Ytaju7vDSIKHTxc9yUYtvWZ0G6wawH8cGSa0J9r0t2G92K0ZtamJA5PEgsfaOTNHjMlC3uSYADKMnSG6B9GIr6uqehYN1m2tqSGxre4KtJDdhfcF/O5ow1hSAMVweJNmNOcHvlbb3J1KTg6K7MTO62m3Jz1LBQMdk1fSo8IpJALizSUNNA/jWBNPvOFGahG1MOeYFnpgAwHNNm8kB4CDODJI/F380bSbXzNAzAYD1OYzkAjAYABjKYaQDIMdJxQDqnFMsVC4A6wIAgzmMdADkOKkYwKM5jOQCcFcAYCqjxpUpF4Bovu+IB3BfDiO5AIzrFWJlDiO5ACzpABgP4I4cRnIBWBwAWJLDSC4AfR0A4wHcksNILgCLAgB9OYzkAnDFkQ5gDZbj3ta2XCp5bVydYuncBnKrAyC3gdzqAMhtILfqBDCCb6SZ3velatGXsUFa5NC+vdjatrf2/QAfSyUyv9dlsgyAEXyGt/G8VBl+PS7A3PaGTllJCy7n40Zp8GQD3sWXSgCaDIBRqcBxGx7GYmll57hixlySaojPx83SuqW3pLXC0wKwX7oF+3GloILjUJE03X6NtAh8J76LAOzFRtwqKGY63CStX14mLen5+m9Vgqdbrd+9cgAAAABJRU5ErkJggg==", + "title": "Yahoo!", + "type": "affiliate", + "url": "https://www.yahoo.com/" + }, + { + "directoryId": 10000005, + "imageURI": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAJdklEQVR4nO1ba1QTZwL9iCY8hCDxDUpYOIUDBOVZAlJ5NQXRFlEQ8YGiRaqESlhxeRQ3FAWURRC6FYglqBHTEaI8Eg+CRMRatfKwLNL6oqer1h57XEWYbwJK9sdKixiSmRCCbrnn3D8w351770wy3zd8ADCJSUxiPMHhcPS3bdv2/tq1a9MWL16MODs7t5iamt4zMjLq0dfX76VSqU8sLCzueHt7S+3t7QtjYmLWJyYmzp1o32NGSUmJd0BAQLmRkVEPAEBOhCQS6bm3t7c0MjIyHACgMyEB1AGCILTY2NgUOzu7fwGCoUfj/Pnz/x0XF/fXgoICXW3nIYTs7Owlpqam94CGgo+ktbX1j4mJiY7azIQbMTExu3V0dF6AcQo/RF1dXYggSIj2kuFAfHx8Lhjn4ODVElCxWOygpXjKkZWVxQZaDD9EOp3eXV9fb6yVkKOho6Njgb6+fp8ig9pgfHx8gjZyjgYdX1/fhtHMqSKJRHpOJpNl6o4HAMidnJxatJJUEWpraz3xmBxOExOT30JDQ/+elpZmD14+26uqqubExcVFzJs3T53H5uCJEydMtZN4BKKiovKJmA0KChLu3bt3zmh6Bw4c0F+3bl0BEU0AgLygoCByXAKqgq2tbSdek0FBQTV4dZlM5kW8ugAAOZPJ/IeGIuHH9evXp+no6DzHa7K+vt4Lr3ZiYuIOvLoAALmnp6dQQ7HwIzc31xqvQRsbmy5AYC6fnJzsjlcbACD38/M7p5lUBBEeHp5lbm7+E4lEUnonREdHpxPRLSwsNFWmN5Kurq5XNZNITfj4+EwtLCw0dXFxcebz+UFhYWFbmExm6qZNm/IjIiKyampqDIjonTp1ajp4mwrQNP70BfD5/D9PAcXFxQZcLteOwWCw9u/f/3FwcHC6paVlOfg/K4CUlpbmEB0dvSEkJCTLxsamwtnZuXXmzJmPAACDgEBYRXxjC8jJyfFYtmzZYWNj48dgjCGV8Y0qICsryyQiIiKVTqf/BMYxNHgTC8jPz/+ISqU+AVoKDt6kAnbs2MGeMmXKANByePAmFBAbG5utyJi2OKEFJCcnx+MxqYizZs361d/fvyw9PX0bg8FgZWRk2FZVVc1pampaQERnwgoQi8VLVK0DRpJMJstWrlx5hMPhvMflckmKdN+amSDRdbuenl5fRkaGnyrdt6KAsrKyD4iYpFKpTy9cuPAuHu23ogAmk9lIxGRCQsLneLVLS0tnEdHWegFisXgu0b8ANTc3W+LVT0pKsiSirfUCDh06tJyIQSsrq5tE9PPy8jyI6Gu9gMDAQA5Bg9VE9Hfu3BlFRJ/BYHSMPRUBsNnsfUQMksnkciL6q1ev5hHRNzQ07AHa3D9A9N19QEAA7lfiAAAdOp3eTUQfACBHEMR87MlwgmgBZmZm9wHOK1RUVLSFiPYQd+/eHaOheKrB4XCyiBrk8XguqnTb29vNDAwMeolqAwDkCxcuvKbBiMoREhLyKVGDy5cvr1Cm2d3dPd3Ly+sCUd3h3LNnT7xGg46GoqKiQHUMbt68uZjL5VJG6qWkpPjS6fS76miO4Ivm5maVd9qYUV5ePlPdtb+FhUV3cHBwkbm5efrGjRvz7O3tr2og+O90dHRslUqlUxX5lkq5U7HmJEtUsnTxwG0kEJ7b4Cer32ArlXIVHq8ULBarVpPGNcng4GDP4V6xloxArNq3oq+M9hTlUeSvsfwvPxMuoKGhwWO8N0RRKBSM6Bg6nf4jgiC0zk6Egl3czsYQhxsKQ48g4QIAAMDf3//0eIWn0WiPjh075jx79uyHeMfY2dl1IAgyFwAAZA1rjqE8ihw9rPscrXRp7xO5lckaN6X0t+dt7m/LiYanPI+iPPLgmApoa2t75+W7fY2GX7RoUcvx48fpAAAgEom88NwJLBbr65ycnGlD3gZuli15VhfC6JZy9Ubzj1W6to6pAAAAEAgEDsbGxv/RVHh3d/ezEomEOvwcqamp6crGsNns/NHeLg0HPPOhV98Pf2ynQRHG+ZcFDKpdAAAAFBcX+6qzF3gkly5dqjCIVCrVs7e3v6RozKpVqw6q8idrybCFonfrUB5FDqsWfzv0c1jh2IHyKHIotL0J+lsyY7CqJXmyO4hamw/Ly8vnhIeH51Op1KdEQtNotN9WrFjxpVAodFOmv337dkMmk1k9NI5CoWCZmZk7EQSZouj4bilfT/ZtUjhW4yNCDxv0ozyKHJaZ9Mq+S1sDAACdnQgF8o1RlEeRy5picsDA3dPv9QnMHqI88iAUudYNdJV4q1NEXFyc7q5duzZ4eXmdtLKyukUikV6ZL1Cp1CeOjo6tHh4eRVwudwWfzx/1MzoSXC53alRUVCaLxapjs9kBio55fC3bGPvm0wRUQL/3yjc9n9Yz0HX090zolTSP//2OPIjdEVkDAAB42nmYJjsbWjo0CEMcfoCNkXvR5lh3BAlT2LQqhIWFTZFIJFSJRELdunUrWR0NVXhc/zdj9MxHa6B4qRAemfHasx6rYUnQjn8uGD5G9k3CHpRHkcvqQk+8Joh1lX2AHl9wf+RkAWtYvx9eSvaWSn2Iz5w0jId1O6fJLn8Wila6iVCe/oCiZ3sff8YT7CLnE6BgJQoRhxtoqcmz3l8vK9629+xW6Sx4dnUmLJv55DXxo3MeQ8ThDKxlfS77Pm8F1pxkqegkmsItSZxu75nAhf1NMZFY47ov4MlFzejLz69ClhrB/vObc3uuZM5QpIf9fM4K5VHk/W25sSpP3tdaaArPrBSgJbovlM6m+LQeWO13HqtwO4i1Zn0CRZ5+2Hf7bB5cK8a9P6hbytfDOgqsMJErC7vyWRw87VUERR5X0VJDiGc2h/IocljL+rr3Klfpv9pg9xttsLOrVIcfDtltEQOtcP0CFZg/wGvmjysyvQ8VmP8ChTa3McThBiZya8NEbm3YSUYXFNrcRgXmv8Ay2jOUp0tMd9gdidWHFsiuH2QQCqUOuFwuSXY5ZT1a6dyullkNEiKMLky6NaYTeX2JrRXILqcslIkDk2GFYyP6lSE27oGPzHiKVTiJZdV+qf2dh50mJPRo6Jby9bBLycvRSpcCeNLpe7SEovw7Aw+/MpDBUx5XsNqAbHgp2buzc4KutDp4dHGXEVq3xh02fbwFq2Htg5WuQljlcwFWLOqAJ97p7hOYPewTmD2EQuu76Emn67DauxmtdEawap8ceH5TNFofznxQs5XQBstJTGISk1AX/wUPVWOeSpK50AAAAABJRU5ErkJggg==", + "title": "Amazon", + "type": "affiliate", + "url": "https://www.amazon.com/" + } + ] +} diff --git a/browser/base/content/newtab/cells.js b/browser/base/content/newtab/cells.js new file mode 100644 index 000000000..47d4ef52d --- /dev/null +++ b/browser/base/content/newtab/cells.js @@ -0,0 +1,126 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * This class manages a cell's DOM node (not the actually cell content, a site). + * It's mostly read-only, i.e. all manipulation of both position and content + * aren't handled here. + */ +function Cell(aGrid, aNode) { + this._grid = aGrid; + this._node = aNode; + this._node._newtabCell = this; + + // Register drag-and-drop event handlers. + ["dragenter", "dragover", "dragexit", "drop"].forEach(function (aType) { + this._node.addEventListener(aType, this, false); + }, this); +} + +Cell.prototype = { + /** + * The grid. + */ + _grid: null, + + /** + * The cell's DOM node. + */ + get node() { return this._node; }, + + /** + * The cell's offset in the grid. + */ + get index() { + let index = this._grid.cells.indexOf(this); + + // Cache this value, overwrite the getter. + Object.defineProperty(this, "index", {value: index, enumerable: true}); + + return index; + }, + + /** + * The previous cell in the grid. + */ + get previousSibling() { + let prev = this.node.previousElementSibling; + prev = prev && prev._newtabCell; + + // Cache this value, overwrite the getter. + Object.defineProperty(this, "previousSibling", {value: prev, enumerable: true}); + + return prev; + }, + + /** + * The next cell in the grid. + */ + get nextSibling() { + let next = this.node.nextElementSibling; + next = next && next._newtabCell; + + // Cache this value, overwrite the getter. + Object.defineProperty(this, "nextSibling", {value: next, enumerable: true}); + + return next; + }, + + /** + * The site contained in the cell, if any. + */ + get site() { + let firstChild = this.node.firstElementChild; + return firstChild && firstChild._newtabSite; + }, + + /** + * Checks whether the cell contains a pinned site. + * @return Whether the cell contains a pinned site. + */ + containsPinnedSite: function Cell_containsPinnedSite() { + let site = this.site; + return site && site.isPinned(); + }, + + /** + * Checks whether the cell contains a site (is empty). + * @return Whether the cell is empty. + */ + isEmpty: function Cell_isEmpty() { + return !this.site; + }, + + /** + * Handles all cell events. + */ + handleEvent: function Cell_handleEvent(aEvent) { + // We're not responding to external drag/drop events + // when our parent window is in private browsing mode. + if (inPrivateBrowsingMode() && !gDrag.draggedSite) + return; + + if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent)) + return; + + switch (aEvent.type) { + case "dragenter": + aEvent.preventDefault(); + gDrop.enter(this, aEvent); + break; + case "dragover": + aEvent.preventDefault(); + break; + case "dragexit": + gDrop.exit(this, aEvent); + break; + case "drop": + aEvent.preventDefault(); + gDrop.drop(this, aEvent); + break; + } + } +}; diff --git a/browser/base/content/newtab/customize.js b/browser/base/content/newtab/customize.js new file mode 100644 index 000000000..28a52373c --- /dev/null +++ b/browser/base/content/newtab/customize.js @@ -0,0 +1,133 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +var gCustomize = { + _nodeIDSuffixes: [ + "blank", + "button", + "classic", + "enhanced", + "panel", + "overlay", + "learn" + ], + + _nodes: {}, + + init: function() { + for (let idSuffix of this._nodeIDSuffixes) { + this._nodes[idSuffix] = document.getElementById("newtab-customize-" + idSuffix); + } + + this._nodes.button.addEventListener("click", e => this.showPanel(e)); + this._nodes.blank.addEventListener("click", this); + this._nodes.classic.addEventListener("click", this); + this._nodes.enhanced.addEventListener("click", this); + this._nodes.learn.addEventListener("click", this); + + this.updateSelected(); + }, + + hidePanel: function() { + this._nodes.overlay.addEventListener("transitionend", function onTransitionEnd() { + gCustomize._nodes.overlay.removeEventListener("transitionend", onTransitionEnd); + gCustomize._nodes.overlay.style.display = "none"; + }); + this._nodes.overlay.style.opacity = 0; + this._nodes.button.removeAttribute("active"); + this._nodes.panel.removeAttribute("open"); + document.removeEventListener("click", this); + document.removeEventListener("keydown", this); + }, + + showPanel: function(event) { + if (this._nodes.panel.getAttribute("open") == "true") { + return; + } + + let {panel, button, overlay} = this._nodes; + overlay.style.display = "block"; + panel.setAttribute("open", "true"); + button.setAttribute("active", "true"); + setTimeout(() => { + // Wait for display update to take place, then animate. + overlay.style.opacity = 0.8; + }, 0); + + document.addEventListener("click", this); + document.addEventListener("keydown", this); + + // Stop the event propogation to prevent panel from immediately closing + // via the document click event that we just added. + event.stopPropagation(); + }, + + handleEvent: function(event) { + switch (event.type) { + case "click": + this.onClick(event); + break; + case "keydown": + this.onKeyDown(event); + break; + } + }, + + onClick: function(event) { + if (event.currentTarget == document) { + if (!this._nodes.panel.contains(event.target)) { + this.hidePanel(); + } + } + switch (event.currentTarget.id) { + case "newtab-customize-blank": + sendAsyncMessage("NewTab:Customize", {enabled: false, enhanced: false}); + break; + case "newtab-customize-classic": + if (this._nodes.enhanced.getAttribute("selected")){ + sendAsyncMessage("NewTab:Customize", {enabled: true, enhanced: true}); + } else { + sendAsyncMessage("NewTab:Customize", {enabled: true, enhanced: false}); + } + break; + case "newtab-customize-enhanced": + sendAsyncMessage("NewTab:Customize", {enabled: true, enhanced: !gAllPages.enhanced}); + break; + case "newtab-customize-learn": + this.showLearn(); + break; + } + }, + + onKeyDown: function(event) { + if (event.keyCode == event.DOM_VK_ESCAPE) { + this.hidePanel(); + } + }, + + showLearn: function() { + window.open(TILES_INTRO_LINK, 'new_window'); + this.hidePanel(); + }, + + updateSelected: function() { + let {enabled, enhanced} = gAllPages; + let selected = enabled ? enhanced ? "enhanced" : "classic" : "blank"; + ["enhanced", "classic", "blank"].forEach(id => { + let node = this._nodes[id]; + if (id == selected) { + node.setAttribute("selected", true); + } + else { + node.removeAttribute("selected"); + } + }); + if (selected == "enhanced") { + // If enhanced is selected, so is classic (since enhanced is a subitem of classic) + this._nodes.classic.setAttribute("selected", true); + } + }, +}; diff --git a/browser/base/content/newtab/drag.js b/browser/base/content/newtab/drag.js new file mode 100644 index 000000000..e3928ebd0 --- /dev/null +++ b/browser/base/content/newtab/drag.js @@ -0,0 +1,151 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * This singleton implements site dragging functionality. + */ +var gDrag = { + /** + * The site offset to the drag start point. + */ + _offsetX: null, + _offsetY: null, + + /** + * The site that is dragged. + */ + _draggedSite: null, + get draggedSite() { return this._draggedSite; }, + + /** + * The cell width/height at the point the drag started. + */ + _cellWidth: null, + _cellHeight: null, + get cellWidth() { return this._cellWidth; }, + get cellHeight() { return this._cellHeight; }, + + /** + * Start a new drag operation. + * @param aSite The site that's being dragged. + * @param aEvent The 'dragstart' event. + */ + start: function Drag_start(aSite, aEvent) { + this._draggedSite = aSite; + + // Mark nodes as being dragged. + let selector = ".newtab-site, .newtab-control, .newtab-thumbnail"; + let parentCell = aSite.node.parentNode; + let nodes = parentCell.querySelectorAll(selector); + for (let i = 0; i < nodes.length; i++) + nodes[i].setAttribute("dragged", "true"); + + parentCell.setAttribute("dragged", "true"); + + this._setDragData(aSite, aEvent); + + // Store the cursor offset. + let node = aSite.node; + let rect = node.getBoundingClientRect(); + this._offsetX = aEvent.clientX - rect.left; + this._offsetY = aEvent.clientY - rect.top; + + // Store the cell dimensions. + let cellNode = aSite.cell.node; + this._cellWidth = cellNode.offsetWidth; + this._cellHeight = cellNode.offsetHeight; + + gTransformation.freezeSitePosition(aSite); + }, + + /** + * Handles the 'drag' event. + * @param aSite The site that's being dragged. + * @param aEvent The 'drag' event. + */ + drag: function Drag_drag(aSite, aEvent) { + // Get the viewport size. + let {clientWidth, clientHeight} = document.documentElement; + + // We'll want a padding of 5px. + let border = 5; + + // Enforce minimum constraints to keep the drag image inside the window. + let left = Math.max(scrollX + aEvent.clientX - this._offsetX, border); + let top = Math.max(scrollY + aEvent.clientY - this._offsetY, border); + + // Enforce maximum constraints to keep the drag image inside the window. + left = Math.min(left, scrollX + clientWidth - this.cellWidth - border); + top = Math.min(top, scrollY + clientHeight - this.cellHeight - border); + + // Update the drag image's position. + gTransformation.setSitePosition(aSite, {left: left, top: top}); + }, + + /** + * Ends the current drag operation. + * @param aSite The site that's being dragged. + * @param aEvent The 'dragend' event. + */ + end: function Drag_end(aSite, aEvent) { + let nodes = gGrid.node.querySelectorAll("[dragged]") + for (let i = 0; i < nodes.length; i++) + nodes[i].removeAttribute("dragged"); + + // Slide the dragged site back into its cell (may be the old or the new cell). + gTransformation.slideSiteTo(aSite, aSite.cell, {unfreeze: true}); + + this._draggedSite = null; + }, + + /** + * Checks whether we're responsible for a given drag event. + * @param aEvent The drag event to check. + * @return Whether we should handle this drag and drop operation. + */ + isValid: function Drag_isValid(aEvent) { + let link = gDragDataHelper.getLinkFromDragEvent(aEvent); + + // Check that the drag data is non-empty. + // Can happen when dragging places folders. + if (!link || !link.url) { + return false; + } + + // Check that we're not accepting URLs which would inherit the caller's + // principal (such as javascript: or data:). + return gLinkChecker.checkLoadURI(link.url); + }, + + /** + * Initializes the drag data for the current drag operation. + * @param aSite The site that's being dragged. + * @param aEvent The 'dragstart' event. + */ + _setDragData: function Drag_setDragData(aSite, aEvent) { + let {url, title} = aSite; + + let dt = aEvent.dataTransfer; + dt.mozCursor = "default"; + dt.effectAllowed = "move"; + dt.setData("text/plain", url); + dt.setData("text/uri-list", url); + dt.setData("text/x-moz-url", url + "\n" + title); + dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>"); + + // Create and use an empty drag element. We don't want to use the default + // drag image with its default opacity. + let dragElement = document.createElementNS(HTML_NAMESPACE, "div"); + dragElement.classList.add("newtab-drag"); + let scrollbox = document.getElementById("newtab-vertical-margin"); + scrollbox.appendChild(dragElement); + dt.setDragImage(dragElement, 0, 0); + + // After the 'dragstart' event has been processed we can remove the + // temporary drag element from the DOM. + setTimeout(() => scrollbox.removeChild(dragElement), 0); + } +}; diff --git a/browser/base/content/newtab/dragDataHelper.js b/browser/base/content/newtab/dragDataHelper.js new file mode 100644 index 000000000..675ff2671 --- /dev/null +++ b/browser/base/content/newtab/dragDataHelper.js @@ -0,0 +1,22 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +var gDragDataHelper = { + get mimeType() { + return "text/x-moz-url"; + }, + + getLinkFromDragEvent: function DragDataHelper_getLinkFromDragEvent(aEvent) { + let dt = aEvent.dataTransfer; + if (!dt || !dt.types.includes(this.mimeType)) { + return null; + } + + let data = dt.getData(this.mimeType) || ""; + let [url, title] = data.split(/[\r\n]+/); + return {url: url, title: title}; + } +}; diff --git a/browser/base/content/newtab/drop.js b/browser/base/content/newtab/drop.js new file mode 100644 index 000000000..748652455 --- /dev/null +++ b/browser/base/content/newtab/drop.js @@ -0,0 +1,150 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +// A little delay that prevents the grid from being too sensitive when dragging +// sites around. +const DELAY_REARRANGE_MS = 100; + +/** + * This singleton implements site dropping functionality. + */ +var gDrop = { + /** + * The last drop target. + */ + _lastDropTarget: null, + + /** + * Handles the 'dragenter' event. + * @param aCell The drop target cell. + */ + enter: function Drop_enter(aCell) { + this._delayedRearrange(aCell); + }, + + /** + * Handles the 'dragexit' event. + * @param aCell The drop target cell. + * @param aEvent The 'dragexit' event. + */ + exit: function Drop_exit(aCell, aEvent) { + if (aEvent.dataTransfer && !aEvent.dataTransfer.mozUserCancelled) { + this._delayedRearrange(); + } else { + // The drag operation has been cancelled. + this._cancelDelayedArrange(); + this._rearrange(); + } + }, + + /** + * Handles the 'drop' event. + * @param aCell The drop target cell. + * @param aEvent The 'dragexit' event. + */ + drop: function Drop_drop(aCell, aEvent) { + // The cell that is the drop target could contain a pinned site. We need + // to find out where that site has gone and re-pin it there. + if (aCell.containsPinnedSite()) + this._repinSitesAfterDrop(aCell); + + // Pin the dragged or insert the new site. + this._pinDraggedSite(aCell, aEvent); + + this._cancelDelayedArrange(); + + // Update the grid and move all sites to their new places. + gUpdater.updateGrid(); + }, + + /** + * Re-pins all pinned sites in their (new) positions. + * @param aCell The drop target cell. + */ + _repinSitesAfterDrop: function Drop_repinSitesAfterDrop(aCell) { + let sites = gDropPreview.rearrange(aCell); + + // Filter out pinned sites. + let pinnedSites = sites.filter(function (aSite) { + return aSite && aSite.isPinned(); + }); + + // Re-pin all shifted pinned cells. + pinnedSites.forEach(aSite => aSite.pin(sites.indexOf(aSite))); + }, + + /** + * Pins the dragged site in its new place. + * @param aCell The drop target cell. + * @param aEvent The 'dragexit' event. + */ + _pinDraggedSite: function Drop_pinDraggedSite(aCell, aEvent) { + let index = aCell.index; + let draggedSite = gDrag.draggedSite; + + if (draggedSite) { + // Pin the dragged site at its new place. + if (aCell != draggedSite.cell) + draggedSite.pin(index); + } else { + let link = gDragDataHelper.getLinkFromDragEvent(aEvent); + if (link) { + // A new link was dragged onto the grid. Create it by pinning its URL. + gPinnedLinks.pin(link, index); + + // Make sure the newly added link is not blocked. + gBlockedLinks.unblock(link); + } + } + }, + + /** + * Time a rearrange with a little delay. + * @param aCell The drop target cell. + */ + _delayedRearrange: function Drop_delayedRearrange(aCell) { + // The last drop target didn't change so there's no need to re-arrange. + if (this._lastDropTarget == aCell) + return; + + let self = this; + + function callback() { + self._rearrangeTimeout = null; + self._rearrange(aCell); + } + + this._cancelDelayedArrange(); + this._rearrangeTimeout = setTimeout(callback, DELAY_REARRANGE_MS); + + // Store the last drop target. + this._lastDropTarget = aCell; + }, + + /** + * Cancels a timed rearrange, if any. + */ + _cancelDelayedArrange: function Drop_cancelDelayedArrange() { + if (this._rearrangeTimeout) { + clearTimeout(this._rearrangeTimeout); + this._rearrangeTimeout = null; + } + }, + + /** + * Rearrange all sites in the grid depending on the current drop target. + * @param aCell The drop target cell. + */ + _rearrange: function Drop_rearrange(aCell) { + let sites = gGrid.sites; + + // We need to rearrange the grid only if there's a current drop target. + if (aCell) + sites = gDropPreview.rearrange(aCell); + + gTransformation.rearrangeSites(sites, {unfreeze: !aCell}); + } +}; diff --git a/browser/base/content/newtab/dropPreview.js b/browser/base/content/newtab/dropPreview.js new file mode 100644 index 000000000..fd7587a35 --- /dev/null +++ b/browser/base/content/newtab/dropPreview.js @@ -0,0 +1,222 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * This singleton provides the ability to re-arrange the current grid to + * indicate the transformation that results from dropping a cell at a certain + * position. + */ +var gDropPreview = { + /** + * Rearranges the sites currently contained in the grid when a site would be + * dropped onto the given cell. + * @param aCell The drop target cell. + * @return The re-arranged array of sites. + */ + rearrange: function DropPreview_rearrange(aCell) { + let sites = gGrid.sites; + + // Insert the dragged site into the current grid. + this._insertDraggedSite(sites, aCell); + + // After the new site has been inserted we need to correct the positions + // of all pinned tabs that have been moved around. + this._repositionPinnedSites(sites, aCell); + + return sites; + }, + + /** + * Inserts the currently dragged site into the given array of sites. + * @param aSites The array of sites to insert into. + * @param aCell The drop target cell. + */ + _insertDraggedSite: function DropPreview_insertDraggedSite(aSites, aCell) { + let dropIndex = aCell.index; + let draggedSite = gDrag.draggedSite; + + // We're currently dragging a site. + if (draggedSite) { + let dragCell = draggedSite.cell; + let dragIndex = dragCell.index; + + // Move the dragged site into its new position. + if (dragIndex != dropIndex) { + aSites.splice(dragIndex, 1); + aSites.splice(dropIndex, 0, draggedSite); + } + // We're handling an external drag item. + } else { + aSites.splice(dropIndex, 0, null); + } + }, + + /** + * Correct the position of all pinned sites that might have been moved to + * different positions after the dragged site has been inserted. + * @param aSites The array of sites containing the dragged site. + * @param aCell The drop target cell. + */ + _repositionPinnedSites: + function DropPreview_repositionPinnedSites(aSites, aCell) { + + // Collect all pinned sites. + let pinnedSites = this._filterPinnedSites(aSites, aCell); + + // Correct pinned site positions. + pinnedSites.forEach(function (aSite) { + aSites[aSites.indexOf(aSite)] = aSites[aSite.cell.index]; + aSites[aSite.cell.index] = aSite; + }, this); + + // There might be a pinned cell that got pushed out of the grid, try to + // sneak it in by removing a lower-priority cell. + if (this._hasOverflowedPinnedSite(aSites, aCell)) + this._repositionOverflowedPinnedSite(aSites, aCell); + }, + + /** + * Filter pinned sites out of the grid that are still on their old positions + * and have not moved. + * @param aSites The array of sites to filter. + * @param aCell The drop target cell. + * @return The filtered array of sites. + */ + _filterPinnedSites: function DropPreview_filterPinnedSites(aSites, aCell) { + let draggedSite = gDrag.draggedSite; + + // When dropping on a cell that contains a pinned site make sure that all + // pinned cells surrounding the drop target are moved as well. + let range = this._getPinnedRange(aCell); + + return aSites.filter(function (aSite, aIndex) { + // The site must be valid, pinned and not the dragged site. + if (!aSite || aSite == draggedSite || !aSite.isPinned()) + return false; + + let index = aSite.cell.index; + + // If it's not in the 'pinned range' it's a valid pinned site. + return (index > range.end || index < range.start); + }); + }, + + /** + * Determines the range of pinned sites surrounding the drop target cell. + * @param aCell The drop target cell. + * @return The range of pinned cells. + */ + _getPinnedRange: function DropPreview_getPinnedRange(aCell) { + let dropIndex = aCell.index; + let range = {start: dropIndex, end: dropIndex}; + + // We need a pinned range only when dropping on a pinned site. + if (aCell.containsPinnedSite()) { + let links = gPinnedLinks.links; + + // Find all previous siblings of the drop target that are pinned as well. + while (range.start && links[range.start - 1]) + range.start--; + + let maxEnd = links.length - 1; + + // Find all next siblings of the drop target that are pinned as well. + while (range.end < maxEnd && links[range.end + 1]) + range.end++; + } + + return range; + }, + + /** + * Checks if the given array of sites contains a pinned site that has + * been pushed out of the grid. + * @param aSites The array of sites to check. + * @param aCell The drop target cell. + * @return Whether there is an overflowed pinned cell. + */ + _hasOverflowedPinnedSite: + function DropPreview_hasOverflowedPinnedSite(aSites, aCell) { + + // If the drop target isn't pinned there's no way a pinned site has been + // pushed out of the grid so we can just exit here. + if (!aCell.containsPinnedSite()) + return false; + + let cells = gGrid.cells; + + // No cells have been pushed out of the grid, nothing to do here. + if (aSites.length <= cells.length) + return false; + + let overflowedSite = aSites[cells.length]; + + // Nothing to do if the site that got pushed out of the grid is not pinned. + return (overflowedSite && overflowedSite.isPinned()); + }, + + /** + * We have a overflowed pinned site that we need to re-position so that it's + * visible again. We try to find a lower-priority cell (empty or containing + * an unpinned site) that we can move it to. + * @param aSites The array of sites. + * @param aCell The drop target cell. + */ + _repositionOverflowedPinnedSite: + function DropPreview_repositionOverflowedPinnedSite(aSites, aCell) { + + // Try to find a lower-priority cell (empty or containing an unpinned site). + let index = this._indexOfLowerPrioritySite(aSites, aCell); + + if (index > -1) { + let cells = gGrid.cells; + let dropIndex = aCell.index; + + // Move all pinned cells to their new positions to let the overflowed + // site fit into the grid. + for (let i = index + 1, lastPosition = index; i < aSites.length; i++) { + if (i != dropIndex) { + aSites[lastPosition] = aSites[i]; + lastPosition = i; + } + } + + // Finally, remove the overflowed site from its previous position. + aSites.splice(cells.length, 1); + } + }, + + /** + * Finds the index of the last cell that is empty or contains an unpinned + * site. These are considered to be of a lower priority. + * @param aSites The array of sites. + * @param aCell The drop target cell. + * @return The cell's index. + */ + _indexOfLowerPrioritySite: + function DropPreview_indexOfLowerPrioritySite(aSites, aCell) { + + let cells = gGrid.cells; + let dropIndex = aCell.index; + + // Search (beginning with the last site in the grid) for a site that is + // empty or unpinned (an thus lower-priority) and can be pushed out of the + // grid instead of the pinned site. + for (let i = cells.length - 1; i >= 0; i--) { + // The cell that is our drop target is not a good choice. + if (i == dropIndex) + continue; + + let site = aSites[i]; + + // We can use the cell only if it's empty or the site is un-pinned. + if (!site || !site.isPinned()) + return i; + } + + return -1; + } +}; diff --git a/browser/base/content/newtab/dropTargetShim.js b/browser/base/content/newtab/dropTargetShim.js new file mode 100644 index 000000000..57a97fa00 --- /dev/null +++ b/browser/base/content/newtab/dropTargetShim.js @@ -0,0 +1,232 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * This singleton provides a custom drop target detection. We need this because + * the default DnD target detection relies on the cursor's position. We want + * to pick a drop target based on the dragged site's position. + */ +var gDropTargetShim = { + /** + * Cache for the position of all cells, cleaned after drag finished. + */ + _cellPositions: null, + + /** + * The last drop target that was hovered. + */ + _lastDropTarget: null, + + /** + * Initializes the drop target shim. + */ + init: function () { + gGrid.node.addEventListener("dragstart", this, true); + }, + + /** + * Add all event listeners needed during a drag operation. + */ + _addEventListeners: function () { + gGrid.node.addEventListener("dragend", this); + + let docElement = document.documentElement; + docElement.addEventListener("dragover", this); + docElement.addEventListener("dragenter", this); + docElement.addEventListener("drop", this); + }, + + /** + * Remove all event listeners that were needed during a drag operation. + */ + _removeEventListeners: function () { + gGrid.node.removeEventListener("dragend", this); + + let docElement = document.documentElement; + docElement.removeEventListener("dragover", this); + docElement.removeEventListener("dragenter", this); + docElement.removeEventListener("drop", this); + }, + + /** + * Handles all shim events. + */ + handleEvent: function (aEvent) { + switch (aEvent.type) { + case "dragstart": + this._dragstart(aEvent); + break; + case "dragenter": + aEvent.preventDefault(); + break; + case "dragover": + this._dragover(aEvent); + break; + case "drop": + this._drop(aEvent); + break; + case "dragend": + this._dragend(aEvent); + break; + } + }, + + /** + * Handles the 'dragstart' event. + * @param aEvent The 'dragstart' event. + */ + _dragstart: function (aEvent) { + if (aEvent.target.classList.contains("newtab-link")) { + gGrid.lock(); + this._addEventListeners(); + } + }, + + /** + * Handles the 'dragover' event. + * @param aEvent The 'dragover' event. + */ + _dragover: function (aEvent) { + // XXX bug 505521 - Use the dragover event to retrieve the + // current mouse coordinates while dragging. + let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode; + gDrag.drag(sourceNode._newtabSite, aEvent); + + // Find the current drop target, if there's one. + this._updateDropTarget(aEvent); + + // If we have a valid drop target, + // let the drag-and-drop service know. + if (this._lastDropTarget) { + aEvent.preventDefault(); + } + }, + + /** + * Handles the 'drop' event. + * @param aEvent The 'drop' event. + */ + _drop: function (aEvent) { + // We're accepting all drops. + aEvent.preventDefault(); + + // remember that drop event was seen, this explicitly + // assumes that drop event preceeds dragend event + this._dropSeen = true; + + // Make sure to determine the current drop target + // in case the dragover event hasn't been fired. + this._updateDropTarget(aEvent); + + // A site was successfully dropped. + this._dispatchEvent(aEvent, "drop", this._lastDropTarget); + }, + + /** + * Handles the 'dragend' event. + * @param aEvent The 'dragend' event. + */ + _dragend: function (aEvent) { + if (this._lastDropTarget) { + if (aEvent.dataTransfer.mozUserCancelled || !this._dropSeen) { + // The drag operation was cancelled or no drop event was generated + this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget); + this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget); + } + + // Clean up. + this._lastDropTarget = null; + this._cellPositions = null; + } + + this._dropSeen = false; + gGrid.unlock(); + this._removeEventListeners(); + }, + + /** + * Tries to find the current drop target and will fire + * appropriate dragenter, dragexit, and dragleave events. + * @param aEvent The current drag event. + */ + _updateDropTarget: function (aEvent) { + // Let's see if we find a drop target. + let target = this._findDropTarget(aEvent); + + if (target != this._lastDropTarget) { + if (this._lastDropTarget) + // We left the last drop target. + this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget); + + if (target) + // We're now hovering a (new) drop target. + this._dispatchEvent(aEvent, "dragenter", target); + + if (this._lastDropTarget) + // We left the last drop target. + this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget); + + this._lastDropTarget = target; + } + }, + + /** + * Determines the current drop target by matching the dragged site's position + * against all cells in the grid. + * @return The currently hovered drop target or null. + */ + _findDropTarget: function () { + // These are the minimum intersection values - we want to use the cell if + // the site is >= 50% hovering its position. + let minWidth = gDrag.cellWidth / 2; + let minHeight = gDrag.cellHeight / 2; + + let cellPositions = this._getCellPositions(); + let rect = gTransformation.getNodePosition(gDrag.draggedSite.node); + + // Compare each cell's position to the dragged site's position. + for (let i = 0; i < cellPositions.length; i++) { + let inter = rect.intersect(cellPositions[i].rect); + + // If the intersection is big enough we found a drop target. + if (inter.width >= minWidth && inter.height >= minHeight) + return cellPositions[i].cell; + } + + // No drop target found. + return null; + }, + + /** + * Gets the positions of all cell nodes. + * @return The (cached) cell positions. + */ + _getCellPositions: function DropTargetShim_getCellPositions() { + if (this._cellPositions) + return this._cellPositions; + + return this._cellPositions = gGrid.cells.map(function (cell) { + return {cell: cell, rect: gTransformation.getNodePosition(cell.node)}; + }); + }, + + /** + * Dispatches a custom DragEvent on the given target node. + * @param aEvent The source event. + * @param aType The event type. + * @param aTarget The target node that receives the event. + */ + _dispatchEvent: function (aEvent, aType, aTarget) { + let node = aTarget.node; + let event = document.createEvent("DragEvent"); + + // The event should not bubble to prevent recursion. + event.initDragEvent(aType, false, true, window, 0, 0, 0, 0, 0, false, false, + false, false, 0, node, aEvent.dataTransfer); + + node.dispatchEvent(event); + } +}; diff --git a/browser/base/content/newtab/grid.js b/browser/base/content/newtab/grid.js new file mode 100644 index 000000000..b6f98fa17 --- /dev/null +++ b/browser/base/content/newtab/grid.js @@ -0,0 +1,279 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * Define various fixed dimensions + */ +const GRID_BOTTOM_EXTRA = 7; // title's line-height extends 7px past the margin +const GRID_WIDTH_EXTRA = 1; // provide 1px buffer to allow for rounding error +const SPONSORED_TAG_BUFFER = 2; // 2px buffer to clip off top of sponsored tag + +/** + * This singleton represents the grid that contains all sites. + */ +var gGrid = { + /** + * The DOM node of the grid. + */ + _node: null, + _gridDefaultContent: null, + get node() { return this._node; }, + + /** + * The cached DOM fragment for sites. + */ + _siteFragment: null, + + /** + * All cells contained in the grid. + */ + _cells: [], + get cells() { return this._cells; }, + + /** + * All sites contained in the grid's cells. Sites may be empty. + */ + get sites() { return [for (cell of this.cells) cell.site]; }, + + // Tells whether the grid has already been initialized. + get ready() { return !!this._ready; }, + + // Returns whether the page has finished loading yet. + get isDocumentLoaded() { return document.readyState == "complete"; }, + + /** + * Initializes the grid. + * @param aSelector The query selector of the grid. + */ + init: function Grid_init() { + this._node = document.getElementById("newtab-grid"); + this._gridDefaultContent = this._node.lastChild; + this._createSiteFragment(); + + gLinks.populateCache(() => { + this._refreshGrid(); + this._ready = true; + + // If fetching links took longer than loading the page itself then + // we need to resize the grid as that was blocked until now. + // We also want to resize now if the page was already loaded when + // initializing the grid (the user toggled the page). + this._resizeGrid(); + + addEventListener("resize", this); + }); + + // Resize the grid as soon as the page loads. + if (!this.isDocumentLoaded) { + addEventListener("load", this); + } + }, + + /** + * Creates a new site in the grid. + * @param aLink The new site's link. + * @param aCell The cell that will contain the new site. + * @return The newly created site. + */ + createSite: function Grid_createSite(aLink, aCell) { + let node = aCell.node; + node.appendChild(this._siteFragment.cloneNode(true)); + return new Site(node.firstElementChild, aLink); + }, + + /** + * Handles all grid events. + */ + handleEvent: function Grid_handleEvent(aEvent) { + switch (aEvent.type) { + case "load": + case "resize": + this._resizeGrid(); + break; + } + }, + + /** + * Locks the grid to block all pointer events. + */ + lock: function Grid_lock() { + this.node.setAttribute("locked", "true"); + }, + + /** + * Unlocks the grid to allow all pointer events. + */ + unlock: function Grid_unlock() { + this.node.removeAttribute("locked"); + }, + + /** + * Renders and resizes the gird. _resizeGrid() call is needed to ensure + * that scrollbar disappears when the bottom row becomes empty following + * the block action, or tile display is turmed off via cog menu + */ + + refresh() { + this._refreshGrid(); + this._resizeGrid(); + }, + + /** + * Renders the grid, including cells and sites. + */ + _refreshGrid() { + let cell = document.createElementNS(HTML_NAMESPACE, "div"); + cell.classList.add("newtab-cell"); + + // Creates all the cells up to the maximum + let fragment = document.createDocumentFragment(); + for (let i = 0; i < gGridPrefs.gridColumns * gGridPrefs.gridRows; i++) { + fragment.appendChild(cell.cloneNode(true)); + } + + // Create cells. + let cells = Array.from(fragment.childNodes, (cell) => new Cell(this, cell)); + + // Fetch links. + let links = gLinks.getLinks(); + + // Create sites. + let numLinks = Math.min(links.length, cells.length); + let hasHistoryTiles = false; + for (let i = 0; i < numLinks; i++) { + if (links[i]) { + this.createSite(links[i], cells[i]); + if (links[i].type == "history") { + hasHistoryTiles = true; + } + } + } + + this._cells = cells; + while (this._gridDefaultContent.nextSibling) { + this._gridDefaultContent.nextSibling.remove(); + } + this._node.appendChild(fragment); + + document.getElementById("topsites-heading").textContent = + hasHistoryTiles ? "Your Top Sites" : "Top Sites"; + }, + + /** + * Calculate the height for a number of rows up to the maximum rows + * @param rows Number of rows defaulting to the max + */ + _computeHeight: function Grid_computeHeight(aRows) { + let {gridRows} = gGridPrefs; + aRows = aRows === undefined ? gridRows : Math.min(gridRows, aRows); + return aRows * this._cellHeight + GRID_BOTTOM_EXTRA; + }, + + /** + * Creates the DOM fragment that is re-used when creating sites. + */ + _createSiteFragment: function Grid_createSiteFragment() { + let site = document.createElementNS(HTML_NAMESPACE, "div"); + site.classList.add("newtab-site"); + site.setAttribute("draggable", "true"); + + // Create the site's inner HTML code. + site.innerHTML = + '<span class="newtab-sponsored">' + newTabString("sponsored.button") + '</span>' + + '<a class="newtab-link">' + + ' <span class="newtab-thumbnail placeholder"/>' + + ' <span class="newtab-thumbnail thumbnail"/>' + + ' <span class="newtab-thumbnail enhanced-content"/>' + + ' <span class="newtab-title"/>' + + '</a>' + + '<input type="button" title="' + newTabString("pin") + '"' + + ' class="newtab-control newtab-control-pin"/>' + + '<input type="button" title="' + newTabString("block") + '"' + + ' class="newtab-control newtab-control-block"/>' + + '<span class="newtab-suggested"/>'; + + this._siteFragment = document.createDocumentFragment(); + this._siteFragment.appendChild(site); + }, + + /** + * Test a tile at a given position for being pinned or history + * @param position Position in sites array + */ + _isHistoricalTile: function Grid_isHistoricalTile(aPos) { + let site = this.sites[aPos]; + return site && (site.isPinned() || site.link && site.link.type == "history"); + }, + + /** + * Make sure the correct number of rows and columns are visible + */ + _resizeGrid: function Grid_resizeGrid() { + // If we're somehow called before the page has finished loading, + // let's bail out to avoid caching zero heights and widths. + // We'll be called again when DOMContentLoaded fires. + // Same goes for the grid if that's not ready yet. + if (!this.isDocumentLoaded || !this._ready) { + return; + } + + // Save the cell's computed height/width including margin and border + if (this._cellHeight === undefined) { + let refCell = document.querySelector(".newtab-cell"); + let style = getComputedStyle(refCell); + this._cellHeight = refCell.offsetHeight + + parseFloat(style.marginTop) + parseFloat(style.marginBottom); + this._cellWidth = refCell.offsetWidth + + parseFloat(style.marginLeft) + parseFloat(style.marginRight); + } + + let searchContainer = document.querySelector("#newtab-search-container"); + // Save search-container margin height + if (this._searchContainerMargin === undefined) { + let style = getComputedStyle(searchContainer); + this._searchContainerMargin = parseFloat(style.marginBottom) + + parseFloat(style.marginTop); + } + + // Find the number of rows we can place into view port + let availHeight = document.documentElement.clientHeight - + searchContainer.offsetHeight - this._searchContainerMargin; + let visibleRows = Math.floor(availHeight / this._cellHeight); + + // Find the number of columns that fit into view port + let maxGridWidth = gGridPrefs.gridColumns * this._cellWidth + GRID_WIDTH_EXTRA; + // available width is current grid width, but no greater than maxGridWidth + let availWidth = Math.min(document.querySelector("#newtab-grid").clientWidth, + maxGridWidth); + // finally get the number of columns we can fit into view port + let gridColumns = Math.floor(availWidth / this._cellWidth); + // walk sites backwords until a pinned or history tile is found or visibleRows reached + let tileIndex = Math.min(gGridPrefs.gridRows * gridColumns, this.sites.length) - 1; + while (tileIndex >= visibleRows * gridColumns) { + if (this._isHistoricalTile(tileIndex)) { + break; + } + tileIndex--; + } + + // Compute the actual number of grid rows we will display (potentially + // with a scroll bar). tileIndex now points to a historical tile with + // heighest index or to the last index of the visible row, if none found + // Dividing tileIndex by number of tiles in a column gives the rows + let gridRows = Math.floor(tileIndex / gridColumns) + 1; + + // we need to set grid width, for otherwise the scrollbar may shrink + // the grid when shown and cause grid layout to be different from + // what being computed above. This, in turn, may cause scrollbar shown + // for directory tiles, and introduce jitter when grid width is aligned + // exactly on the column boundary + this._node.style.width = gridColumns * this._cellWidth + "px"; + this._node.style.maxWidth = gGridPrefs.gridColumns * this._cellWidth + + GRID_WIDTH_EXTRA + "px"; + this._node.style.height = this._computeHeight() + "px"; + this._node.style.maxHeight = this._computeHeight(gridRows) - SPONSORED_TAG_BUFFER + "px"; + } +}; diff --git a/browser/base/content/newtab/newTab.css b/browser/base/content/newtab/newTab.css new file mode 100644 index 000000000..658ad2ed3 --- /dev/null +++ b/browser/base/content/newtab/newTab.css @@ -0,0 +1,654 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +html { + width: 100%; + height: 100%; +} + +body { + font: message-box; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + background-color: #F9F9F9; + display: -moz-box; + position: relative; + -moz-box-flex: 1; + -moz-user-focus: normal; + -moz-box-orient: vertical; +} + +input { + font: message-box; + font-size: 16px; +} + +input[type=button] { + cursor: pointer; +} + +/* UNDO */ +#newtab-undo-container { + transition: opacity 100ms ease-out; + -moz-box-align: center; + -moz-box-pack: center; +} + +#newtab-undo-container[undo-disabled] { + opacity: 0; + pointer-events: none; +} + +/* CUSTOMIZE */ +#newtab-customize-button { + position: absolute; + top: 10px; + right: 20px; + z-index: 101; +} + +#newtab-customize-button:dir(rtl) { + left: 20px; + right: auto; +} + +/* MARGINS */ +#newtab-vertical-margin { + display: -moz-box; + position: relative; + -moz-box-flex: 1; + -moz-box-orient: vertical; +} + +#newtab-margin-undo-container { + display: -moz-box; + left: 6px; + position: absolute; + top: 6px; + z-index: 1; +} + +#newtab-margin-undo-container:dir(rtl) { + left: auto; + right: 6px; +} + +#newtab-undo-close-button:dir(rtl) { + float:left; +} + +#newtab-horizontal-margin { + display: -moz-box; + -moz-box-flex: 1; +} + +#newtab-margin-top, +#newtab-margin-bottom { + display: -moz-box; + position: relative; +} + +#newtab-margin-top { + -moz-box-flex: 1; +} + +#newtab-margin-bottom { + -moz-box-flex: 2; +} + +.newtab-side-margin { + min-width: 10px; + -moz-box-flex: 1; +} + +/* GRID */ +#newtab-grid { + -moz-box-flex: 5; + overflow: hidden; + text-align: center; + transition: 100ms ease-out; + transition-property: opacity; +} + +#newtab-grid[page-disabled] { + opacity: 0; +} + +#newtab-grid[locked], +#newtab-grid[page-disabled] { + pointer-events: none; +} + +body:not(.compact) #topsites-heading { + display: none; +} + +/* + * If you change the sizes here, make sure you + * change the preferences: + * toolkit.pageThumbs.minWidth + * toolkit.pageThumbs.minHeight + */ +/* CELLS */ +.newtab-cell { + display: -moz-box; + height: 210px; + margin: 20px 10px 35px; + width: 290px; +} + +body.compact .newtab-cell { + width: 110px; + height: 110px; + margin: 12px; +} + +/* SITES */ +.newtab-site { + position: relative; + -moz-box-flex: 1; + transition: 100ms ease-out; + transition-property: top, left, opacity; +} + +.newtab-site[frozen] { + position: absolute; + pointer-events: none; +} + +.newtab-site[dragged] { + transition-property: none; + z-index: 10; +} + +/* LINK + THUMBNAILS */ +.newtab-link, +.newtab-thumbnail { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; +} + +/* TITLES */ +.newtab-sponsored, +.newtab-title, +.newtab-suggested { + overflow: hidden; + position: absolute; + right: 0; + text-align: center; +} + +.newtab-sponsored, +.newtab-title { + bottom: 0; + white-space: nowrap; + text-overflow: ellipsis; + vertical-align: middle; +} + +.newtab-suggested { + border: 1px solid transparent; + border-radius: 2px; + font-size: 12px; + height: 17px; + line-height: 17px; + margin-bottom: -1px; + padding: 2px 8px; + display: none; + margin-left: auto; + margin-right: auto; + left: 0; + top: 215px; + -moz-user-select: none; +} + +.newtab-suggested-bounds { + max-height: 34px; /* 34 / 17 = 2 lines maximum */ +} + +.newtab-title { + left: 0; + padding: 0 4px; +} + +.newtab-sponsored { + background-color: #FFFFFF; + border: 1px solid #E2E2E2; + border-radius: 3px; + color: #4A4A4A; + cursor: pointer; + display: none; + font-family: Arial; + font-size: 9px; + height: 17px; + left: 0; + line-height: 6px; + padding: 4px; + right: auto; + top: -15px; +} + +.newtab-site[suggested=true] > .newtab-sponsored { + background-color: #E2E2E2; + border: none; +} + +.newtab-site > .newtab-sponsored:-moz-any(:hover, [active]) { + background-color: #4A90E2; + border: 0; + color: white; +} + +.newtab-site > .newtab-sponsored[active] { + background-color: #000000; +} + +.newtab-sponsored:dir(rtl) { + right: 0; + left: auto; +} + +.newtab-site:-moz-any([type=enhanced], [type=sponsored], [suggested]) .newtab-sponsored { + display: block; +} + +.newtab-site[suggested] .newtab-suggested { + display: table; +} + +.sponsored-explain, +.sponsored-explain a, +.suggested-explain, +.suggested-explain a { + color: white; +} + +.sponsored-explain, +.suggested-explain { + background-color: rgba(51, 51, 51, 0.95); + bottom: 30px; + line-height: 20px; + padding: 15px 10px; + position: absolute; + text-align: start; +} + +.sponsored-explain input, +.suggested-explain input { + background-size: 18px; + height: 18px; + opacity: 1; + pointer-events: none; + position: static; + width: 18px; +} + +/* CONTROLS */ +.newtab-control { + position: absolute; + opacity: 0; + transition: opacity 100ms ease-out; +} + +.newtab-control:-moz-focusring, +.newtab-cell:not([ignorehover]) > .newtab-site:hover > .newtab-control { + opacity: 1; +} + +.newtab-control[dragged] { + opacity: 0 !important; +} + +@media (-moz-touch-enabled) { + .newtab-control { + opacity: 1; + } +} + +/* DRAG & DROP */ + +/* + * This is just a temporary drag element used for dataTransfer.setDragImage() + * so that we can use custom drag images and elements. It needs an opacity of + * 0.01 so that the core code detects that it's in fact a visible element. + */ +.newtab-drag { + width: 1px; + height: 1px; + background-color: #fff; + opacity: 0.01; +} + +/* SEARCH */ +#newtab-search-container { + display: -moz-box; + position: relative; + -moz-box-pack: center; + margin: 40px 0 15px; +} + +body.compact #newtab-search-container { + margin-top: 0; + margin-bottom: 80px; +} + +#newtab-search-container[page-disabled] { + opacity: 0; + pointer-events: none; +} + +#newtab-search-form { + display: -moz-box; + position: relative; + height: 36px; + -moz-box-flex: 1; + max-width: 600px; /* 2 * (290 cell width + 10 cell margin) */ +} + +#newtab-search-icon { + border: 1px transparent; + padding: 0; + margin: 0; + width: 36px; + height: 36px; + background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat; + position: absolute; +} + +#newtab-search-text { + -moz-box-flex: 1; + padding-top: 6px; + padding-bottom: 6px; + padding-inline-start: 34px; + padding-inline-end: 8px; + background: hsla(0,0%,100%,.9) padding-box; + border: 1px solid; + border-spacing: 0; + border-radius: 2px 0 0 2px; + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); + box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset, + 0 0 2px hsla(210,65%,9%,.1) inset, + 0 1px 0 hsla(0,0%,100%,.2); + color: inherit; + unicode-bidi: plaintext; +} + +#newtab-search-text:dir(rtl) { + border-radius: 0 2px 2px 0; +} + +#newtab-search-text[aria-expanded="true"] { + border-radius: 2px 0 0 0; +} + +#newtab-search-text[aria-expanded="true"]:dir(rtl) { + border-radius: 0 2px 0 0; +} + +#newtab-search-text[keepfocus], +#newtab-search-text:focus, +#newtab-search-text[autofocus] { + border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6); +} + +#newtab-search-submit { + margin-inline-start: -1px; + color: transparent; + background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go") center center no-repeat, linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box; + padding: 0; + border: 1px solid; + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); + border-radius: 0 2px 2px 0; + border-inline-start: 1px solid transparent; + box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, + 0 1px 0 hsla(0,0%,100%,.2); + cursor: pointer; + transition-property: background-color, border-color, box-shadow; + transition-duration: 150ms; + width: 50px; +} + +#newtab-search-submit:dir(rtl) { + border-radius: 2px 0 0 2px; + background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-rtl"), linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)); +} + +#newtab-search-text:focus + #newtab-search-submit, +#newtab-search-text + #newtab-search-submit:hover, +#newtab-search-text[autofocus] + #newtab-search-submit { + border-color: #59b5fc #45a3e7 #3294d5; +} + +#newtab-search-text:focus + #newtab-search-submit, +#newtab-search-text[keepfocus] + #newtab-search-submit, +#newtab-search-text[autofocus] + #newtab-search-submit { + background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted"), linear-gradient(#4cb1ff, #1793e5); + box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, + 0 0 0 1px hsla(0,0%,100%,.1) inset, + 0 1px 0 hsla(210,54%,20%,.03); +} + +#newtab-search-text + #newtab-search-submit:hover { + background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted"), linear-gradient(#4cb1ff, #1793e5); + box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, + 0 0 0 1px hsla(0,0%,100%,.1) inset, + 0 1px 0 hsla(210,54%,20%,.03), + 0 0 4px hsla(206,100%,20%,.2); +} + +#newtab-search-text + #newtab-search-submit:hover:active { + box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset, + 0 0 1px hsla(211,79%,6%,.2) inset; + transition-duration: 0ms; +} + +#newtab-search-text:focus + #newtab-search-submit:dir(rtl), +#newtab-search-text[keepfocus] + #newtab-search-submit:dir(rtl), +#newtab-search-text[autofocus] + #newtab-search-submit:dir(rtl), +#newtab-search-text + #newtab-search-submit:dir(rtl):hover { + background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-rtl-inverted"), linear-gradient(#4cb1ff, #1793e5); +} + +/* CUSTOMIZE */ +#newtab-customize-overlay { + opacity: 0; + display: none; + width: 100%; + height: 100%; + background: #F9F9F9; + z-index: 100; + position: fixed; + transition: opacity .07s linear; +} + +.newtab-customize-panel-container { + position: absolute; + margin-right: 40px; + right: 0; +} + +.newtab-customize-panel-container:dir(rtl) { + right: auto; + left: 0; +} + +#newtab-customize-panel { + z-index: 999; + margin-top: 55px; + min-width: 270px; + position: absolute; + top: 100%; + right: -25px; + filter: drop-shadow(0 0 1px rgba(0,0,0,0.4)) drop-shadow(0 3px 4px rgba(0,0,0,0.4)); + transition: all 200ms ease-in-out; + transform-origin: top right; + transform: translate(-30px, -20px) scale(0) translate(30px, 20px); +} + +#newtab-customize-panel:dir(rtl) { + transform-origin: 40px top 20px; +} + +#newtab-customize-panel:dir(rtl), +#newtab-customize-panel-anchor:dir(rtl) { + left: 15px; + right: auto; +} + +#newtab-customize-panel[open="true"] { + transform: translate(-30px, -20px) scale(1) translate(30px, 20px); +} + +#newtab-customize-panel-anchor { + width: 18px; + height: 18px; + background-color: white; + transform: rotate(45deg); + position: absolute; + top: -6px; + right: 15px; +} + +#newtab-customize-title { + color: #7A7A7A; + font-size: 14px; + background-color: #FFFFFF; + line-height: 25px; + padding: 15px; + font-weight: 600; + cursor: default; + border-radius: 5px 5px 0px 0px; + max-width: 300px; + overflow: hidden; + display: table-cell; + border-top: none; +} + +#newtab-customize-panel-inner-wrapper { + background-color: #FFFFFF; + border-radius: 6px; + overflow: hidden; +} + +#newtab-customize-title > label { + cursor: default; +} + +#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent { + padding: 0; +} + +.newtab-customize-panel-item { + line-height: 25px; + padding: 15px; + padding-inline-start: 40px; + font-size: 14px; + cursor: pointer; + max-width: 300px; +} + +.newtab-customize-panel-item:not(:first-child) { + border-top: 1px solid threedshadow; +} + +.newtab-customize-panel-subitem > label, +.newtab-customize-panel-item > label, +.newtab-customize-complex-option { + padding: 0; + margin: 0; + cursor: pointer; +} + +.newtab-customize-panel-item, +.newtab-customize-complex-option { + display: block; + text-align: start; + background-color: #F9F9F9; +} + +.newtab-customize-panel-item[selected]:-moz-locale-dir(rtl) { + background-position: right 15px center; +} + +.newtab-customize-complex-option:hover > .selectable:not([selected]):-moz-locale-dir(rtl), +.selectable:not([selected]):hover:-moz-locale-dir(rtl) { + background-position: right 15px center; +} + +.newtab-customize-panel-item:not([selected]), +.newtab-customize-panel-subitem:not([selected]){ + color: #7A7A7A; +} + +.newtab-customize-panel-item:not([selected]):hover { + color: #FFFFFF; + background-color: #4A90E2 +} + +.newtab-customize-complex-option:hover > .selectable:not([selected]), +.selectable:not([selected]):hover { + background: url("chrome://global/skin/menu/shared-menu-check-hover.svg") no-repeat #FFFFFF; + background-size: 16px 16px; + background-position: 15px 15px; + color: #171F26; +} + +.newtab-customize-complex-option:hover > .selectable:not([selected]) + .newtab-customize-panel-subitem { + background-color: #FFFFFF; +} + +.newtab-customize-panel-item[selected] { + background: url("chrome://global/skin/menu/shared-menu-check-active.svg") no-repeat transparent; + background-size: 16px 16px; + background-position: 15px 15px; + color: black; + font-weight: 600; +} + +.newtab-customize-panel-subitem > .checkbox { + width: 18px; + height: 18px; + background-color: #FFFFFF; + border: solid 1px threedshadow; +} + +.newtab-customize-panel-subitem[selected] > .checkbox { + background: url("chrome://global/skin/menu/shared-menu-check-black.svg") no-repeat #FFFFFF; + background-size: 9px 9px; + background-position: center; + color: #333333; +} + +.newtab-customize-panel-subitem { + font-size: 12px; + padding: 0px 15px 15px 15px; + padding-inline-start: 40px; + display: block; + max-width: 300px; +} + +.newtab-customize-panel-subitem > label { + padding: 0px 10px; + line-height: 20px; + vertical-align: middle; + max-width: 225px; +} + +.newtab-customize-panel-superitem { + line-height: 20px; + border-bottom: medium none !important; + padding: 15px 15px 10px 15px; + padding-inline-start: 40px; + border-top: 1px solid threedshadow; +} + +.contentSearchSuggestionTable { + font: message-box; + font-size: 16px; +} diff --git a/browser/base/content/newtab/newTab.inadjacent.json b/browser/base/content/newtab/newTab.inadjacent.json new file mode 100644 index 000000000..53fb542af --- /dev/null +++ b/browser/base/content/newtab/newTab.inadjacent.json @@ -0,0 +1,3209 @@ +{ + "domains": [ + "rp5slFCxq/e7hYhXJCd0vQ==", + "2rEimAJDNX5g8HPZehOrGg==", + "nvLEpj6ZZF3LWH3wUB6lKg==", + "9Cqd4Lm3VvXuJxz79Bbqyg==", + "vNRy4LR+7TOKTixqsr5ybw==", + "N4zSgsZCo6Z4XRwZ4fu8WQ==", + "jsDtRfVbMsFg3KkEl2UiZQ==", + "TckkKpiq0a6J6NTw7uOZqw==", + "9Or7IAYuuIgZA370w9rNIg==", + "ul8WvOjCkxTz9LjT4RqTHg==", + "ZGJrbwb5878Nsqm0z+A7nQ==", + "5iT64HTeeG5SIFXG7A9o3w==", + "YSeSEghPe1kV6g8ghFcNAA==", + "0jIUl1NDmJZQkDY12VDeIQ==", + "aos6UyDyIw0R1nTK5wTawA==", + "G1xxubsq65ugK06UT2DO5A==", + "lbhavoDrDPP/8m0onwo63w==", + "ObcLsjW0SkdvY0nkZmiTGQ==", + "FHZ5084LC0nTAzZlnSKN3Q==", + "cdEr+0Fv5iaVZzalZToseg==", + "Co8WbNYbCPTFPcHpeK3hRQ==", + "qXSzhCEhByLQq9N84tqV+Q==", + "h3ufhRk5IEFaNH11rIACtQ==", + "fQ1PJ/JwazIaYoy/zy49QQ==", + "zAJqfbn54Nsm2ddGtkb59A==", + "ixPM9T8ik/gWGZ7BRIcaig==", + "/E9pwA3E3hVAZoYq3FmCyw==", + "U6ygonI8CxpruhpGB2+Q6A==", + "Igi4voB8oVMVw6WUeDSjZg==", + "jtuHIJhwoTGzavFpM7ilNw==", + "eBvTV27n6Gs+ZsBkpVynvw==", + "sFbzw0AUOGG0NEzkaSxVDg==", + "yAkIS+Ezj6woEff9YvdO7Q==", + "IP1+BwG6q60QzDADi8j7oA==", + "Q/teQEBFepHtwZ7UHa2TEA==", + "B1vDep5a1Gok5Gnth39+LA==", + "cyEIyQ2MZaPGf+K1x9Bbkg==", + "aaM+oEJnF4/nwMWyXJU8rA==", + "qpDNIpxah8FUiqXm5IRaUg==", + "ZTeJ35gMPqIv2WWbeNyIEg==", + "nzoAGQAnC/Xgg5PmOgXqkA==", + "J5pJDuNi3cqQiyaRJAJk4g==", + "2vqN53BXhXzPrKYsh6QH1A==", + "QlrzHNYxCwCBMVENvbXjQA==", + "Ou2HGn43nmsL3RWSNvMdXw==", + "3qk9lsvGTMqVMAZW+xihfw==", + "RncMe42RB2bhmUbYtGVnKQ==", + "hzNXR6dqPq1+vf4Qh5ByWA==", + "sRq3S2ZRs3H39cEQHv4Vig==", + "B4ThUBTVJOUPyOsHxikHXA==", + "A2lU9GkAdSibLO1JJfFnIA==", + "ef3HNkSvuWQrAzkuty2iqg==", + "yKDiRM6bf2xc0QXIwHYuaA==", + "AdCk4ccJuhA0bIT/61J+RQ==", + "UXvAZ7ULCVz2f505K0Wkvg==", + "ueKWblrOwVJNgiOvkXKLBQ==", + "s8u/jPuBAxu1d18HfV5Z0g==", + "hUT0Uc5YMUdNZQEGLz4hJw==", + "6jo/phmMTrEXKrNRsionGQ==", + "s/Ea/3fkyJ9honzPJkgEQQ==", + "hgu2/Jf+WrQAHfO+asW2zw==", + "kiVuTNwZ1r2lqYEZxIHyiQ==", + "24T5KVrVE2mYwJ5Goj3xJw==", + "fiWBVlfj97GGjEvf/Q9Spg==", + "5VWdlvJe7eoXMGkTtHzCUg==", + "+cFQxKa5RWVtc1z00Jujew==", + "nVa+rLH5p+yXBksLwQsjRQ==", + "5tyI6bMdb3tMIi4ewvr/SQ==", + "S6Roj31yS5bZbSFcd3f4Hg==", + "uW1Zl8iuEF8ZT/gwCBEqwA==", + "YwL+FJgxlZ8JVig+9iP5Cw==", + "ThIYK/mQsp9cMf8+rws/4Q==", + "w0oSxOhRG6kE9B868aoYVQ==", + "DJUDGQ0J32dF1kfItyxALg==", + "34/ab69lPkuAKt6WBxJPpA==", + "25jH4C9apgqWZGZP15lM6Q==", + "GxvwleSaSwILD1pG9k9buA==", + "YRAMt2ArEINo83ms6AqJ0A==", + "15HyTJNoMYzi3XCkeU5Z7A==", + "/SqjXGD+TKC90uz1vsjqUw==", + "karhKOknkhtg/LSFo9BGRA==", + "+tD1d0t3vfJvc1hUAvTT4Q==", + "rkaKbtlnyVr53D0rexLqdQ==", + "fAugw4rtnXzzRXfC1wRgOQ==", + "RgxoepF/XOwIsGat5r5HpQ==", + "Y49/EnzVz3ugXCYxFjFN7g==", + "tHMyzBm/2wNDw7TeNeujbg==", + "LlqzYV4uZpiJy4ORWPSekA==", + "M3Huar7/ded9OGgDwJhZgw==", + "QkNNATSx/PJ1XjgZyTtkUQ==", + "skVw0v6Wx00sfHAScPK1Bw==", + "v1gsIvg+C68T9wixMzL2sQ==", + "hDL75EXhl7BaYnAxkoGwbw==", + "tReG97snx2ESpXbfllCL7Q==", + "EiZBXR15dT1TMrkgkzmkvw==", + "5hRHirfD90/sdp0ILJQU8A==", + "rabElvtYtG0jW6dxAOHofg==", + "JpxoTRKWN+SEeBQ453R1YQ==", + "Faz4Lm0cpjvF0IjVkHiZMg==", + "jGErFAIoXx+50KFpVIGZiA==", + "5GzBkduKpUX7u1uwtYIFug==", + "cRBe0J9/KWRX19N2vPkCiw==", + "t/7g8t4Kr3/+SCnOn3XFWQ==", + "sd08c6jUXs5/hxND0fBkPA==", + "nTxKpqIdnHNdpDk7Dx3TEg==", + "5l+RALcce+lTDnmXI+Wqqg==", + "pzJ4QmEBGRNiiX6z0xHh8g==", + "Vfl3YbqR6JRR7SIdsUA/vA==", + "cgfhOdB376a4GAcuACADvA==", + "inAefsQM6tiIhQCMtcPcyA==", + "FTSULGL8CMhmcc7Cyf/X8A==", + "9XSWpaZyHEy7V/tuw5uZEw==", + "+VtM1opKlgb/jrCwc4YjFA==", + "oF46xheuI/NUxUOnOttzvA==", + "Qy+lvhDbJCumr6kiPLd1oA==", + "swps7UEKpIbVBJ9SnPK3zQ==", + "b7wyIiJvJs+29QePxsdWtQ==", + "x3iDZxYyuHtG/9rNW5HMYg==", + "r1dx1g5UOksywvOaQTamfA==", + "KvVF3Si/fr4JQtr7jCJiog==", + "spvZ7hhtG5QY7JXs96lBUg==", + "ECL8mA5B6CswyDH6yJ4hVw==", + "7Uu+YsdS69dMSDYUr6vTag==", + "Rnm9pSvQRRbkHpOijraLZw==", + "aQJqpnXdzqNSFhMn3EJA2Q==", + "TctnXpd7Wd5ZXKMnOFHAQA==", + "+lPqG8l6mf2FWVGWflyF/g==", + "mPmnmL2oRRJmKYjQ6TfN3g==", + "fyXFcT5ZCawDBg74n1WSpg==", + "uq5Zrxq10pO1HoPxReT5og==", + "3eoCsOKXY8RDrHSdlXqmrA==", + "9nQv2BFG56xsHViN5UpHYw==", + "RtP/nJgy/ItyuDrpBbAotg==", + "5E/drRptfHmBhJ7qplujGg==", + "cUxyZvoqXbQ0a/0I9s6Zbg==", + "womzqSigwEF30V422YmxKw==", + "FPvZqDfN8dTFHLVOuYEbUA==", + "YZMXx+scKXp/v9GaJjb1bA==", + "bjURu5MRsNIZavG5HV0eZw==", + "iY0C9uSMEOn8ikT+J7+/Eg==", + "aXkD6BzsdkMEv7A+eYqQQQ==", + "dOcOfEDGHYG2kgmrglDkPw==", + "c7GjtY05Mh+cp6SNuWY3Ig==", + "lM1uY1oVncHXNzKs/cCEtQ==", + "7jXnQJkutLsi+r9aYmrMxw==", + "NgrugWWduj2qdWnEQf9dLA==", + "faYjmy/yn5iXdS28QCIdWw==", + "68XbaOvIZpCGb4G1gaKErA==", + "Yi67HkOLtGYXeL7WD4GPrA==", + "Puo8gXuUkwcoQViaXwkdSQ==", + "L202Et5aZh60Vl20LTKNFg==", + "4agAzQ5+dnTmLZEjsZs26g==", + "LegGM1ft8Y7Ka3CUxpObvg==", + "KRdILc1QDOpow5im/qY+Kw==", + "peMW+rpwmXrSwplVuB/gTA==", + "Pic1ncr+Zn6wv75zjAdzQA==", + "ilSPlWYbiPzIC13vQUBlOw==", + "GUlDufLoTalBqrG/h3mZ6w==", + "5twANNlT57T9BG4r2D9+Hw==", + "ENrnM8HlMi+5y8Hsu4Pn4A==", + "K/DzpLEbz1MpRjA6qyYn4Q==", + "yN1cHJRHDXoFxFZacL6wsw==", + "Rc6r+KqIePH+dnj1aNYCsQ==", + "8u/z5htgqXVU5Dqwd9whJQ==", + "jV575O42EYoqDNxCm9643Q==", + "xCxGo0h3lS8N6X+ivKfpjA==", + "us+2nfpj2gjI7s14Hw0gmA==", + "bp90A/rbESwVU7eh9xRTfQ==", + "5QtMXzbTafvKDQOWZP7M8w==", + "1gFCxPLjQlQGKmSGmHwmJQ==", + "m+/dnOIe6SaIFhfvg+ybDg==", + "9Dcg87+RPq9U+swRg4dH3Q==", + "mnjbL7WFmrWp0RUqS8AMGA==", + "/0e0E+NFmq8GeE5+y2Gekw==", + "y11mbpHHtka9Ep8cr2nEvQ==", + "GdmjRyliw+W21Q+dHO4CWA==", + "X65wWQTpkg756V/Nfn92kQ==", + "xj06KvacQOxRSofbhzBNgA==", + "nVDxVhaa2o38gd1XJgE3aw==", + "4IV+JOGXrltpkQamBRXMgA==", + "jIfp8LqaYXT88r/K3a8gNw==", + "vhT4dDtbMFVyevS6yCGy0g==", + "zMs7/x8hDt8xj2FFc5+6vA==", + "1J7u2N62JGb2VrnCRlJIrw==", + "3hJs9P/RRxB0CO4q0Icb+g==", + "ZpuVY1ZyoKD3hqosdsfT6Q==", + "6KIM7C7eWgxZtqZboiJvZQ==", + "vtb6fdqirkuUkqITAmXTlw==", + "C/rEVr22mw2u/1dwUx9VTg==", + "NrY4Q5C67haCWLK8HXHq9g==", + "X9qvEftCEFWX3gBU5hXy+Q==", + "0zgw7xNB3xVGaH48TyxaNQ==", + "g7J9Jy/PJrAGRgVdvA+bEg==", + "9zb+anAyZVBzuU9rW4cJtg==", + "6Zc5FzT/m0YIjxEPYA6zDQ==", + "R2YPNlvCbVK0EodTR7czIw==", + "gsI6EGgXMtDu+1u364A8mw==", + "Bg2wBFb1/xaxeEiHfBHX+A==", + "64aapfVI6dV3LpTK56KZlg==", + "HdgcJU0W3yVnH69VYStmug==", + "qTDCcv+LK3JPFB/++t66IQ==", + "P0HEIXMnAmbvq+QYREwFzw==", + "aaU7CAmtyE35jNKTkyXOkg==", + "r9G97WKDiQ48qJHP9LBRNg==", + "8mPgQhYVDn8KshDDvvf5SA==", + "GCQiiOLDguXLiYwuLcFPsA==", + "R2Use39If2C0FVBP7KDerA==", + "23C4eh3yBb5n/RNZeTyJkA==", + "2QQtKtBAm2AjJ5c0WQ6BQA==", + "Qc+XYy2qyWJ5VVwd2PExbw==", + "zJ7ScHNxr2leCDNNcuDApA==", + "vFtC0B2oe1gck28JOM1dyg==", + "bLEntCrCHFy9pg3T3gbBzg==", + "G3PmmPGHaWHpPW30xQgm3Q==", + "me61ST+JrXM5k3/a11gRAA==", + "+LJYVZl1iPrdMU3L5+nxZw==", + "CLPzjXKGGpJ0VrkSJp7wPQ==", + "Pc+u0MAzp4lndTz4m6oQ5w==", + "cwBNvZc0u4bGABo88YUsVQ==", + "q7m/EtZySBjZNBjQ5m1hKw==", + "8ZBiwr842ZMKphlqmNngHw==", + "LMCZqd3UoF/kHHwzTdj7Tw==", + "0ODJyWKJSfObo+FNdRQkkA==", + "ViweSJuNWbx5Lc49ETEs/A==", + "x+8rwkqKCv0juoT5m1A4eg==", + "pxuSWn1u+bHtRjyh2Z8veA==", + "GKzs8mlnQQc58CyOBTlfIg==", + "Owg8qCpjZa+PmbhZew6/sw==", + "YLz+HA6qIneP+4naavq44Q==", + "9ajIS45NTicqRANzRhDWFA==", + "DjeSrUoWW2QAZOAybeLGJg==", + "qxALQrqHoDq9d91nU0DckA==", + "yPIeWcW8+3HjDagegrN8bw==", + "ocpLRASvTgqfkY20YlVFHQ==", + "RuLeQHP1wHsxhdmYMcgtrQ==", + "3WwITQML938W9+MUM56a3A==", + "ZbLVNTQSVZQWTNgC4ZGfQg==", + "X6Ln4si8G5aKar52ZH/FEQ==", + "+gbitI/gpxebN/rK7qj8Fw==", + "7cnUHeaPO8txZGGWHL9tKg==", + "epY+dsm5EMoXnZCnO4WSHw==", + "nf8x+F03kOpMhsCSUWEhVg==", + "VE4sLM5bKlLdk85sslxiLQ==", + "Hs3vUOOs2TWQdQZHs+FaQQ==", + "hkOBNoHbno2iNR7t3/d4vg==", + "Ar9N1VYgE7riwmcrM3bA2Q==", + "SbMjjI8/P8B9a9H2G0wHEQ==", + "tU31r8zla146sqczdKXufg==", + "tFmWYH82I3zb+ymk5dhepA==", + "XHjrTLXkm/bBY/BewmJcCQ==", + "FV/D5uSco+Iz8L+5t7E8SA==", + "yKLLiqzxfrCsr6+Rm6kx1Q==", + "B6reUwMkQFaCHb9BYZExpw==", + "5jyuDp82Fux+B0+zlx8EXw==", + "WGKFTWJac8uehn3N59yHJw==", + "JQf9UmutPh3tAnu7FDk3nA==", + "hv5GrLEIjPb4bGOi8RSO0w==", + "p3V7NfveB6cNxFW7+XQNeQ==", + "DinJuuBX9OKsK5fUtcaTcQ==", + "UEMwF4kwgIGxGT4jrBhMPQ==", + "Y78dviyBS3Jq9zoRD5sZtQ==", + "zbjXhZaeyMfdTb2zxvmRMg==", + "kydoXVaNcx1peR5g6i588g==", + "M2suCoFHJ5fh9oKEpUG3xA==", + "/VnKh/NDv7y/bfO6CWsLaQ==", + "S+b37XhKRm8cDwRb1gSsKQ==", + "jz7QlwxCIzysP39Cgro8jg==", + "IjmLaf3stWDAwvjzNbJpQA==", + "cHSj5dpQ04h/WyefjABfmQ==", + "+gO0bg8LY+py2dLM1sM7Ag==", + "fSANOaHD0Koaqg7AoieY9A==", + "vqYHQ3MnHrAIAr1QHwfIag==", + "Uh1mvZNGehK1AaI4a1auKQ==", + "HCbHUfsTDl6+bxPjT57lrA==", + "S7Vjy/gOWp0HozPP1RUOZw==", + "KPh6TwYpspne4KZA6NyMbw==", + "cfh5VZFmIqJH/bKboDvtlA==", + "H1zH9I8RwfEy5DGz3z+dHw==", + "2ksediOVrh4asSBxKcudTg==", + "+jVN/3ASc2O44sX6ab8/cg==", + "uvKYnKE01D5r7kR9UQyo5A==", + "BB9PTlwKAWkExt3kKC/Wog==", + "yqQPU4jT9XvRABZgNQXjgg==", + "6v3eTZtPYBfKFSjfOo2UaA==", + "49z/15Nx9Og7dN9ebVqIzg==", + "VjclDY8HN4fSpB263jsEiQ==", + "vSKsa0JhLCe9QFZKkcj58Q==", + "PolhKCedOsplEcaX4hQ0YQ==", + "D0Qt9sRlMaPnOv1xaq+XUg==", + "gBgJF0PiGEfcUnXF0RO7/w==", + "sC11Rf/mau3FG5SnON4+vQ==", + "rKb3TBM4EPx/RErFOFVCnQ==", + "+n0K7OB2ItzhySZ4rhUrMg==", + "Epm0d/DvXkOFeM4hoPCBrg==", + "K8PVQhEJCEH1ghwOdztjRw==", + "xjA21QjNdThLW3VV7SCnrg==", + "nE72uQToQFVLOzcu/nMjww==", + "2Hc5oyl0AYRy2VzcDKy+VA==", + "Y7XpxIwsGK3Lm/7jX/rRmg==", + "MK7AqlJIGqK2+K5mCvMXRQ==", + "mXycPfF5zOvcj1p4hnikWw==", + "V1fvtnJ0L3sluj9nI5KzRw==", + "TahqPgS7kEg+y6Df0HBASw==", + "EKU3OVlT4b/8j3MTBqpMNg==", + "EdvIAKdRAXj7e42mMlFOGQ==", + "uPm+cF4Jq08S5pQhYFjU8A==", + "CnIwpRVC2URVfoiymnsdYQ==", + "wyx5mnUMgP5wjykjAfTO7w==", + "OwIGvTh8FPFqa4ijNkguAw==", + "4ID0PHTzIMZz2rQqDGBVfA==", + "rlXt6zKE7DswUl0oWGOQUQ==", + "4NP8EFFJyPcuQKnBSxzKgQ==", + "bJgsuw29cO2WozqsGZxl7w==", + "b3q8kjHJPj9DWrz3yNgwjQ==", + "QGYFMpkv37CS2wmyp42ppg==", + "Kzs+/IZJO8v4uIv9mlyJ2Q==", + "ZJY+hujfd58mTKTdsmHoQQ==", + "R8FxgXWKBpEVbnl41+tWEw==", + "+CvLiih/gf2ugXAF+LgWqw==", + "BDbfe/xa9Mz1lVD82ZYRGA==", + "Dz90OhYEjpaJ/pxwg1Qxhg==", + "MLHt6Ak288G0RGhCVaOeqA==", + "r0QffVKB9OD9yGsOtqzlhA==", + "hK8KhTFcR06onlIJjTji/Q==", + "wMum67lfk5E1ohUObJgrOg==", + "JKmZqz9cUnj6eTsWnFaB0A==", + "rtJdfki8fG6CB36CADp0QA==", + "cUyqCa7Oue934riyC17F8g==", + "y4Y4mSSTw/WrIdRpktc5Hw==", + "r36kVMpF+9J+sfI3GeGqow==", + "ydVj2odhergi+2zGUwK4/A==", + "J2NFyb8cXEpZyxWDthYQiA==", + "qYuo5vY8V3tZx41Kh9/4Dw==", + "jrfRznO0nAz6tZM1mHOKIA==", + "JSr/lqDej81xqUvd/O2s7w==", + "vHGjRRSlZHJIliCwIkCAmQ==", + "sQAxqWXeiu/Su0pnnXgI9A==", + "xPe76nHyHmald6kmMQsKdg==", + "50jASqzGm4VyHJbFv8qVRA==", + "uuiJ+yB7JLDh2ulthM0mjg==", + "TI90EuS/bHq/CAlX32UFXg==", + "JgxNrUlL8wutG04ogKFPvw==", + "aMa1yVA71/w6Uf1Szc9rMA==", + "k/Aou2Jmyh8Bu3k8/+ndsQ==", + "iANKiuMqWzrHSk9nbPe3bQ==", + "7GgNLBppgAKcgJCDSsRqOQ==", + "bzVeU2qM9zHuzf7cVIsSZw==", + "rkeLYwMZ1/pW2EmIibALfA==", + "91+Yms6Oy/rP0rVjha5z9w==", + "JgXSPXDqaS1G9NqmJXZG0A==", + "ZzduJxTnXLD9EPKMn1LI4Q==", + "6W79FmpUN1ByNtv5IEXY4w==", + "Y1Nm3omeWX2MXaCjDDYnWQ==", + "ejfikwrSPMqEHjZAk3DMkA==", + "WNfDNaWUOqABQ6c6kR+eyw==", + "4BkqgraeXY7yaI1FE07Evw==", + "AjHz9GkRTFPjrqBokCDzFw==", + "T/6gSz2HwWJDFIVrmcm8Ug==", + "VWy9lB5t4fNCp4O/4n8S4w==", + "/FdZzSprPnNDPwbhV1C0Cg==", + "LUWxfy4lfgB5wUrqCOUisw==", + "r1VGXWeqGeGbfKjigaAS+Q==", + "ztULoqHvCOE6qV7ocqa4/w==", + "QCpzCTReHxGm5lcLsgwPCA==", + "Hst3yfyTB7yBUinvVzYROQ==", + "gf1Ypna/Tt+TZ08Y+GcvGg==", + "3rbml1D0gfXnwOs5jRZ3gA==", + "2vm7g3rk1ACJOTCXkLB3zA==", + "11FE2kknwYi2Qu0JUKMn3A==", + "1b2uf+CdVjufqiVpUShvHw==", + "0a4SafpDIe8V4FlFWYkMHw==", + "7btpMFgeGkUsiTtsmNxGQA==", + "dUx1REyXKiDFAABooqrKEA==", + "knYKU74onR6NkGVjQLezZg==", + "Scto+9TWxj1eZgvNKo+a9A==", + "cvZT1pvNbIL8TWg+SoTZdA==", + "1nXByug2eKq0kR3H3VjnWQ==", + "tG+rpfJBXlyGXxTmkceiKA==", + "7W9aF7dxnL+E8lbS/F7brg==", + "8vr+ERVrM99dp+IGnCWDGQ==", + "oFNMOKbQXcydxnp8fUNOHw==", + "uJZGw3IY2nCcdVeWW1geNQ==", + "q6LG0VzO1oxiogAAU63hyg==", + "f0H/AFSx2KLZi9kVx5BAZg==", + "1RQZ2pWSxT+RKyhBigtSFg==", + "scCQPl0em2Zmv/RQYar60g==", + "A2ODff+ImIkreJtDPUVrlg==", + "vRgkZZGVN7YZrlml0vxrKA==", + "68jPYo3znYoU4uWI7FH3/g==", + "iJ2nT8w8LuK11IXYqBK+YA==", + "54XELlPm8gBvx8D5bN3aUg==", + "PTAm/jGkie7OlgVOvPKpaA==", + "v7BrkRmK0FfWSHunTRHQFQ==", + "dVh/XMTUIx1nYN4q1iH1bA==", + "TSGL3iQYUgVg/O9SBKP9EA==", + "wTO49YX/ePHMWtcoxUAHpw==", + "bMb1ia0rElr2ZpZVhva0Jw==", + "sNmW2b2Ud7dZi3qOF8O8EQ==", + "3djRJvkZk9O2bZeUTe+7xQ==", + "I9KNZC1tijiG1T72C4cVqQ==", + "sQzCwNDlRsSH7iB9cTbBcg==", + "mk1CKDah7EzDJEdhL22B7w==", + "lON3WM0uMJ30F8poBMvAjQ==", + "88PNi9+yn3Bp4/upgxtWGA==", + "C+Ssp+v1r+00+qiTy2d7kA==", + "11U5XEwfMI7avx014LfC8g==", + "xsf0m31Am0W9eLhopAkfnA==", + "d13Rj3NJdcat0K/kxlHLFw==", + "UP7NXAE0uxHRXUAWPhto0w==", + "ZKXxq9yr7NGBOHidht34uQ==", + "Fd2fYFs8vtjws2kx1gf6Rw==", + "ojf6uL85EuEYgLvHoGhUrw==", + "KjnL3x+56r3M2pDj1pPihA==", + "WdCWezJU4JK43EOZ9YHVdg==", + "/jH6imhTPZ/tHI4gYz2+HA==", + "+OLntmlsMBBYPREPnS6iVw==", + "5lfLJAk1L3QzGMML3fOuSw==", + "AZs3v4KJYxdi8T1gjVjI2Q==", + "7pkUY2UzSbGnwLvyRrbxfA==", + "BjfOelfc1IBgmUxMJFjlbQ==", + "TcGhAJHRr7eMwGeFgpFBhg==", + "Y7iDCWYrO1coopM3RZWIPg==", + "mnalaO6xJucSiZ0+99r3Cg==", + "plXHHzA8X9QGwWzlJxhLRw==", + "Zqd6+81TwYuiIgLrToFOTQ==", + "1Pmnur6TbZ9cmemvu0+dSA==", + "OaNpzwshdHUZMphQXa6i8w==", + "WKehT4nGF2T7aKuzABDMlA==", + "4LvQSicqsgxQFWauqlcEjw==", + "BMZB1FwvAuEqyrd0rZrEzw==", + "YfbfE3WyYOW7083Y8sGfwQ==", + "46FCwqh+eMkf+czjhjworw==", + "734u4Y1R3u7UNUnD+wWUoA==", + "yf06Slv9l3IZEjVqvxP2aA==", + "bIk7Fa6SW7X18hfDjTKowg==", + "DnF6TYSJxlc+cwdfevLYng==", + "ionqS0piAOY2LeSReAz4zg==", + "hlMumZ7RJFpILuKs09ABtw==", + "NjeDgQ1nzH1XGRnLNqCmSg==", + "o7y4zQXQAryST2cak4gVbw==", + "29EybnMEO95Ng4l/qK4NWQ==", + "udU65VtsvJspYmamiOsgXw==", + "v1AWe5qb5y3vSKFb7ADeEw==", + "wK6Srd83eLigZ11Q20XGrg==", + "GmC+0rNDMIR+YbUudoNUXw==", + "W4utAK3ws0zjiba/3i91YA==", + "MlKWxeEh8404vXenBLq4bw==", + "Gdf4VEDLBrKJNQ8qzDsIyw==", + "Z9bDWIgcq6XwMoU2ECDR5Q==", + "VIkS30v268x+M1GCcq/A8A==", + "iPwX3SbbG9ez9HoHsrHbKw==", + "yKrsKX4/1B1C0TyvciNz5w==", + "BophnnMszW5o+ywgb+3Qbw==", + "eJLrGwPRa6NgWiOrw1pA7w==", + "eV+RwWPiGEB+76bqvw+hbA==", + "oad5SwflzN0vfNcyEyF4EA==", + "Uw6Iw+TP9ZdZGm2b/DAmkg==", + "9qWLbRLXWIBJUXYjYhY2pg==", + "dxWv00FN/2Cgmgq9U3NVDQ==", + "AX1HxQKXD12Yv5HWi39aPQ==", + "J0NauydfKsACUUEpMhQg8A==", + "mxug34EekabLz0JynutfBg==", + "bNq/hj0Cjt4lkLQeVxDVdQ==", + "nW3zZshjZEoM8KVJoVfnuQ==", + "ghp8sWGKWw20S/z1tbTxFg==", + "S4rFuiKLFKZ+cL7ldiTwpg==", + "8ZqmPJDnQSOFXvNMRQYG2Q==", + "6XYqR2WvDzx4fWO7BIOTjA==", + "Uo+FIhw1mfjF6/M8cE1c/Q==", + "bsHIShcLS134C+dTxFQHyA==", + "19yQHaBemtlgo2QkU5M6jQ==", + "sWLcS+m4aWk31BiBF+vfJQ==", + "BlCgDd7EYDIqnoAiKOXX6Q==", + "MrxR3cJaDHp0t3jQNThEyg==", + "cMo6l1EQESx1rIo+R4Vogg==", + "VOvrzqiZ1EHw+ZzzTWtpsw==", + "1/ZheMsbojazxt31j/l3iA==", + "0QxPAqRF8inBuFEEzNmLjA==", + "UXUNYEOffgW3AdBs7zTMFA==", + "lOPJhHqCtMRFZfWMX/vFZQ==", + "rXSbbRABEf4Ymtda45w8Fw==", + "jfegbZSZWkDoPulFomVntA==", + "hfcH5Az2M7rp+EjtVpPwsg==", + "VsXEBIaMkVftkxt1kIh7TA==", + "M20iX2sUfw5SXaZLZYlTaA==", + "VUDsc9RMS1fSM43c+Jo9dQ==", + "itPtn+JaO4i7wz2wOPOmDQ==", + "rCxoo4TP/+fupXMuIM0sDA==", + "cSHSg9xJz/3F6kc+hKXkwg==", + "b4BoZmzVErvuynxirLxn0w==", + "e4B3HmWjW+6hQzcOLru6Xg==", + "lTE6u9G/RzvmbuAzq2J2/Q==", + "897ptlztTjr7yk+pk8MT0Q==", + "jd6IpPJwOJW1otHKtKZ5Gw==", + "b4aFwwcWMXsSdgS1AdFOXA==", + "FltEN+7NKvzt+XAktHpfHA==", + "ZyDh3vCQWzS5DI1zSasXWA==", + "kcJ1acgBv6FtUhV8KuWoow==", + "zgEyxj/sCs63O98sZS94Yw==", + "/kGxvyEokQsVz0xlKzCn2A==", + "cxqHS4UbPolcYUwMMzgoOA==", + "62RHCbpGU8Hb+Ubn+SCTBg==", + "ePlsM/iOMme2jEUYwi15ng==", + "0fN+eHlbRS6mVZBbH/B9FQ==", + "k0XIjxp2vFG7sTrKcfAihA==", + "0rfG4gRugAwVP0i3AGVxxg==", + "M98hjSxCwvZ27aBaJTGozQ==", + "kzGNkWh3fz27cZer4BspUQ==", + "3CJbrUdW68E3Drhe4ahUnQ==", + "NGApiVkDSwzO45GT57GDQw==", + "lMjip5hbCjkD9JQjuhewDg==", + "GrSbnecYAC3j5gtoKntL0A==", + "9dbn0Kzwr9adCEfBJh78uQ==", + "64QzHOYX0A9++FqRzZRHlQ==", + "YZt6HwCvdI5DRQqndA/hBQ==", + "6GXHGF62/+jZ7PfIBlMxZw==", + "PBULPuFXb6V3Di713n3Gug==", + "8Cm19vJW8ivhFPy0oQXVNA==", + "zDSQ3NJuUGkVOlvVCATRwA==", + "6QAtjOK9enNLRhcVa2iaTg==", + "v/PshI6JjkL9nojLlMNfhg==", + "yTgN5xFIdz1MzFS6xMl5uQ==", + "SCO9nQncEcyVXGCtx30Jdg==", + "7b0oo4+qphu6HRvJq6qkHQ==", + "ol9xhVTG9e1wNo50JdZbOA==", + "hIABph+vhtSF5kkZQtOCTA==", + "k+IBS52XdOe5/hLp28ufnA==", + "6HnWgYNKohqhoa1tnjjU3A==", + "HDxGhvdQwGh0aLRYEGFqnw==", + "LDuBcL5r3PUuzKKZ9x6Kfw==", + "HPvYV94ufwiNHEImu4OYvQ==", + "h2cnQQF2/R3Mq2hWdDdrTg==", + "nqpKfidczdgrNaAyPi7BOQ==", + "2ywo4t5PPSVUCWDwUlOVwQ==", + "jZMDIu95ITTjaUX0pk4V5g==", + "bA2kaTpeXflTElTnQRp6GQ==", + "lwYQm2ynA3ik2gE1m11IEg==", + "5ugVOraop5P5z5XLlYPJyQ==", + "l2NppPcweAtmA1V2CNdk2Q==", + "DbWQI3H2tcJsVJThszfHGA==", + "H6HPFAcdHFbQUNrYnB74dA==", + "H1NJEI+fvOQbI51kaNQQjQ==", + "53UccFNzMi9mKmdeD82vAw==", + "lffapwUUgaQOIqLz2QPbAg==", + "rSvhrHyIlnIBlfNJqemEbw==", + "BLJk9wA88z6e0IQNrWJIVw==", + "5m1ijXEW+4RTNGZsDA/rxQ==", + "GG8a3BlwGrYIwZH9j3cnPA==", + "HhBHt5lQauNl7EZXpsDHJA==", + "/XjB6c5fxFGcKVAQ4o+OMw==", + "+tuUmnRDRWVLA+1k0dcUvg==", + "SM7E98MyViSSS9G0Pwzwyw==", + "c5q/8n7Oeffv3B1snHM/lA==", + "kwlAQhR2jPMmfLTAwcmoxw==", + "0b/xj6fd0x+aB8EB0LC4SA==", + "S8jlvuYuankCnvIvMVMzmg==", + "kZkmDatUOdIqs7GzH3nI1A==", + "obW3kzv2KBvuckU7F+tfjA==", + "pa8nkpAAzDKUldWjIvYMYg==", + "m+eh+ZqS74w2q0vejBkjaw==", + "LcoJBEPTlSsQwfuoKQUxEw==", + "KO2XVYyNZadcQv8aCNn5JA==", + "uvzmRcvgepW6mZbMfYgcNw==", + "KhUT2buOXavGCpcDOcbOYg==", + "fo3JL+2kPgDWfP+CCrFlFw==", + "wIfvvLKC61gOpsddUFjVog==", + "SPHU6ES1WVm0Mu2LB+YjrA==", + "LWWfRqgtph1XrpxF4N64TA==", + "LCvz/h9hbouXCmdWDPGWqg==", + "PXC6ZpdMH0ATis/jGW12iA==", + "z920R8eahJPiTsifrPYdxA==", + "GIHKW6plyLra0BmMOurFgA==", + "k6OmSlaSZ5CB0i7SD9LczQ==", + "YZ39RIXpeLAhyMgmW2vfkQ==", + "bs2QG8yYWxPzhtyMqO6u3A==", + "pKaTI+TfcV3p/sxbd2e7YQ==", + "xWYecfzAtXT9WyQ8NYY/hw==", + "Fz8EI+ZpYlbcttSHs5PfpA==", + "wfwuxn+Vja1DNwiDwL2pcQ==", + "wux5Y8AipBnc5tJapTzgEQ==", + "U+oTpcjhc0E+6UjP11OE/Q==", + "yTVJKBn72RjakMBXDoBKHg==", + "0TxcYwG72dT7Tg+eG8pP1w==", + "imZ+mwiT22sW2M9alcUFfg==", + "CkDIoAFLlIRXra78bxT/ZA==", + "4qMSNAxichi3ori/pR+o0w==", + "zNLlWGW/aKBhUwQZ4DZWoQ==", + "D31ZticrjGWAO45l5hFh7A==", + "HdXg64DBy5WcL5fRRiUVOg==", + "yhI5jHlfFJxu4eV5VJO2zQ==", + "e9GqAEnk8XI5ix6kJuieNQ==", + "EC0+iUdSZvmIEzipXgj7Gg==", + "chwv4+xbEAa93PHg8q9zgQ==", + "B1VVUbl8pU0Phyl1RYrmBg==", + "A+DLpIlYyCb9DaarpLN76g==", + "wHA+D5cObfV3kGORCdEknw==", + "+Mp+JIyO0XC5urvMyi3wvQ==", + "vUE8Iw3NyWXURpXyoNJdaw==", + "ParhxI6RtLETBSwB0vwChQ==", + "NxSdT2+MUkQN49pyNO2bJw==", + "JSyhTcHLTfzHsPrxJyiVrA==", + "PAlx9+U+yQCAc5Fi0BOG0w==", + "W/0s1x3Qm+wN8DhROk6FrQ==", + "L3Jt5dHQpWQk74IAuDOL8g==", + "VWb8U4jF/Ic0+wpoXi/y/g==", + "1wBuHqS1ciup31WTfm3NPg==", + "BDNM1u/9mefjuW1YM2DuBg==", + "SDi5+FoP9bMyKYp+vVv1XA==", + "23d9B9Gz5kUOi1I//EYsSQ==", + "/a9O7kWeXa0le45ab3+nVw==", + "PcoVtZrS1x1Q+6nfm4f80w==", + "A6TLWhipfymkjPYq8kaoDQ==", + "lzUQ1o7JAbdJYpmEqi6KnQ==", + "/2jGyMekNu7U136K+2N3Jg==", + "ZItMIn1vhGqAlpDHclg0Ig==", + "Ee4A3lTMLQ7iDQ7b8QP8Qg==", + "bO55S58bqDiRWXSAIUGJKw==", + "zeHF6fdeqcOId3fRUGscRw==", + "BxsDnI8jXr4lBwDbyHaYXw==", + "ylA6sU7Kaf9fMNIx1+sIlw==", + "ZWXfE3uGU91WpPMGyknmqw==", + "f1+fHgR5rDPsCZOzqrHM7Q==", + "8VqeoQELbCs232+Mu+HblA==", + "beSrliUu0BOadCWmx+yZyA==", + "NQVQfN3nIg9ipHiFh4BvfQ==", + "4wnUAbPT3AHRJrPwTTEjyw==", + "/cdR1i5TuQvO+u3Ov3b0KQ==", + "wtyAZIfhomcHe9dLbYoSvA==", + "ulpDxLeQnIRPnq6oaah2AA==", + "pdPwUHauXOowaq9hpL2yFw==", + "1+A9FCGP3bZhk6gU3LQtNg==", + "raYifKqev8pASjjuV+UTKQ==", + "+OERSmo7OQUUjudkccSMOA==", + "FeRovookFQIsXmHXUJhGOw==", + "USCvrMEm/Wqeu9oX6FrgcQ==", + "kly/2kE4/7ffbO34WTgoGg==", + "IindlAnepkazs5DssBCPhA==", + "Bq82MoMcDjIo/exqd/6UoA==", + "ocvA1/NbyxM0hanwwY6EiA==", + "rtd6mqFgGe98mqO0pFGbSw==", + "nvLEpj6ZZF3LWH3wUB6lKg==", + "AGd0rcLnQ0n+meYyJur1Pw==", + "wI7JrSPQwYHpv2lRsQu9nQ==", + "OnmvXbyT2BYsSDJYZhLScA==", + "CmBf5qchS1V3C2mS6Rl4bw==", + "TafM7nTE5d+tBpRCsb8TjQ==", + "wxkb8evGEaGf/rg/1XUWiA==", + "y1J+o6DC2sETFsySgpDZyA==", + "SVLHWPCCH7GPVCF7QApPbw==", + "HMWOlMmzocOIiJ7yG1YaDQ==", + "DJmrmNRKARzsTCKSMLmcNA==", + "/XC/FmMIOdhMTPqmy4DfUA==", + "63OTPaKM0xCfJOy9EDto+Q==", + "PxReytUUn/BbxYTFMu1r2Q==", + "WjDqf1LyFyhdd8qkwWk+MA==", + "/DiUApY7cVp5W9o24rkgRA==", + "alJtvTAD7dH/zss/Ek1DMQ==", + "xLm/bJBonpTs0PwsF0DvRg==", + "eAOEgF5N80A/oDVnlZYRAw==", + "LqgzKxbI6WTMz0AMIDJR5w==", + "MJ1FuK8PXcmnBAG9meU84A==", + "JLq/DrW2f26NaRwfpDXIEA==", + "fsrX00onlGvfsuiCc35pGg==", + "tXVb5f90k9l3e1oK2NGXog==", + "1JRgSHnfAQFQtSkFTttkqQ==", + "B0TaUQ6dKhPfSc5V/MjLEQ==", + "nkbLVLvh3ClKED97+nH+7Q==", + "avFTp3rS6z5zxQUZQuaBHQ==", + "lNF8PvUIN02NattcGi5u4g==", + "bBEndaOStXBpAK79FrgHaw==", + "dM9up4vKQV5LeX82j//1jQ==", + "4WO6eT0Rh6sokb29zSJQnQ==", + "RHKCMAqrPjvUYt13BVcmvw==", + "Ju4YwtPw+MKzpbC0wJsZow==", + "tzV7ixFH37ze4zuLILTlfA==", + "oPlhC4ebXdkIDazeMSn1fQ==", + "5pje7qyz8BRsa8U4a4rmoA==", + "7E6V6/zSjbtqraG7Umj+Jw==", + "8QK7emHS6rAcAF5QQemW/A==", + "LhqRc9oewY4XaaXTcnXIHQ==", + "p/7qM5+Lwzw1/lIPY91YxQ==", + "fy54Milpa7KZH/zgrDmMXQ==", + "LyPXOoOPMieqINtX8C9Zag==", + "aD4QvtMlr8Lk/zZgZ6zIMg==", + "dsueq9eygFXILDC7ZpamuA==", + "+mJLK+6qq8xFv7O/mbILTw==", + "nHUpYmfV59fe3RWaXhPs3Q==", + "VbCoGr8apEcN7xfdaVwVXw==", + "/2Chaw2M9DzsadFFkCu6WQ==", + "rKAQxu80Q8g1EEhW5Wh8tg==", + "RJJqFMeiCZHdsqs72J17MQ==", + "GF2yvI9UWf1WY7V7HXmKPA==", + "JyIDGL1m/w+pQDOyyeYupA==", + "wR2Gxb07nkaPcZHlEjr8iA==", + "PbDVq2Iw1eeM8c2o/XYdTA==", + "BL3buzSCV78rCXNEhUhuKQ==", + "i42XumprV/aDT5R0HcmfIQ==", + "DuEKxykezAvyaFO2/5ZmKQ==", + "6ACvJNfryPSjGOK39ov8Qg==", + "YaUKOTyByjUvp1XaoLiW5Q==", + "jNcMS2zX1iSZN9uYnb2EIg==", + "VRnx+kd6VdxChwsfbo1oeQ==", + "4Qinl7cWmVeLJgah8bcNkw==", + "Fiy3hkcGZQjNKSQP9vRqyA==", + "HaSc7MZphCMysTy2JbTJkw==", + "VhYGC8KYe5Up+UJ2OTLKUw==", + "K2gk9zWGd0lJFRMQ1AjQ/Q==", + "NfxVYc3RNWZwzh2RmfXpiA==", + "JGeqHRQpf4No74aCs+YTfA==", + "7VHlLw20dWck+I8tCEZilA==", + "V5HKdaTHjA8IzvHNd9C51g==", + "9TalxEyFgy6hFCM73hgb7Q==", + "R/y6+JJP8rzz1KITJ4qWBw==", + "7bM/pn4G7g7Zl6Xf1r62Lg==", + "CHsFJfsvZkPWDXkA6ZMsDQ==", + "uXuPA/2KJbb7ZX+NymN3dw==", + "o+nYS4TqJc6XOiuUzEpC3A==", + "8N3mhHt29FZDHn1P2WH1wQ==", + "uZ2gUA74/7Q33tI2TcGQlg==", + "8B12CamjOGzJDnQ+RkUf4w==", + "9FdpxlIFu11qIPdO7WC5nw==", + "G+sGF13VXPH4Ih6XgFEXxg==", + "y+1I05LDAYJ09tKMs3zW6g==", + "gnkadeCgjdmLdlu/AjBZJg==", + "1I+UVx3krrD4NhzO7dgfHQ==", + "8LNNoHe6rEQyJ0ebl151Mw==", + "yOE90OHQdyOfrAgwDvn2gA==", + "ayBGGPEy++biljvGcwIjXA==", + "o/Y4U6rWfsUCXJ72p5CUGw==", + "5kvyy902llnYGQdn2Py04w==", + "6k2cuk0McTThSMW/QRHfjA==", + "2XrR2hjDEvx8MQpHk9dnjw==", + "fv/PW8oexJYWf5De30fdLQ==", + "861mBNvjIkVgkBiocCUj/Q==", + "NKGY0ANVZ0gnUtzVx1pKSw==", + "4DIPP/yWRgRuFqVeqIyxMQ==", + "cgSEbLqqvDsNUyeA3ryJ6Q==", + "xbBxUP9JyY0wDgHDipBHeg==", + "c3WVxyC5ZFtzGeQlH5Gw+w==", + "ZKeTDCboOgCptrjSfgu0xw==", + "DjHszpS8Dgocv3oQkW/VZQ==", + "Iqszlv4R49UevjGxIPMhIA==", + "uChFnF0oCwARhAOz/d47eA==", + "0egBaMnAf0CQEXf1pCIKnA==", + "FnVNxl5AFH1AieYru2ZG+A==", + "2Ct+pLXrK6Ku1f4qehjurQ==", + "x2nSgcTjA3oGgI8mMgiqjw==", + "AUGmvZkpkKBry5bHZn4DJA==", + "x8kRVzohTdhkryvYeMvkMw==", + "rXfWkabSPN+23Ei1bdxfmQ==", + "ElTNyMR4Rg8ApKrPw88WPg==", + "9jxA/t3TQx8dQ+FBsn/YCg==", + "I07W2eDQwe6DVsm1zHKM8A==", + "0p1jMr06OyBoXQuSLYN4aQ==", + "odGhKtO4bDW5R8SYiI5yCg==", + "5Q/Y2V0iSVTK8HE8JerEig==", + "Ily2MKoFI1zr5LxBy93EmQ==", + "8dUcSkd2qnX5lD9B+fUe+Q==", + "80UE+Ivby3nwplO/HA7cPw==", + "sS6QcitMPdvUBLiMXkWQkw==", + "5VY++KiWgo7jXSdFJsPN3A==", + "aY6B28XdPnuYnbOy9uSP8A==", + "ZfRlID+pC1Rr4IY14jolMw==", + "/YuQw7oAF08KDptxJEBS9g==", + "16d+fhFlgayu3ttKVV/pbg==", + "8dBIsHMEAk7aoArLZKDZtg==", + "wRqaDZVHHurp5whOQ1kDbQ==", + "lFUq6PGk9dBRtUuiEW7Cug==", + "FoJZ61VrU8i084pAuoWhDQ==", + "4mig4AMLUw+T/ect9p4CfA==", + "Po0lhBfiMaXhl+vYh1D8gA==", + "z9cd+Qj+ueX34Zf3997MNQ==", + "1dsKN1nG6upj7kKTKuJWsQ==", + "UtLYUlQJ02oKcjNR3l+ktg==", + "O538ibsrI4gkE5tfwjxjmg==", + "G736AX070whraDxChqUrqw==", + "THs1r8ZEPChSGrrhrNTlsA==", + "pVG1hL96/+hQ+58rJJy6/A==", + "1BjsijOzgHt/0i36ZGffoQ==", + "6rIWazDEWU5WPZHLkqznuQ==", + "cdWUm6uLNzR/knuj2x75eA==", + "nsnX3tKkN1elr18E31tXDw==", + "0fnruVOCxEczscBuv4yL9A==", + "SVuEYfQ9FGyVMo1672n0Yg==", + "ZRWyfXyXqAaOEjkzWl949Q==", + "S2MAIYeDQeJ1pl9vhtYtUg==", + "vsRNZx4thFFFPneubKq1Fw==", + "kuWGANwzNRpG4XmY7KjjNg==", + "i6r+mZfyhZyqlYv56o0H+w==", + "wqWqe0KRjZlUIrGgEOG9Mg==", + "t5wh9JGSkQO78QoQoEqvXA==", + "AGoVLd0QPcXnTedT5T95JQ==", + "aRrcmH+Ud3mF1vEXcpEm4w==", + "C65PZm8rZxJ6tTEb6d08Eg==", + "oAHVGBSJ2cf4dVnb/KEYmw==", + "BuDVDLl0OGdomEcr+73XhQ==", + "bLsStF0DDebpO+xulqGNtg==", + "xukOAM0QVsA72qEy0yku9A==", + "LpoayYsTO8WLFLCSh2kf2w==", + "LEVYAE54618FrlXkDN01Kw==", + "Jm862vBTCYbv/V4T1t46+Q==", + "X4kdXUuhcUqMSduqhfLpxA==", + "cLR0Ry4/N5swqga1R6QDMw==", + "0klouNfZRHFFpdHi4ZR2hA==", + "JGx8sTyvr4bLREIhSqpFkw==", + "ZiJ/kJ9GneF3TIEm08lfvQ==", + "hP7dSa8lLn9KTE/Z0s4GVQ==", + "600bwlyhcy754W1E6tuyYg==", + "U49SfOBeqQV9wzsNkboi8Q==", + "5DDb7fFJQEb3XTc3YyOTjg==", + "6uT7LZiWjLnnqnnSEW4e/Q==", + "tq5xUJt8GtjDIh1b48SthQ==", + "eJFIQh/TR7JriMzYiTw4Sg==", + "jdRzkUJrWxrqoyNH9paHfQ==", + "RKVDdE1AkILTFndYWi9wFg==", + "AEpTVUQhIEJGlXJB6rS26A==", + "PD+yHtJxZJ2XEvjIPIJHsQ==", + "dOS+mVCy3rFX9FvpkTxGXA==", + "lz+SeifYXxamOLs1FsFmSQ==", + "QTz21WkhpPjfK8YoBrpo+w==", + "9wUIeSgNN36SFxy8v2unVg==", + "ash1r2J6B0PUxJe8P0otVQ==", + "y7yS9x3yshVhMpDbQtfYOQ==", + "f07bdNVAe9x+cAMdF1bByQ==", + "N2KovXW14hN/6+iWa1Yv3g==", + "2DNbXVgesUa7PgYQ4zX5Lw==", + "WQznrwqvMhUlM3CzmbhAOQ==", + "FpWDTLTDmkUhH/Sgo+g1Gg==", + "OVHqwV8oQMC5KSMzd5VemA==", + "Bv4mNIC72KppYw/nHQxfpQ==", + "MI+HSMRh8KTW+Afiaxd/Fw==", + "10OltdxPXOvfatJuwPVKbQ==", + "y4/HohCJxtt+cT7nLJB08w==", + "RhcqXY4OsZlVVF7ZlkTeRw==", + "/mrqas0eDX+sFUNJvCQY8g==", + "ZIZx4MehWTVXPN9cVQBmyA==", + "z20AAnvj7WsfJeOu3vemlA==", + "dL6n/JsK+Iq6UTbQuo/GOw==", + "rMm9bHK69h0fcMkMdGgeeA==", + "ftsf2qztw3NC78ep/CZXWQ==", + "/n1RLTTVpygre1dl36PDwQ==", + "/FsJYFNe+7UvsSkiotNJEQ==", + "Yy2pPhITTmkEwoudXizHqQ==", + "lizovLQxu6L9sbafNQuShQ==", + "XV5MYe0Q7YMtoBD6/iMdSw==", + "5jHgQF4SfO/zy9xy9t+9dw==", + "16iT/jCcPDrJEfi2bE5F+Q==", + "syeBfQBUmkXNWCZ1GV8xSA==", + "sr3UXbMg5zzkRduFx/as7g==", + "xUXEE7OBBCudsQnuj5ycOA==", + "ojZY7Gi2QJXE/fp6Wy31iA==", + "RlNPyhgYOIn28R4vKCVtYA==", + "KOm8PTa+ICgDrgK9QxCJZw==", + "DJoy1NSZZw87oxWGlNHhfg==", + "jEdanvXKyZdZJG6mj/3FWw==", + "Omr+zPWVucPCSfkgOzLmSQ==", + "71w3aSvuh2mBLtdqJCN3wA==", + "xjTMO2mvtpvwQrounD4e8g==", + "Zz/5VMbw1TqwazReplvsEg==", + "hIjgi20+km+Ks23NJ4VQ6Q==", + "00TVKawojyqrJkC7YqT41Q==", + "YgVpC5d5V6K/BpOD663yQA==", + "wX70jKLKJApHnhyK0r6t3A==", + "lacCCRiWdquNm4YRO7FoKA==", + "cWdlhVZD7NWHUGte24tMjg==", + "t5U+VMsTtlWAAWSW+00SfQ==", + "AMfL0rH+g8c0VqOUSgNzQw==", + "0G93AxGPVwmr66ZOleM90A==", + "9tiibT8V9VwnPOErWGNT3w==", + "+dBv88reDrjEz6a2xX3Hzw==", + "xX6atcCApI08oVLjjLteLg==", + "+YrqTEJlJCv0A2RHQ8tr1A==", + "aqcOby9QyEbizPsgO3g0yw==", + "s/BZAhh1cTV3JCDUQsV8mA==", + "x9VwDdFPp/rJ+SF16ooWYg==", + "k/OVIllJvW6BefaLEPq7DA==", + "rIMXaCaozDvrdpvpWvyZOQ==", + "qQQwJ/aF87BbnLu3okXxaw==", + "TIWSM78m0RprwgPGK/e0JA==", + "r/b5px/UImGNjT/X5sYjuA==", + "7K8l6KoP0BH82/WMLntfrg==", + "gEHGeR2F82OgBeAlnYhRSw==", + "1/SGIab+NnizimUmNDC4wA==", + "WADmxH7R6B4LR+W6HqQQ6A==", + "pcoBh5ic7baSD4TZWb3BSw==", + "es/L9iW8wsyLeC5S4Q8t+g==", + "D175i+2bZ7aWa4quSSkQpA==", + "WQMffxULFKJ+bun6NrCURA==", + "82hTTe1Nr4N2g7zwgGjxkw==", + "oyYtf08AkWLR52bXm5+sKw==", + "8uP4HUnSodw88yoiWXOIcw==", + "x2NpqNnqRihktNzpxmepkQ==", + "x5zMDuW66467ofgL3spLUQ==", + "OMO4pqzfcbQ11YO4nkTXfg==", + "N4/mQFyhDpPzmihjFJJn6w==", + "NN/ymVQNa17JOTGr6ki3eQ==", + "htDbVu1xGhCRd8qoMlBoMg==", + "S47hklz3Ow+n5aY6+qsCoA==", + "ji+1YHlRvzevs3q5Uw1gfA==", + "3Y4w0nETru3SiSVUMcWXqw==", + "XfBOCJwi2dezYzLe316ivw==", + "kMUdiwM7WR8KGOucLK4Brw==", + "V/xG5QFyx1pihimKmAo8ZA==", + "sQskMBELEq86o1SJGQqfzg==", + "6+jhreeBLfw64tJ+Nhyipw==", + "8iYdEleTXGM+Wc85/7vU9w==", + "D7piVoB2NJlBxK5owyo4+g==", + "hDGa2yLwNvgBd/v6mxmQaQ==", + "WLsh3UF4WXdHwgnbKEwRlQ==", + "D5jaV+HtXkSpSxJPmaBDXg==", + "jCgdKXsBCgf7giUKnr6paQ==", + "XqW7UBTobbV4lt1yfh0LZw==", + "EbGG4X18upaiVQmPfwKytg==", + "dXDPnL1ggEoBqR13aaW9HA==", + "Vik8tGNxO0xfdV0pFmmFDw==", + "Swjn3YkWgj0uxbZ1Idtk+A==", + "JPxEncA4IkfBDvpjHsQzig==", + "F5FcNti7lUa9DyF2iEpBug==", + "HJYgUxFZ66fRT8Ka73RaUg==", + "Jbxl8Nw1vlHO9rtu0q/Fpg==", + "fmC+85h5WBuk8fDEUWPjtQ==", + "dZgMquvZmfLqP4EcFaWCiA==", + "XF/yncdoT4ruPeXCxEhl9Q==", + "QJEbr3+42P9yiAfrekKdRQ==", + "Sr9c0ReRpkDYGAiqSy683g==", + "Nr4zGo5VUrjXbI8Lr4YVWQ==", + "NDZWIhhixq7NT8baJUR4VQ==", + "GFRJoPcXlkKSvJRuBOAYHQ==", + "WHutPin+uUEqtrA7L8878A==", + "2rhjiY0O0Lo36wTHjmlNyw==", + "XsF7R12agx/KkRWl0TyXRA==", + "R6cO8GzYfOGTIi773jtkXw==", + "zrZWcqQsUE3ocWE0fG+SOA==", + "uNzpptKjihEfKRo5A1nWmw==", + "gICaI06E9scnisonpvqCsA==", + "TA9WjiLAFgJubLN4StPwLw==", + "sBpytpE38xz0zYeT+0qc2A==", + "Ej7W3+67kCIng3yulXGpRQ==", + "nR3ACzeVF5YcLX6Gj6AGyQ==", + "b0vZfEyuTja2JYMa20Rtbg==", + "f1h+Vp+xmdZsZIziHrB2+g==", + "WzjvUJ4jZAEK7sBqw+m07A==", + "OzMR5D2LriC5yrVd5hchnA==", + "cw1gBLtxH/m4H7dSM7yvFg==", + "CZbd+UoTz0Qu1kkCS3k8Xg==", + "WtT0QAERZSiIt2SFDiAizg==", + "QsquNcCZL9wv7oZFqm64vQ==", + "FXzaxi3nAXBc8WZfFElQeA==", + "Ml3mi1lGS1IspHp3dYYClg==", + "XGAXhUFjORwKmAq9gGEcRg==", + "wOhbpTzmFla8R0kI9OiHaA==", + "qoK2keBg3hdbn7Q24kkVXg==", + "ZAQHWU6RMg4IadOxuaukyw==", + "RiahBXX2JbPzt8baPiP/8g==", + "Qx6rVv9Xj8CBjqikWI9KFA==", + "ZRnR6i+5WKMRfs3BDRBCJg==", + "91LQuW6bMSxl10J/UDX23A==", + "0dIeIM5Zvm5nSVWLy94LWg==", + "Ja3ECL7ClwDrWMTdcSQ6Ug==", + "f6iLrMpxKhFxIlfRsFAuew==", + "iSeH0JFSGK73F470Rhtesw==", + "DwOTyyCoUfaSShHZx9u6xg==", + "rdeftHE7gwAT67wwhCmkYQ==", + "kUhyc3G8Zvx8+q5q5nVEhw==", + "W8bATujVUT80v2XGJTKXDg==", + "dMRx4Mf6LrN64tiJuyWmDw==", + "9cvHJmim9e0pOaoUEtiM6A==", + "RHToSGASrwEmvzjX6VPvNQ==", + "V7eji28JSg3vTi30BCS7gw==", + "4+htiqjEz9oq0YcI/ErBVg==", + "jKJn4czwUl/6wtZklcMsSg==", + "bvyB6OEwhwCIfJ6KRhjnRw==", + "59ipbMH7cKBsF9bNf4PLeQ==", + "M/cQja3uIk1im9++brbBOA==", + "AChOz8avRYsvxlbWcorQ3w==", + "FcKjlHKfQAGoovtpf+DxWQ==", + "y+cl1/Knb9MZPz8nBB0M+w==", + "b8BZV1NfBdLi70ir4vYvZg==", + "aFJuE/s+Kbge4ppn+wulkA==", + "CWBGcRFYwZ0va6115vV/oQ==", + "glnqaRfwm6NxivtB2nySzw==", + "mPk1IsU5DmDFA/Ym5+1ojw==", + "LGwcvetzQ3QqKjNh5vA8vw==", + "yctId8ltkl3+xqi9bj+RqA==", + "spJI3xFUlpCDqzg0XCxopA==", + "V8m51xgUgywRoV6BGKUrgg==", + "rgcXxjx3pDLotH7TTfAoZw==", + "/TSsi/AwKHtP6kQaeReI3w==", + "8dbyfox/isKLsnVjQNsEXg==", + "MOrAbuJTyGKPC6MgYJlx5Q==", + "uNWFZlP7DA96sf+LWiAhtQ==", + "hNHqznsrIVRSQdII6crkww==", + "GT6WUDXiheKAM7tPg3he9A==", + "JC8Q+8yOJ52NvtVeyHo68w==", + "HMQarkPWOUDIg5+5ja2dBQ==", + "nknBKPgb7US42v8A0fTl/w==", + "fDOUzPTU2ndpbH0vgkgrJQ==", + "GTNttXfMniNhrbhn92Aykg==", + "D2JcY4zWwqaCKebLM8lPiQ==", + "/c34NtdUZAHWIwGl3JM8Tw==", + "/G26n5Xoviqldr5sg/Jl3w==", + "GF0lY77rx1NQzAsZpFtXIQ==", + "BMOi5JmFUg5sCkbTTffXHw==", + "R+beucURp/H5jLs4kW6wmg==", + "xfYZ6qhWNBqqJ0PdWRjOwA==", + "Ahpi9+nl13kPTdzL+jgqMw==", + "oIU19xAvLJwQSZzIH577aA==", + "50xwiYvGQytEDyVgeeOnMg==", + "M0ESOGwJ4WZ4Ons1ljP0bQ==", + "fS471/rN4K2m10mUwGFuLg==", + "RrE3B3X/SJi3CqCUlTYwaw==", + "oDca3JEdRb4vONT9GUUsaQ==", + "pHo1O5zrCHCiLvopP2xaWw==", + "7sCJ4RxbxRqVnF4MBoKfuQ==", + "7R5rFaXCxM3moIUtoCfM2g==", + "4rrSL6N0wyucuxeRELfAmw==", + "9Gkw+hvsR/tFY1cO89topg==", + "aw4CzX8pYbPVMuNrGCEcWg==", + "KyLQxi5UP+qOiyZl0PoHNQ==", + "T1pMWdoNDpIsHF8nKuOn2A==", + "Qv6wWP4PpycDGxe7EZNSCw==", + "ZJc7GV0Yb6MrXkpDVIuc8g==", + "aXrbsro7KLV8s4I4NMi4Eg==", + "7k5rBuh8FbTTI4TP87wBPQ==", + "NRyFx6jqO/oo9ojvbYzsAg==", + "P7eMlOz9YUcJO+pJy0Kpkw==", + "jpjpNjL1IKzJdGqWujhxCw==", + "9k1u/5TgPmXrsx3/NsYUhg==", + "c1wbFbN7AdUERO/xVPJlgw==", + "Yw4ztKv6yqxK9U1L0noFXg==", + "GnJKlRzmgKN9vWyGfMq3aA==", + "91VcAVv7YDzkC1XtluPigw==", + "h1NNwMy0RjQmLloSw1hvdg==", + "pzC8Y0Vj9MPBy3YXR32z6w==", + "UTmTgvl+vGiCDQpLXyVgOg==", + "CzWhuxwYbNB/Ffj/uSCtbw==", + "VOB+9Bcfu8aHKGdNO0iMRw==", + "X2Tawm2Cra6H7WtXi1Z4Qw==", + "6cTETZ9iebhWl+4W5CB+YQ==", + "X4hrgqMIcApsjA9qOWBoCw==", + "1buQEv2YlH/ljTgH0uJEtw==", + "FH5Z60RXXUiDk+dSZBxD3g==", + "FI2WhaSMb3guFLe3e9il8Q==", + "O/EizzJSuFY8MpusBRn7Tg==", + "b6rrRA0W247O+FfvDHbVCQ==", + "ng1Q0A7ljho3TUWWYl46sw==", + "1Ym0lyBJ9aFjhJb/GdUPvQ==", + "+OXdvbTxHtSoLg7bZMho4w==", + "cuQslgfqD2VOMhAdnApHrA==", + "pCQmlnn3BxhsV2GwqjRhXg==", + "6PzjncEw2wHZg7SP7SQk9w==", + "nqtQI1bSM7DCO9P1jGV97Q==", + "O1ckWUwuhD44MswpaD6/rw==", + "RUmhye56tQu9xXs4SRJpOQ==", + "llujnWE17U8MIHmx4SbrSA==", + "UwqBVd4Wfias4ElOjk2BzQ==", + "kBAB2PSjXwqoQOXNrv80AA==", + "w1zN28mSrI/gqHsgs4ME3A==", + "301utVPZ93AnPLYbsiJggw==", + "qIFpKKwUmztsBpJgMaVvSg==", + "QmcURiMzmVeUNaYPSOtTTg==", + "x/MpsQvziUpW40nNUHDS5Q==", + "t1O9jSNjg4DTIv/Za4NbtA==", + "1B5gxGQSGzVKoNd5Ol4N7g==", + "81iQLU+YwxNwq4of6e9z7A==", + "x0eIHCvQLd2jdDaXwSWTYQ==", + "96ORaz1JRHY1Gk8H74+C2g==", + "bNDKcFu8T5Y6OoLSV+o/Sw==", + "WrJMOuXSLKKzgmIDALkyNw==", + "+gpHnUj2GWocP74t5XWz4w==", + "z5DveTu377UW8IHnsiUGZg==", + "irnD9K8bsT+up/JUrxPw6A==", + "ginkFyNVMwkZLE49AbfqfA==", + "2hEzujfG3mR5uQJXbvOPTQ==", + "E9yeifEZtpqlD0N3pomnGw==", + "OpC/sL320wl5anx6AVEL+A==", + "D7wN7b5u5PKkMaLJBP9Ksw==", + "83WGpQGWyt6mCV+emaomog==", + "X6ulLp4noBgefQTsbuIbYQ==", + "BH+rkZWQjTp7au6vtll/CQ==", + "Ex3x5HeDPhgO2S9jjCFy4g==", + "YNqIHCmBp/EbCgaPKJ7phw==", + "312g8iTB9oJgk/OqcgR7Cw==", + "LcF0OqPWrcpHby8RwXz1Yg==", + "gaEtlJtD6ZjF5Ftx0IFt0A==", + "bvbMJZMHScwjJALxEyGIyg==", + "StoXC7TBzyRViPzytAlzyQ==", + "XqFSbgvgZn0CpaZoZiRauQ==", + "AqHVaj3JcR44hnMzUPvVYg==", + "jTg9Y6EfpON4CRFOq0QovA==", + "q/siBRjx6wNu+OTvpFKDwA==", + "goSgZ8N5UbT5NMnW3PjIlQ==", + "9onh6QKp70glZk9cX3s34A==", + "o5XVEpdP4OXH0NEO4Yfc/A==", + "a5gZ5uuRrXEAjgaoh7PXAg==", + "PaROi5U16Tk35p0EKX5JpA==", + "dtnE401dC0zRWU0S/QOTAg==", + "7J3FoFGuTIW36q0PZkgBiw==", + "hiYg+aVzdBUDCG0CXz9kCw==", + "vhdFtKVH4bVatb4n8KzeXw==", + "DWKsPfKDAtfuwgmc2dKUNg==", + "M2JMnViESVHTZaru6LDM6w==", + "G/PA+kt0N+jXDVKjR/054A==", + "6rqK8sjLPJUIp7ohkEwfZg==", + "wajwXfWz2J+O+NVaj6j2UQ==", + "C4QEzQKGxyRi2rjwioHttA==", + "N/HgDydvaXuJvTCBhG/KtA==", + "6erpZS36qZRXeZ9RN9L+kw==", + "bbBsi6tXMVWyq3SDVTIXUg==", + "aySnrShOW4/xRSzl/dtSKQ==", + "rxfACPLtKXbYua18l3WlUw==", + "L4+C6I7ausPl6JbIbmozAg==", + "R3ijnutzvK6IKV3AKHQZSA==", + "leDlMcM+B1mDE8k5SWtUeg==", + "KGI/cXVz6v6CfL8H6akcUQ==", + "NtwqUO3SKZE/9MXLbTJo/g==", + "dJHKDkfMFJeoULg7U4wwDQ==", + "IEz72W2/W8xBx5aCobUFOQ==", + "wUYhs4j3W9nIywu1HIv2JA==", + "GzbeM7snhe+M+J7X+gAsQw==", + "3/1puZTGSrD9qNKPGaUZww==", + "eKQCVzLuzoCLcB4im8147A==", + "CCK+6Dr72G3WlNCzV7nmqw==", + "CJoZn5wdTXbhrWO5LkiW0g==", + "bJ1cZW7KsXmoLw0BcoppJg==", + "OlpA9HsF8MBh7b45WZSSlg==", + "JZRjdJLgZ+S0ieWVDj8IJg==", + "uhT12XY79CtbwhcSfAmAXQ==", + "isep9d+Q7DEUf0W7CJJYzw==", + "K9A87aMlJC8XB9LuFM913g==", + "uqe3rFveJ2JIkcZQ3ZMXHQ==", + "0e8hM3E5tnABRyy29A8yFw==", + "4iiCq+HhC+hPMldNQMt0NA==", + "X4o0OkTz0ec70mzgwRfltA==", + "1E3pMgAHOnHx3ALdNoHr8Q==", + "xNilc7UOu1kyP0+nK5MrLw==", + "DQlZWBgdTCoYB1tJrNS5YQ==", + "iruDC5MeywV4yA8o1tw/KQ==", + "z+1oDVy8GJ5u/UDF+bIQdA==", + "uExgqZkkJnZj252l5dKAGg==", + "ZgdpqFrVGiaHkh9o3rDszg==", + "5N2oi2pB69NxeNt08yPLhw==", + "G37U8XTFyshfCs7qzFxATg==", + "0ZEC3hy411LkOhKblvTcqg==", + "ITZ3P47ALS0JguFms6/cDA==", + "WWN44lbUnEdHmxSfMCZc6w==", + "r2f2MyT+ww1g9uEBzdYI1w==", + "ZvvxwDd0I6MsYd7aobjLUA==", + "uQs79rbD/wEakMUxqMI48A==", + "022B0oiRMx8Xb4Af98mTvQ==", + "afMd/Hr3rYz/l7a3CfdDjg==", + "xmsYnsJq78/f9xuKuQ2pBQ==", + "dFetwmFw+D6bPMAZodUMZQ==", + "TBQpcKq2huNC5OmI2wzRQw==", + "skrQRB9xbOsiSA19YgAdIQ==", + "anyANMnNkUqr3JuPJz5Qzw==", + "6QUGE2S8oFYx4T4nW56cCw==", + "rwtF86ZAbWyKI6kLn4+KBw==", + "6txm8z4/LGCH0cpaet/Hsg==", + "wdRyYjaM11VmqkkxV/5bsA==", + "+k5lDb+QdNc9iZ01hL5yBg==", + "k/pBSWE2BvUsvJhA9Zl5uw==", + "jQjyjWCEo9nWFjP4O8lehw==", + "R6Me6sSGP5xpNI8R0xGOWw==", + "9+hjTVMQUsvVKs7Tmp52tg==", + "VQIpquUqmeyt/q6OgxzduQ==", + "KXvdjZ3rRKn60djPTCENGA==", + "5HovoyHtul8lXh+z8ywq9A==", + "1+XWdu4qCqLLVjqkKz3nmA==", + "LCj4hI520tA685Sscq6uLw==", + "b53qqLnrTBthRXmmnuXWvw==", + "WTr3q/gDkmB4Zyj7Ly20+w==", + "FbxScyuRacAQkdQ034ShTA==", + "qaTdVEeZ6S8NMOxfm+wOMA==", + "ZNrjP1fLdQpGykFXoLBNPw==", + "/Bwpt5fllzDHq2Ul6v86fA==", + "/mFp3GFkGNLhx2CiDvJv4A==", + "RppDe/WGt1Ed6Vqg1+cCkQ==", + "6M6QapJ5xtMXfiD3bMaiLA==", + "Ghuj9hAyfehmYgebBktfgA==", + "GncGQgmWpI/fZyb/6zaFCg==", + "R1TCCfgltnXBvt5AiUnCtQ==", + "5NEP7Xt7ynj6xCzWzt21hQ==", + "4yEkKp2FYZ09mAhw2IcrrA==", + "y2Tn2gmhKs5WKc01ce74rg==", + "wnfYUctNK+UPwefX5y4/Rw==", + "BV1moliPL15M14xkL+H1zw==", + "80C9TB9/XT1gGFfQDJxRoA==", + "yL1DwlIIREPuyuCFULi0uw==", + "D09afzGpwCEH0EgZUSmIZA==", + "eCy/T+a8kXggn1L8SQwgvA==", + "+dIEf5FBrHpkjmwUmGS6eg==", + "kzXsrxWRnWhkA82LsLRYog==", + "Nf9fbRHm844KZ2sqUjNgkA==", + "XAq/C+XyR6m3uzzLlMWO5Q==", + "jiV+b/1EFMnHG6J0hHpzBg==", + "HK0yf7F97bkf1VYCrEFoWA==", + "Cz1G77hsDtAjpe0WzEgQog==", + "xdCCdP8SNBOK3IsX6PiPQA==", + "8snljTGo/uICl9q0Hxy7/A==", + "sLdxIKap0ZfC3GpUk3gjog==", + "IA1jmtfpYkz/E2wD0+27WA==", + "PPa7BDMpRdxJdBxkuWCxKA==", + "CuGIxWhRLN7AalafBZLCKQ==", + "MWcV03ULc0vSt/pFPYPvFA==", + "QVwuN66yPajcjiRnVk/V8g==", + "aLY2pCT0WfFO5EJyinLpPg==", + "dGrf9SWJ13+eWS6BtmKCNw==", + "YtZ8CYfnIpMd2FFA5fJ+1Q==", + "Umd+5fTcxa3mzRFDL9Z8Ww==", + "Al8+d/dlOA5BXsUc5GL8Tg==", + "/KYZdUWrkfxSsIrp46xxow==", + "kr8tw1+3NxoPExnAtTmfxg==", + "PwvPBc+4L73xK22S9kTrdA==", + "VWNDBOtjiiI4uVNntOlu/A==", + "lJFPmPWcDzDp5B2S8Ad8AA==", + "Mofqu40zMRrlcGRLS42eBw==", + "BuENxPg7JNrWXcCxBltOPg==", + "nmD7fEU4u7/4+W/pkC4/0Q==", + "axEl7xXt/bwlvxKhI7hx4g==", + "W04GeDh+Tk/I1S85KlozRA==", + "tVw8U1AsslIFmQs4H1xshg==", + "TSPFvkgw6uLsJh66Ou0H9w==", + "IYIbEaErHoFBn8sTT9ICIQ==", + "WBu0gJmmjVdVbjDmQOkU6w==", + "ZgjifTVKmxOieco81gnccQ==", + "ZrCnZB/U/vcqEtI1cSvnww==", + "2D6yhuABiaFFoXz0Lh0C+w==", + "SfwnYZCKP1iUJyU1yq4eKg==", + "tsiqwelcBAMU/HpLGBtMGw==", + "S9L29U2P5K8wNW+sWbiH7w==", + "sGLPmr568+SalaQr8SE/PA==", + "Hm6MG6BXbAGURVJKWRM6ZA==", + "euxzbIq4vfGYoY3s1QmLcw==", + "/FchS2nPezycB8Bcqc2dbg==", + "ZKvox7BaQg4/p5jIX69Umw==", + "HkbdaMuDTPBDnt3wAn5RpQ==", + "eddhS+FkXxiUnbPoCd5JJw==", + "Muf2Eafcf9G3U2ZvQ9OgtQ==", + "a7Pv1SOWYnkhIUC22dhdDA==", + "O839JUrR+JS30/nOp428QA==", + "2qK2ZEY9LgdKSTaLf6VnLA==", + "BTiGLT6XdZIpFBc91IJY6g==", + "EqYq2aVOrdX5r7hBqUJP7g==", + "SIuKH/Qediq0TyvqUF93HQ==", + "c5ymZKqx/td1MiS2ERiz9A==", + "rqucO37p86LpzehR/asCSQ==", + "1tpM0qgdo7JDFwvT0TD78g==", + "Ar1Eb/f/LtuIjXnnVPYQlA==", + "V8q+xz4ljszLZMrOMOngug==", + "P5WPQc5NOaK7WQiRtFabkw==", + "Xo8ZjXOIoXlBjFCGdlPuZw==", + "jTmPbq+wh30+yJ/dRXk1cA==", + "KSumhnbKxMXQDkZIpDSWmQ==", + "Kh/J1NpDBGoyDU+Mrnnxkg==", + "3BjLFon1Il0SsjxHE2A1LQ==", + "dml2gqLPsKpbIZ93zTXwCQ==", + "ZyoaR1cMiKAsElmYZqKjLA==", + "vnOJ3e9Zd4wPx8PX7QgZzQ==", + "2melaInV0wnhBpiI3da6/A==", + "mUek9NkXm8HiVhQ6YXiyzA==", + "RZTpYKxOAH9JgF1QFGN+hw==", + "a/Y6IAVFv0ykRs9WD+ming==", + "yhRi5M9Etuu9HSu4d24i3w==", + "+1gcqAqaRZwCj5BGiZp3CA==", + "o1zeXHJEKevURAAbUE/Vog==", + "cvOg7N4DmTM+ok1NBLyBiQ==", + "uPdjKJIGzN7pbGZDZdCGaA==", + "REnDNe9mGfqVGZt+GdsmjQ==", + "XqTK/2QuGWj50tGmiDxysA==", + "bL2FuwsPT7a7oserJQnPcw==", + "uO+uK1DntCxVRr1KttfUIw==", + "Xconi1dtldH90Wou9swggw==", + "HRF3WL/ue3/QlYyu7NUTrA==", + "5LuFDNKzMd2BzpWEIYO2Ww==", + "dNTU+/2DdZyGGTdc+3KMhQ==", + "H+NHjk/GJDh/GaNzMQSzjg==", + "/Ph/6l/lFNVqxAje1+PgFA==", + "4WRdAjiUmOQg2MahsunjAg==", + "j+lDhAnWAyso+1N8cm85hQ==", + "nFBXCPeiwxK9mLXPScXzTA==", + "vGKknndb4j6VTV8DxeT4fQ==", + "fdqt93OrpG13KAJ5cASvkg==", + "1MIn73MLroxXirrb+vyg2Q==", + "Q7teXmTHAC5qBy+t7ugf0w==", + "bWwtTFlhO3xEh/pdw0uWaQ==", + "Omi2ZB9kdR1HrVP2nueQkA==", + "+ZozWaPWw8ws1cE5DJACeg==", + "3FH4D31nKV13sC9RpRZFIg==", + "4kXlJNuT79XXf1HuuFOlHw==", + "36XDmX6j542q+Oei1/x0gw==", + "MqqDg9Iyt4k3vYVW5F+LDw==", + "cvrGmub2LoJ+FaM5HTPt9A==", + "uC2lzm7HaMAoczJO6Z/IhQ==", + "MnStiFQAr3QlaRZ02SYGaQ==", + "ZuayB6IpbeITokKGVi9R5w==", + "FtxpWdhEmC6MT61qQv4DGA==", + "KujFdhhgB9q4oJfjYMSsLg==", + "ZV8mEgJweIYk0/l0BFKetA==", + "gDLjxT7vm07arF4SRX5/Vg==", + "/MEOgAhwb7F0nBnV4tIRZA==", + "k2KP9oPMnHmFlZO6u6tgyw==", + "fbTm027Ms0/tEzbGnKZMDA==", + "HOi+vsGAae4vhr+lJ5ATnQ==", + "9Bet5waJF5/ZvsYaHUVEjQ==", + "Wd0dOs7eIMqW5wnILTQBtg==", + "z/e5M2lE9qh3bzB97jZCKA==", + "b16O4LF7sVqB7aLU2f3F1A==", + "lsBTMnse2BgPS6wvPbe7JA==", + "0nOg18ZJ/NicqVUz5Jr0Hg==", + "MFeXfNZy6Q9wBfZmPQy3xg==", + "ksOFI9C7IrDNk4OP6SpPgw==", + "NquRbPn8fFQhBrUCQeRRoQ==", + "ccmy4GVuX967KaQyycmO0w==", + "DY0IolKTYlW+jbKLPAlYjQ==", + "aJFbBhYtMbTyMFBFIz/dTA==", + "9pdeedz1UZUlv8jPfPeZ1g==", + "qZ2q5j2gH3O56xqxkNhlIA==", + "N7fHwb397tuQHtBz1P80ZQ==", + "uOkMpYy/7DYYoethJdixfQ==", + "E9ajQQMe02gyUiW3YLjO/A==", + "dFSavcNwGd8OaLUdWq3sng==", + "TAD0Lk95CD86vbwrcRogaQ==", + "jLI3XpVfjJ6IzrwOc4g9Pw==", + "CzP13PM/mNpJcJg8JD3s6w==", + "GSWncBq4nwomZCBoxCULww==", + "9k17UqdR1HzlF7OBAjpREA==", + "TrWS+reCJ0vbrDNT5HDR9w==", + "CXMKIdGvm60bgfsNc+Imvg==", + "6NP81geiL14BeQW6TpLnUA==", + "hW9DJA1YCxHmVUAF7rhSmQ==", + "8M0kSvjn5KN8bjsMdUqKZQ==", + "eS/vTdSlMUnpmnl1PbHjyw==", + "h2B0ty0GobQhDnFqmKOpKQ==", + "n7KL1Kv027TSxBVwzt9qeA==", + "yYmnM/WOgi+48Rw7foGyXA==", + "FhthAO5IkMyW4dFwpFS7RA==", + "81ZH3SO0NrOO+xoR/Ngw1g==", + "t7HaNlXL16fVwjgSXmeOAQ==", + "N+K1ibXAOyMWdfYctNDSZQ==", + "yQCLV9IoPyXEOaj3IdFMWw==", + "3+zsjCi7TnJhti//YXK35w==", + "600mjiWke4u0CDaSQKLOOg==", + "K4VS+DDkTdBblG93l2eNkA==", + "5KOgetfZR+O2wHQSKt41BQ==", + "kj5WqpRCjWAfjM7ULMcuPQ==", + "AxEjImKz4tMFieSo7m60Sg==", + "jp5Em/0Ml4Txr1ptTUQjpg==", + "jQVlDU+HjZ2OHSDBidxX5A==", + "4NHQwbb3zWq2klqbT/pG6g==", + "PeJS+mXnAA6jQ0WxybRQ8w==", + "l6Ssc04/CnsqUua9ELu2iQ==", + "nFPDZGZowr3XXLmDVpo7hg==", + "yYBIS9PZbKo7Gram7IXWPA==", + "/HU2+fBqfWTEuqINc0UZSA==", + "adT+OjEB2kqpeYi4kQ6FPg==", + "GW1Uaq622QamiiF24QUA0g==", + "rTwJggSxTbwIYdp07ly0LA==", + "4yrFNgqWq17zVCyffULocA==", + "vvh9vAIrXjIwLVkuJb5oDQ==", + "C7UaoIEXsVRxjeA0u99Qmw==", + "x1A74vg/hwwjAx6GrkU8zw==", + "7XRiYvytcwscemlxd9iXIQ==", + "64AA4jLHXc1Dp15aMaGVcA==", + "u/QxrP1NOM/bOJlJlsi/jQ==", + "5M3dFrAOemzQ0MAbA8bI5w==", + "wyqmQGB6vgRVrYtmB2vB7w==", + "8vLA9MOdmLTo3Qg+/2GzLA==", + "/u5W2Gab4GgCMIc4KTp2mg==", + "lhAOM81Ej6YZYBu45pQYgg==", + "MArbGuIAGnw4+fw6mZIxaw==", + "ZZImGypBWwYOAW43xDRWCQ==", + "L2IeUnATZHqOPcrnW2APbA==", + "bQKkL+/KUCsAXlwwIH0N3w==", + "f09F7+1LRolRL5nZTcfKGA==", + "hPnPQOhz4QKhZi02KD6C+A==", + "78b8sDBp28zUlYPV5UTnYw==", + "iVDd2Zk7vwmEh97LkOONpQ==", + "LHQETSI5zsejvDaPpsO29g==", + "Yjm5tSq1ejZn3aWqqysNvA==", + "gkrg0NR0iCaL7edq0vtewA==", + "Lo1xTCEWSxVuIGEbBEkVxA==", + "8GyPup4QAiolFJ9v80/Nkw==", + "3L3KEBHhgDwH615w4OvgZA==", + "hJSP7CostefBkJrwVEjKHA==", + "9oQ/SVNJ4Ye9lq8AaguGAQ==", + "n7Bns42aTungqxKkRfQ5OQ==", + "K5lhaAIZkGeP5rH2ebSJFw==", + "ZaPsR9X77SNt7dLjMJUh8A==", + "18ndtDM9UaNfBR1cr3SHdA==", + "0QbH4oI8IjZ9BRcqRyvvDQ==", + "J/eAtAPswMELIj8K2ai+Xg==", + "qenHZKKlTUiEFv6goKM/Mw==", + "vjrSYGUpeKOtJ2cNgLFg2g==", + "DA+3fjr7mgpwf6BZcExj0w==", + "rh7bzsTQ1UZjG7amysr0Gg==", + "tFMJRXfWE9g78O1uBUxeqQ==", + "e/nWuo5YalCAFKsoJmFyFA==", + "gqehq46BhFX2YLknuMv02w==", + "Uudn69Kcv2CGz2FbfJSSEA==", + "Otz/PgYOEZ1CQDW54FWJIQ==", + "IwfeA6d0cT4nDTCCRhK+pA==", + "jgNijyoj2JrQNSlUv4gk4A==", + "KzWdWPP2gH0DoMYV4ndJRg==", + "pv/m2mA/RJiEQu2Qyfv9RA==", + "ATmMzriwGLl+M3ppkfcZNA==", + "tVvWdA+JqH0HR2OlNVRoag==", + "n6QVaozMGniCO0PCwGQZ6w==", + "gU3gu8Y5CYVPqHrZmLYHbQ==", + "cBBOQn7ZjxDku0CUrxq2ng==", + "w+jzM0I5DRzoUiLS/9QIMQ==", + "MLlVniZ08FHAS5xe+ZKRaA==", + "wMyJLQJdmrC2TSeFkIuSvQ==", + "dG98w8MynOoX7aWmkvt+jg==", + "zm+z+OOyHhljV2TjA3U9zw==", + "Tk5MAqd1gyHpkYi8ErlbWg==", + "g6zSo8BvLuKqdmBFM1ejLA==", + "d0VAZLbLcDUgLgIfT1GmVQ==", + "SNPYH4r/J9vpciGN2ybP5Q==", + "XA2hUgq3GVPpxtRYiqnclg==", + "fVCRaPsTCKEVLkoF4y3zEw==", + "FpgdsQ2OG+bVEy3AeuLXFQ==", + "JquDByOmaQEpFb47ZJ4+JA==", + "e369ZIQjxMZJtopA//G55Q==", + "Nsd+DfRX6L54xs+iWeMjCQ==", + "+/UCpAhZhz368iGioEO8aQ==", + "e5l9ZiNWXglpw6nVCtO8JQ==", + "Cl1u5nGyXaoGyDmNdt38Bw==", + "6sNP0rzCCm3w976I2q2s/w==", + "qcpeZWUlPllQYZU6mHVwUw==", + "kzYddqiMsY3EYrpxve2/CQ==", + "3iC21ByW/YVL+pSyppanWw==", + "3HPOzIZxoaQAmWRy9OkoSg==", + "xsCZVhCk2qJmOqvUjK3Y8Q==", + "i2sSvrTh/RdLJX0uKhbrew==", + "7Y87wVJok20UfuwkGbXxLg==", + "ibsb1ncaLZXAYgGkMO7tjQ==", + "+VfRcTBQ80KSeJRdg0cDfw==", + "kgKWQJJQKLUuD2VYKIKvxA==", + "ARKIvf4+zRF8eCvUITWPng==", + "1fztTtQWNMIMSAc5Hr6jMQ==", + "md6zNd7ZBn3qArYqQz7/fw==", + "kvAaIJb+aRAfKK104dxFAA==", + "UIXytIHyVODxlrg+eQoARA==", + "Dk0L/lQizPEb3Qud6VHb1Q==", + "64YsV2qeDxk2Q6WK/h7OqA==", + "90dtIMq0ozJXezT2r79vMQ==", + "wy/Z8505o4sVovk4UuBp1A==", + "ytDXLDBqWiU1w3sTurYmaw==", + "9pk75mBzhmcdT+koHvgDlw==", + "DQeib845UqBMEl96sqsaSg==", + "UPYR575ASaBSZIR3aX1IgQ==", + "swsVVsPi/5aPFBGP+jmPIw==", + "1cj1Fpd3+UiBAOahEhsluA==", + "ifuJCv9ZA84Vz1FYAPsyEA==", + "uu+ncs63SdQIvG6z4r7Q3Q==", + "UvC1WADanMrhT+gPp/yVqA==", + "llOvGOUDVfX68jKnAlvVRA==", + "SusSOsWNoAerAIMBVWHtfA==", + "VznvTPAAwAev+yhl9oZT0w==", + "luR/kvHLwA6tSdLeTM4TzA==", + "PcdBtV8pfKU0YbDpsjPgwg==", + "5l6kDfjtZjkTZPJvNNOVFw==", + "4FBBtWPvqJ3dv4w25tRHiQ==", + "JJbzQ/trOeqQomsKXKwUpQ==", + "0bj069wXgEJbw7dpiPr8Tg==", + "tejpAZp7y32SO2+o4OGvwQ==", + "kq26VyDyJTH/eM6QvS2cMw==", + "+zBkeHF4P8vLzk1iO1Zn3Q==", + "BzkNYH03gF/mQY71RwO3VA==", + "RnxOYPSQdHS6fw4KkDJtrA==", + "65KhGKUBFQubRRIEdh9SwQ==", + "k1DPiH6NkOFXP/r3N12GyA==", + "DqzWt1gfyu/e7RQl5zWnuQ==", + "gnez1VrH+UHT8C/SB9qGdA==", + "vZtL0yWpSIA+9v8i23bZSg==", + "FNvQqYoe0s/SogpAB7Hr1Q==", + "6nwR+e9Qw0qp8qIwH9S/Mg==", + "BPT4PQxeQcsZsUQl33VGmg==", + "rOYeIcB+Rg5V6JG2k4zS2w==", + "Je1UESovkBa9T6wS0hevLw==", + "HFHMGgfOeO0UPrray1G+Zw==", + "NBmB/cQfS+ipERd7j9+oVg==", + "iIm8c9uDotr87Aij+4vnMw==", + "S3VQa6DH+BdlSrxT/g6B5g==", + "BwRA+tMtwEvth28IwpZx+w==", + "vg3jozLXEmAnmJwdfcEN0g==", + "gW0oKhtQQ7BxozxUWw5XvQ==", + "Q6vGRQiNwoyz7bDETGvi5g==", + "Ak3rlzEOds6ykivfg39xmw==", + "G4qzBI1sFP2faN+tlRL/Bw==", + "ND9l4JWcncRaSLATsq0LVw==", + "yQmNZnp/JZywbBiZs3gecA==", + "ZoNSxARrRiKZF5Wvpg7bew==", + "GhpJfRSWZigLg/azTssyVA==", + "QyyiJ5I/OZC50o89fa5EmQ==", + "4kj0S8XlmhHXoUP7dQItUw==", + "Dt8Q5ORzTmpPR2Wdk0k+Aw==", + "/hFhjFGJx2wRfz6hyrIpvA==", + "eFimq+LuHi42byKnBeqnZQ==", + "JrKGKAKdjfAaYeQH8Y2ZRQ==", + "JFFeXsFsMA59iNtZey7LAA==", + "91SdBFJEZ65M+ixGaprY/A==", + "+S+WXgVDSU1oGmCzGwuT3g==", + "1X14kHeKwGmLeYqpe60XEA==", + "4xojeUxTFmMLGm6jiMYh/Q==", + "+1e7jvUo8f2/2l0TFrQqfA==", + "8WU1vLKV1GhrL7oS9PpABg==", + "DYWCPUq/hpjr6puBE7KBHg==", + "birqO8GOwGEI97zYaHyAuw==", + "6e8boFcyc8iF0/tHVje4eQ==", + "FLvED9nB9FEl9LqPn7OOrA==", + "ji306HRiq965zb8EZD2uig==", + "AklOdt9/2//3ylUhWebHRw==", + "VGRCSrgGTkBNb8sve0fYnQ==", + "oqlkgrYe9aCOwHXddxuyag==", + "KXuFON8tMBizNkCC48ICLA==", + "9aKH1u5+4lgYhhLztQ4KWA==", + "3hVslsq98QCDIiO40JNOuA==", + "OOS6wQCJsXH8CsWEidB35A==", + "YXHQ3JI9+oca8pc/jMH6mA==", + "V9vkAanK+Pkc4FGAokJsTA==", + "OFLn4wun6lq484I7f6yEwg==", + "3WVBP9fyAiBPZAq3DpMwOQ==", + "5gGoDPTc/sOIDLngmlEq4A==", + "E2lvMXqHdTw0x+KCKVnblg==", + "f1Gs++Iilgq9GHukcnBG3w==", + "uIkVijg7RPi/1j7c18G1qA==", + "9T7gB0ZkdWB0VpbKIXiujQ==", + "KCJJfgLe00+tjSfP6EBcUg==", + "WbAdlac/PhYUq7J2+n5f+w==", + "GLnS9wDCje7TOMvBX9jJVA==", + "VAg/aU5nl72O+cdNuPRO4g==", + "kzTl7WH/JXsX1fqgnuTOgw==", + "1HDgfU7xU7LWO/BXsODZAQ==", + "D0W5F7gKMljoG5rlue1jrg==", + "9reBKZ1Rp6xcdH1pFQacjw==", + "SSKhl2L3Mvy93DcZulADtA==", + "hlu7os0KtAkpBTBV6D2jyQ==", + "sfte/o9vVNyida/yLvqADA==", + "gYGQBLo5TdMyXks0LsZhsQ==", + "dNq2InSVDGnYXjkxPNPRxA==", + "fiv0DJivQeqUkrzDNlluRw==", + "msstzxq++XO0AqNTmA7Bmg==", + "DCjgaGV5hgSVtFY5tcwkuA==", + "aMmrAzoRWLOMPHhBuxczKg==", + "qNOSm15bdkIDSc/iUr+UTQ==", + "2nSTEYzLK77h5Rgyti+ULQ==", + "BhKO1s1O693Fjy1LItR/Jw==", + "kRnBEH6ILR5GNSmjHYOclw==", + "R97chlspND/sE9/HMScXjQ==", + "1Oykse0jQVbuR3MvW5ot4A==", + "Dmyb+a7/QFsU4d2cVQsxDw==", + "W5now3RWSzzMDAxsHSl++Q==", + "IrDuBrVu1HWm0BthAHyOLQ==", + "V6zyoX6MERIybGhhULnZiw==", + "ZQSDYgpsimK+lYGdXBWE/w==", + "lV70RNlE++04G1KFB3BMXA==", + "QmSBVvdk0tqH9RAicXq2zA==", + "qNyy6Fc0b8oOMWqqaliZ/w==", + "xvipmmwKdYt4eoKvvRnjEg==", + "Q7Df6zGwvb4rC+EtIKfaSw==", + "n1M2dgFPpmaICP+JwxHUug==", + "1k8tL2xmGFVYMgKUcmDcEw==", + "fFvXa1dbMoOOoWZdHxPGjw==", + "UP9mmAKzeQqGhod7NCqzhg==", + "PMCWKgog/G+GFZcIruSONw==", + "dnvatwSEcl73ROwcZ4bbIQ==", + "hY82j+sUQQRpCi6CCGea5A==", + "QoUC9nyK1BAzoUVnBLV2zw==", + "+aF4ilbjQbLpAuFXQEYMWQ==", + "XTCcsVfEvqxnjc0K5PLcyw==", + "ML7ipnY/g8mA1PUIju1j8Q==", + "tOkYq1BZY152/7IJ6ZYKUg==", + "2bsIpvnGcFhTCSrK9EW1FQ==", + "Af9j1naGtnZf0u1LyYmK1w==", + "ZmblZauRqO5tGysY3/0kDw==", + "PF0lpolQQXlpc3qTLMBk8w==", + "emVLJVzha7ui5OFHPJzeRQ==", + "gR0sgItXIH8hE4FVs9Q07w==", + "PTW+fhZq/ErxHqpM0DZwHQ==", + "g0kHTNRI7x/lAsr92EEppw==", + "24H9q+E8pgCEdFS7JO5kzQ==", + "HtDXgMuF8PJ1haWk88S0Ew==", + "pulldyBt2sw6QDvTrCh6zw==", + "ehwc2vvwNUAI7MxU4MWQZw==", + "enj9VEzLbmeOyYugTmdGfQ==", + "auvG6kWMnhCMi7c7e9eHrw==", + "R36O31Pj8jn0AWSuqI7X2Q==", + "3AVYtcIv7A5mVbVnQMaCeA==", + "T9WoUJNwp8h4Yydixbx6nA==", + "t0WN8TwMLgi8UVEImoFXKg==", + "mS99D+CXhwyfVt8xJ+dJZA==", + "AFdelaqvxRj6T3YdLgCFyg==", + "Lu02ic/E94s42A14m7NGCA==", + "7w3b73nN/fIBvuLuGZDCYQ==", + "O209ftgvu0vSr0UZywRFXA==", + "MQvAr+OOfnYnr/Il/2Ubkg==", + "e5txnNRcGs2a9+mBFcF1Qg==", + "YA0kMTJ82PYuLA4pkn4rfw==", + "QIKjir/ppRyS63BwUcHWmw==", + "P3y5MoXrkRTSLhCdLlnc4A==", + "WY7mCUGvpXrC8gkBB46euw==", + "g0GbRp2hFVIdc7ct7Ky7ag==", + "Cv079ZF55RnbsDT27MOQIA==", + "cvMJ714elj/HUh89a9lzOQ==", + "9inw7xzbqAnZDKOl/MfCqA==", + "F58ktE4O0f7C9HdsXYm+lw==", + "CsPkyTZADMnKcgSuNu1qxg==", + "mAzsVkijuqihhmhNTTz65g==", + "FxnbKnuDct4OWcnFMT/a5w==", + "P5wS+xB8srW4a5KDp/JVkA==", + "ctJYJegZhG42i+vnPFWAWw==", + "OrqJKjRndcZ8OjE3cSQv7g==", + "aXqiibI6BpW3qilV6izHaQ==", + "BA18GEAOOyVXO2yZt2U35w==", + "saEpnDGBSZWqeXSJm34eOA==", + "CUEueo8QXRxkfVdfNIk/gg==", + "H0UMAUfHFQH92A2AXRCBKA==", + "CT9g8mKsIN/VeHLSTFJcNQ==", + "E4NtzxQruLcetC23zKVIng==", + "203EqmJI9Q4tWxTJaBdSzA==", + "Do3aqbRKtmlQI2fXtSZfxQ==", + "JaYQXntiyznQzrTlEeZMIw==", + "VK95g27ws2C6J2h/7rC2qA==", + "CQ0PPwgdG3N6Ohfwx1C8xA==", + "/MeHciFhvFzQsCIw39xIZA==", + "u5cUPxM6/spLIV8VidPrAA==", + "OwArFF1hpdBupCkanpwT+Q==", + "PdBgXFq5mBqNxgCiqaRnkw==", + "lC5EumoIcctvxYqwELqIqw==", + "xoPSM86Se+1hHX0y3hhdkw==", + "F5bs0GGWBx9eBwcJJpXbqg==", + "1mw6LfTiirFyfjejf8QNGA==", + "daBhAvmE9shDgmciDAC5eg==", + "AvdeYb9XNOUFWiiz+XGfng==", + "JJJkp1TpuDx5wrua2Wml7g==", + "3y5Xk65ShGvWFbQxcZaQAQ==", + "l6QHU5JsJExNoOnqxBPVbw==", + "X2YfnPXgF2VHVX95ZcBaxQ==", + "g6udffWh7qUnSIo1Ldn3eA==", + "V2P75JFB4Se9h7TCUMfeNA==", + "IUZ5aGpkJ9rLgSg6oAmMlw==", + "pyrUqiZ98gVXxlXQNXv5fA==", + "83ERX2XJV3ST4XwvN7YWCg==", + "eJDUejE/Ez/7kV+S74PDYg==", + "M9oqlPb63e0kZE0zWOm+JQ==", + "0rTYcuVYdilO7zEfKrxY3A==", + "rfPTskbnoh3hRJH6ZAzQRg==", + "QtD35QhE8sAccPrDnhtQmQ==", + "jpNUgFnanr9Sxvj2xbBXZw==", + "nykEOLL/o7h0cs0yvdeT2g==", + "wX2URK6eDDHeEOF3cgPgHA==", + "jqPQ0aOuvOJte/ghI1RVng==", + "nHTsDl0xeQPC5zNRnoa0Rw==", + "mNv2Q67zePjk/jbQuvkAFA==", + "HjlPM2FQWdILUXHalIhQ5w==", + "cHkOsVd80Rgwepeweq4S1g==", + "kTCHqcb3Cos51o8cL+MXcg==", + "nvmBgp0YlUrdZ05INsEE8Q==", + "kFrRjz7Cf2KvLtz9X6oD+w==", + "Tmx0suRHzlUK4FdBivwOwA==", + "bG+P+p34t/IJ1ubRiWg6IA==", + "uESeJe/nYrHCq4RQbrNpGA==", + "ehfPlu6YctzzpQmFiQDxGA==", + "ZH5Es/4lJ+D5KEkF1BVSGg==", + "HHxn4iIQ7m0tF1rSd+BZBg==", + "DQJRsUwO1fOuGlkgJavcwQ==", + "HITIVoFoWNg04NExe13dNA==", + "MeKXnEfxeuQu9t3r/qWvcw==", + "Y7OofF9eUvp7qlpgdrzvkg==", + "XSb71ae0v+yDxNF5HJXGbQ==", + "p8W1LgFuW6JSOKjHkx3+aA==", + "y2JOIoIiT9cV1VxplZPraQ==", + "MN94B0r5CNAF9sl3Kccdbw==", + "Q1pdQadt12anX1QRmU2Y/A==", + "JIC8R48jGVqro6wmG2KXIw==", + "eWgLAqJOU+fdn8raHb9HCw==", + "5CMadLqS2KWwwMCpzlDmLw==", + "H1y2iXVaQYwP0SakN6sa+Q==", + "CUCjG2UaEBmiYWQc6+AS1Q==", + "yV3IbbTWAbHMhMGVvgb/ZQ==", + "80PCwYh4llIKAplcDvMj4g==", + "fgdUFvQPb5h+Rqz8pzLsmw==", + "2SI4F7Vvde2yjzMLAwxOog==", + "kJdY3XEdJS/hyHdR+IN0GA==", + "IKgNa2oPaFVGYnOsL+GC5Q==", + "eXFOya6x5inTdGwJx/xtUQ==", + "uTA0XbiH3fTeVV7u5z0b3w==", + "onFcHOO1c3pDdfCb5N4WkQ==", + "Slu3z535ijcs5kzDnR7kfA==", + "SElc2+YVi3afE1eG1MI7dQ==", + "ND2hYtAIQGMxBF7o7+u7nQ==", + "Pv9FWQEDLKnG/9K9EIz4Gw==", + "6CjtF1S2Y6RCbhl7hMsD+g==", + "rs2QrN4qzAHCHhkcrAvIfA==", + "eTMPXa60OTGjSPmvR4IgGw==", + "pvXHwJ3dwf9GDzfDD9JI3g==", + "CRmAj3JcasAb4iZ9ZbNIbw==", + "rcY4Ot40678ByCfqvGOGdg==", + "l4ddTxbTCW5UmZW+KRmx6A==", + "NKRzJndo2uXNiNppVnqy1g==", + "0NrvBuyjcJ2q6yaHpz/FOA==", + "3YXp1PmMldUjBz3hC6ItbA==", + "CmVD6nh8b/04/6JV9SovlA==", + "HjyxyL0db2hGDq2ZjwOOhg==", + "4PBaoeEwUj79njftnYYqLg==", + "vFFzkWgGyw6OPADONtEojQ==", + "czBWiYsQtNFrksWwoQxlOw==", + "9iB7+VwXRbi6HLkWyh9/kg==", + "zwY6tCjjya/bgrYaCncaag==", + "mW6TCje9Zg2Ep7nzmDjSYQ==", + "5LJqHFRyIwQKA4HbtqAYQQ==", + "INNBBin5ePwTyhPIyndHHg==", + "dChBe9QR29ObPFu/9PusLg==", + "1dhq3ozNCx0o4dV1syLVDA==", + "nyaekSYTKzfSeSfPrB114Q==", + "TfNHjSTV8w6Pg6+FaGlxvA==", + "m/Lp4U75AQyk9c8cX14HJg==", + "uU1TX5DoDg6EcFKgFcn0GA==", + "B+TsxQZf0IiQrU8X9S4dsQ==", + "6b7ue29cBDsvmj1VSa5njw==", + "RvXWAFwM+mUAPW1MjPBaHA==", + "pdaY6kZ8+QqkMOInvvACNA==", + "7nr3zyWL+HHtJhRrCPhYZA==", + "BXGlq54wIH6R3OdYfSSDRw==", + "b06KGv5zDYsTxyTbQ9/eyA==", + "8ylI1AS3QJpAi3I/NLMYdg==", + "0fpe9E6m3eLp/5j5rLrz2Q==", + "Qrh7OEHjp80IW+YzQwzlJg==", + "lqhgbgEqROAdfzEnJ17eXA==", + "Dulw855DfgIwiK7hr3X8vg==", + "wsp+vmW8sEqXYVURd/gjHA==", + "VoPth5hDHhkQcrQTxHXbuw==", + "TgWe70YalDPyyUz6n88ujg==", + "9lLhHcrPWI4EsA4fHIIXuw==", + "UymZUnEEQWVnLDdRemv+Tw==", + "qnkFUlJ8QT322JuCI3LQgg==", + "/p/aCTIhi1bU0/liuO/a2Q==", + "hWoxz5HhE50oYBNRoPp1JQ==", + "88tB/HgUIUnqWXEX++b5Aw==", + "Z8T1b9RsUWf59D06MUrXCQ==", + "BZTzHJGhzhs3mCXHDqMjnQ==", + "XfY+QUriCAA1+3QAsswdgg==", + "TZ3ATPOFjNqFGSKY3vP2Hw==", + "cl4t9FXabQg7tbh1g7a0OA==", + "9SgfpAY0UhNC6sYGus9GgQ==", + "d/Wd3Ma1xYyoMByPQnA9Cw==", + "DDitrRSvovaiXe2nfAtp4g==", + "s+eHg5K9zZ2Jozu5Oya9ZQ==", + "z3L2BNjQOMOfTVBUxcpnRA==", + "v4xIYrfPGILEbD/LwVDDzA==", + "HoaBBw2aPCyhh0f5GxF+/Q==", + "i9IRqAqKjBTppsxtPB7rdw==", + "cWUg7AfqhiiEmBIu+ryImA==", + "E+02smwQGBIxv42LIF2Y4Q==", + "W4CfeVp9mXgk04flryL7iA==", + "9SUOfKtfKmkGICJnvbIDMg==", + "xweGAZf+Yb3TtwR/sGmGIA==", + "EJgedRYsZPc4cT9rlwaZhg==", + "wv4NC9CIpwuGf/nOQYe/oA==", + "ZXeMG5eqQpZO/SGKC4WQkA==", + "bzXXzQGZs8ustv0K4leklA==", + "RkQK9S1ezo+dFYHQP57qrw==", + "mrinv7KooPQPrLCNTRWCFg==", + "qIUJPanWmGzTD1XxvHp+6w==", + "Js7g8Dr6XsnGURA4UNF0Ug==", + "dpSTNOCPFHN5yGoMpl1EUA==", + "ugY8rTtJkN4CXWMVcRZiZw==", + "rqHKB91H3qVuQAm+Ym5cUA==", + "UjmDFO7uzjl4RZDPeMeNyg==", + "cu4ZluwohhfIYLkWp72pqA==", + "ZydKlOpn2ySBW0G3uAqwuw==", + "LWd0+N3M94n81qd346LfJQ==", + "VbHoWmtiiPdABvkbt+3XKQ==", + "J4MC9He6oqjOWsYQh9nl3Q==", + "ahAbmGJZvUOXrcK6OydNGQ==", + "Byhi4ymFqqH8uIeoMRvPug==", + "LSN9GmT6LUHlCAMFqpuPIA==", + "IAMInfSYb76GxDlAr1dsTg==", + "qYHdgFAXhF/XcW4lxqfvWQ==", + "26+yXbqI+fmIZsYl4UhUzw==", + "AwPTZpC28NJQhf5fNiJuLA==", + "SESKbGF35rjO64gktmLTWA==", + "YVlRQHQglkbj3J2nHiP/Hw==", + "DdaT4JLC7U0EkF50LzIj9w==", + "G0LChrb0OE5YFqsfTpIL1Q==", + "5Yrj6uevT8wHRyqqgnSfeg==", + "NmWmDxwK5FpKlZbo0Rt8RA==", + "iUsUCB0mfRsE9KPEQctIzw==", + "Tm4zk2Lmg8w4ITMI31NfTA==", + "Vu0E+IJXBnc25x4n41kQig==", + "6wkfN8hyKmKU6tG3YetCmw==", + "trjM81KANPZrg9iSThWx6Q==", + "iGuY4VxcotHvMFXuXum7KA==", + "ICPdBCdONUqPwD5BXU5lrw==", + "alqHQBz8V446EdzuVfeY5Q==", + "74FW/QYTzr/P1k6QwVHMcw==", + "avZp5K7zJvRvJvpLSldNAw==", + "TIKadc6FAaRWSQUg5OATgg==", + "PfkWkSbAxIt1Iso0znW0+Q==", + "Z+bsbVP91KrJvxrujBLrrQ==", + "mrxlFD3FBqpSZr1kuuwxGg==", + "nUgYO7/oVNSX8fJqP2dbdg==", + "tVhXk9Ff3wAg56FbdNtcFg==", + "DdiNGiOSoIZxrMrGNvqkXw==", + "CDsanJz7e3r/eQe+ZYFeVQ==", + "wVfSZYjMjbTsD2gaSbwuqQ==", + "6c0iuya20Ys8BsvoI4iQaQ==", + "qCPfJTR8ecTw6u6b1yHibA==", + "fZrj3wGQSt8RXv0ykJROcQ==", + "gR3B8usSEb0NLos51BmJQg==", + "vTAmgfq3GxL4+ubXpzwk5w==", + "jLkmUZ6fV56GfhC0nkh4GA==", + "3v09RHCPTLUztqapThYaHg==", + "nULSbtw2dXbfVjZh33pDiA==", + "IHhyR6+5sZXTH+/NrghIPg==", + "tnUtJ/DQX9WaVJyTgemsUA==", + "7xTKFcog69nTmMfr5qFUTA==", + "IshzWega6zr3979khNVFQQ==", + "Ng5v/B9Z10TTfsDFQ/XrXQ==", + "hnCUnoxofUiqQvrxl73M8w==", + "VPa7DG6v7KnzMvtJPb88LQ==", + "4LtQrahKXVtsbXrEzYU1zQ==", + "Ev/xjTi7akYBI7IeZJ4Igw==", + "41WEjhYUlG6jp2UPGj11eQ==", + "JvXTdChcE3AqMbFYTT3/wg==", + "2rOkEVl90EPqfHOF5q2FYw==", + "mjFBVRJ7TgnJx+Q74xllPg==", + "Uy4QI8D2y1bq/HDNItCtAw==", + "wMOE/pEKVIklE75xjt6b6w==", + "ZcuIvc8fDI+2uF0I0uLiVA==", + "CX/N/lHckmAtHKysYtGdZA==", + "j8to4gtSIRYpCogv2TESuQ==", + "iS9wumBV5ktCTefFzKYfkA==", + "ewPT4dM12nDWEDoRfiZZnA==", + "vWn9OPnrJgfPavg4D6T/HQ==", + "J/PNYu4y6ZMWFFXsAhaoow==", + "catI+QUNk3uJ+mUBY3bY8Q==", + "F8tEIT5EhcvLNRU5f0zlXQ==", + "zyA9f5J7mw5InjhcfeumAQ==", + "MlOOZOwcRGIkifaktEq0aQ==", + "Pt3i49uweYVgWze3OjkjJA==", + "sfIClgTMtZo9CM9MHaoqhQ==", + "HeQbUuBM9sqfXFXRBDISSw==", + "SFn78uklZfMtKoz2N0xDaQ==", + "H6j2nPbBaxHecXruxiWYkA==", + "fU32wmMeD44UsFSqFY0wBA==", + "hDILjSpTLqJpiSSSGu445A==", + "ieEAgvK9LsWh2t6DsQOpWA==", + "xfjBQk3CrNjhufdPIhr91A==", + "j+8/VARfbQSYhHzj0KPurQ==", + "/zFLRvi75UL8qvg+a6zqGg==", + "U0KmEI6e5zJkaI4YJyA5Ew==", + "uXvr6vi5kazZ9BCg2PWPJA==", + "jEqP0dyHKHiUjZ9dNNGTlQ==", + "1xWx5V3G9murZP7srljFmA==", + "OIwtfdq37eQ0qoXuB2j7Hw==", + "fUAy3f9bAglLvZWvkO2Lug==", + "duRFqmvqF93uf/vWn8aOmg==", + "ysRQ+7Aq7eVLOp88KnFVMA==", + "CkZUmKBAGu0FLpgPDrybpw==", + "TrLmfgwaNATh24eSrOT+pw==", + "83wtvSoSP9FVBsdWaiWfpA==", + "pUfWmRXo70yGkUD/x5oIvA==", + "PybPZhJErbRTuAafrrkb3g==", + "8hsfXqi4uiuL+bV1VrHqCw==", + "TVlHoi8J7sOZ2Ti7Dm92cQ==", + "za4rzveYVMFe3Gw531DQJQ==", + "JKphO0UYjFqcbPr6EeBuqg==", + "hqeSvwu8eqA072iidlJBAw==", + "bUF0JIfS4uKd3JZj2xotLQ==", + "hKOsXOBoFTl/K4xE+RNHDA==", + "JHBjKpCgSgrNNACZW1W+1w==", + "Rrq0ak9YexLqqbSD4SSXlw==", + "+NmjwjsPhGJh9bM10SFkLw==", + "xMIHeno2qj3V8q9H1xezeg==", + "TcFinyBrUoAEcLzWdFymow==", + "Rvchz/xjcY9uKiDAkRBMmA==", + "TYlnrwgyeZoRgOpBYneRAg==", + "PbnxuVerGwHyshkumqAARg==", + "iFtadcw8v6betKka9yaJfg==", + "7wgT9WIiMVcrj48PVAMIgw==", + "2HHqeGRMfzf3RXwVybx+ZQ==", + "tdgI9v7cqJsgCAeW1Fii1A==", + "4ZFYKa7ZgvHyZLS6WpM8gA==", + "gB8wkuIzvuDAIhDtNT1gyA==", + "g1ELwsk6hQ+RAY1BH640Pg==", + "UZoibx+y1YJy/uRSa9Oa2w==", + "yS/yMnJDHW0iaOsbj4oPTg==", + "JzW+yhrjXW1ivKu3mUXPXg==", + "/wIZAye9h1TUiZmDW0ZmYA==", + "YK+q7uJObkQZvOwQ9hplMg==", + "Rs8deApkoosIJSfX7NXtAA==", + "MsCloSmTFoBpm7XWYb+ueQ==", + "3ltw31yJuAl4VT6MieEXXw==", + "1+qmrbC8c7MJ6pxmDMcKuA==", + "AYxGETZs477n2sa1Ulu/RQ==", + "Q0TJZxpn3jk67L7N+YDaNA==", + "OGpsXRHlaN8BvZftxh1e7A==", + "UbABE6ECnjB+9YvblE9CYw==", + "kZ0D191c/uv4YMG15yVLDw==", + "QWURrsEgxbJ8MWcaRmOWqw==", + "xiFlcSfa/gnPiO+LwbixcQ==", + "Szko0IPE7RX2+mfsWczrMg==", + "Ugt8HVC/aUzyWpiHd0gCOQ==", + "8j9GVPiFdfIRm/+ho7hpoA==", + "KR401XBdgCrtVDSaXqPEiA==", + "d0NBFiwGlQNclKObRtGVMQ==", + "XEwOJG24eaEtAuBWtMxhwg==", + "0Y6iiZjCwPDwD/CwJzfioQ==", + "MvMbvZNKbXFe2XdN+HtnpQ==", + "fsoXIbq0T0nmSpW8b+bj+g==", + "Uje3Ild84sN41JEg3PEHDg==", + "i6ZYpFwsyWyMJNgqUMSV1A==", + "+P5q4YD1Rr5SX26Xr+tzlw==", + "z4oKy2wKH+sbNSgGjbdHGw==", + "XwKWd03sAz8MmvJEuN08xA==", + "Xv0mNYedaBc57RrcbHr9OA==", + "9oUawSwUGOmb0sDn3XS6og==", + "9RGIQ2qyevNbSSEF36xk/A==", + "q8YF9G2jqydAxSqwyyys5Q==", + "m5JIUETVXcRza4VL4xlJbg==", + "aRpdnrOyu5mWB1P5YMbvOA==", + "rM/BOovNgnvebKMxZQdk7g==", + "fQS0jnQMnHBn7+JZWkiE/g==", + "gAoV4BZYdW1Wm712YXOhWQ==", + "hCzsi1yDv9ja5/o7t94j9Q==", + "CoLvjQDQGldGDqRxfQo+WQ==", + "pfGcaa49SM3S6yJIPk/EJQ==", + "yYp4iuI5f/y/l1AEJxYolQ==", + "Jj4IrSVpqQnhFrzNvylSzA==", + "4jeOFKuKpCmMXUVJSh9y0g==", + "+NMUaQ7XPsAi0rk7tTT9wQ==", + "Jt4Eg6MJn8O4Ph/K2LeSUA==", + "CiiUeJ0LeWfm7+gmEmYXtg==", + "c5Tc7rTFXNJqYyc0ppW+Iw==", + "4KJZPCE9NKTfzFxl76GWjg==", + "aXs9qTEXLTkN956ch3pnOA==", + "f5Xo7F1uaiM760Qbt978iw==", + "wpZqFkKafFpLcykN2IISqg==", + "vIORTYSHFIXk5E2NyIvWcQ==", + "prOsOG0adI4o+oz50moipw==", + "blygTgAHZJ3NzyAT33Bfww==", + "rBt6L/KLT7eybxKt5wtFdg==", + "vMuaLvAntJB5o7lmt/kVXA==", + "iujlt9fXcUXEYc+T2s5UjA==", + "LyYPOZKm8bBegMr5NTSBfg==", + "ZtWvgitOSRDWq7LAKYYd4Q==", + "kh51WUI5TRnKhur6ZEpRTQ==", + "VzQ1NwNv9btxUzxwVqvHQg==", + "8fJLQeIHaTnJ8wGqUiKU6g==", + "vvEH5A39TTe1AOC11rRCLA==", + "dihDsG7+6aocG6M9BWrCzQ==", + "3jqsY8/xTWELmu/az3Daug==", + "mpOtwBvle+nyY6lUBwTemw==", + "E1CvxFbuu9AYW604mnpGTw==", + "1LPC0BzhJbepHTSAiZ3QTw==", + "XpGXh76RDgXC4qnTCsnNHA==", + "3Gg9N7vjAfQEYOtQKuF/Eg==", + "+WpF8+poKmHPUBB4UYh/ig==", + "UNt7CNMtltJWq8giDciGyA==", + "RIZYDgXqsIdTf9o2Tp/S7g==", + "0QCQORCYfLuSbq94Sbt0bQ==", + "hvsZ5JmVevK1zclFYmxHaw==", + "3+9nURtBK3FKn0J9DQDa3g==", + "jdVMQqApseHH3fd91NFhxg==", + "VX+cVXV8p9i5EBTMoiQOQQ==", + "I5qDndyelK4Njv4YrX7S6w==", + "rWliqgfZ3/uCRBOZ9sMmdA==", + "vwno3vugCvt6ooT3CD4qIQ==", + "cffrYrBX3UQhfX1TbAF+GQ==", + "nOiwBFnXxCBfPCHYITgqNg==", + "LQttmX92SI94+hDNVd8Gtw==", + "iCF+GWw9/YGQXsOOPAnPHQ==", + "nwtCsN1xEYaHvEOPzBv+qQ==", + "CQpJFrpOvcQhsTXIlJli+Q==", + "tYeIZjIm0tVEsYxH1iIiUQ==", + "iCnm5fPmSmxsIzuRK6osrA==", + "tX8X8KoxUQ8atFSCxgwE1Q==", + "hZlX6qOfwxW5SPfqtRqaMw==", + "2aIx9UdMxxZWvrfeJ+DcTw==", + "TlJizlASbPtShZhkPww4UA==", + "p+bx+/WQWALXEBCTnIMr4w==", + "4VR5LiXLew6Nyn91zH9L4w==", + "bfUD03N2PRDT+MZ+WFVtow==", + "cTvDd8okNUx0RCMer6O8sw==", + "49jZr/mEW6fvnyzskyN40w==", + "vHmQUl4WHXs1E/Shh+TeyA==", + "fgXfRuqFfAu8qxbTi4bmhA==", + "Wn+Vj4eiWx0WPUHr3nFbyA==", + "2SwIiUwT4vRZPrg7+vZqDA==", + "nkedTagkmf6YE4tEY+0fKw==", + "8nOTDhFyZ8YUA4b6M5p84w==", + "qnzWszsyJhYtx8wkMN6b1g==", + "ka7pMp8eSiv92WgAsz2vdA==", + "pGQEWJ38hb/ZYy2P1+FIuw==", + "cVhdRFuZaW/09CYPmtNv5g==", + "prCOYlboBnzmLEBG/OeVrQ==", + "oIWwTbkVS5DDL47mY9/1KQ==", + "PKtXc4x4DEjM45dnmPWzyg==", + "f9ywiGXsz+PuEsLTV3zIbQ==", + "6G2bD3Y7qbGmfPqH9TqLFA==", + "DMHmyn2U2n+UXxkqdvKpnA==", + "XOG1PYgqoG8gVLIbVLTQgg==", + "1FSrgkUXgZot2CsmbAtkPw==", + "BxFP+4o6PSlGN78eSVT1pA==", + "EZVQGsXTZvht1qedRLF8bQ==", + "eYAQWuWZX2346VMCD6s7/A==", + "jkUpkLoIXuu7aSH8ZghIAQ==", + "mXPtbPaoNAAlGmUMmJEWBQ==", + "HLesnV3DL+FhWF3h6RXe8g==", + "nDAsSla+9XfAlQSPsXtzPA==", + "RAECgYZmcF4WxcFcZ4A0Ww==", + "W+M4BcYNmjj7xAximDGWsA==", + "ueODvMv/f9ZD8O0aIHn4sg==", + "cszpMdGbsbe6BygqMlnC9Q==", + "siHwJx6EgeB1gBT9z/vTyw==", + "FN7oLGBQGHXXn5dLnr/ElA==", + "Tud+AMyuFkWYYZ73yoJGpQ==", + "TuaG3wRdM9BWKAxh2UmAsg==", + "8CjmgWQSAAGcXX9kz3kssw==", + "ays5/F7JANIgPHN0vp2dqQ==", + "PCOGl7GIqbizAKj/sZmlwQ==", + "rZKD8oJnIj5fSNGiccfcvA==", + "gFEnTI8os2BfRGqx9p5x8w==", + "5r1ZsGkrzNQEpgt/gENibw==", + "1YO9G8qAhLIu2rShvekedw==", + "6ZKmm7IW7IdWuVytLr68CQ==", + "mMfn8OaKBxtetweulho+xQ==", + "GQJxu1SoMBH14KPV/G/KrQ==", + "IYIP2UBRyWetVfYLRsi1SQ==", + "Jit0X0srSNFnn8Ymi1EY+g==", + "ARCWkHAnVgBOIkCDQ19ZuA==", + "qA0sTaeNPNIiQbjIe1bOgQ==", + "iGI9uqMoBBAjPszpxjZBWQ==", + "+L1FDsr5VQtuYc2Is5QGjw==", + "4XNUmgwxsqDYsNmPkgNQYQ==", + "Yig+Wh18VIqdsmwtwfoUQw==", + "uqp92lAqjec8UQYfyjaEZw==", + "QiozlNcQCbqXtwItWExqJQ==", + "JFHutgSe1/SlcYKIbNNYwQ==", + "Y26jxXvl79RcffH8O8b9Ew==", + "bQ7J5mebp38rfP/fuqQOsg==", + "HI4ZIE5s8ez8Rb+Mv39FxA==", + "OzH7jTcyeM7RPVFtBdakpQ==", + "HLxROy6fx/mLXFTDSX4eLA==", + "s5RUHVRNAoKMuPR/Jkfc2Q==", + "X9QAaNjgiOeAWSphrGtyVw==", + "ALJWKUImVE40MbEooqsrng==", + "9MDG0WeBPpjGJLEmUJgBWg==", + "9RXymE9kCkDvBzWGyMgIWA==", + "vFox1d3llOeBeCUZGvTy0A==", + "r3lQAYOYhwlLnDWQIunKqg==", + "2os5s7j7Tl46ZmoZJH8FjA==", + "O5N2yd+QQggPBinQ+zIhtQ==", + "ZygAjaN62XhW5smlLkks+Q==", + "AgDJsaW0LkpGE65Kxk5+IA==", + "omAjyj1l6gyQAlBGfdxJTw==", + "fY9VATklOvceDfHZDDk57A==", + "StpQm/cQF8cT0LFzKUhC5w==", + "CYJB3qy5GalPLAv1KGFEZA==", + "coGEgMVs2b314qrXMjNumQ==", + "DQQB/l55iPN9XcySieNX3A==", + "6dshA8knH5qqD+KmR/kdSQ==", + "qyRmvxh8p4j4f+61c10ZFQ==", + "apWEPWUvMC24Y+2vTSLXoA==", + "RzX2OfSFEd//LhZwRwzBVw==", + "NdULoUDGhIolzw1PyYKV0A==", + "5w/c9WkI/FA+4lOtdPxoww==", + "bV9r7j2kNJpDCEM5E2339Q==", + "vbyiKeDCQ4q9dDRI1Q0Ong==", + "9xIgKpZGqq0/OU6wM5ZSHw==", + "RYkDwwng6eeffPHxt8iD9A==", + "w5N/aHbtOIKzcvG3GlMjGA==", + "3P2aJxV8Trll2GH9ptElYA==", + "yteeQr3ub2lDXgLziZV+DQ==", + "yqtj8GfLaUHYv/BsdjxIVw==", + "NyF+4VRog7etp90B9FuEjA==", + "uwA6N5LptSXqIBkTO0Jd7Q==", + "6lVSzYUQ/r0ep4W2eCzFpg==", + "1d7RPHdZ9qzAbG3Vi9BdFA==", + "7br49X11xc2GxQLSpZWjKQ==", + "peMW+rpwmXrSwplVuB/gTA==", + "RqYpA5AY7mKPaSxoQfI1CA==", + "dqVw2q2nhCvTcW82MT7z0g==", + "5S5/asYfWjOwnzYpbK6JDw==", + "NvkR0inSzAdetpI4SOXGhw==", + "tIqwBotg052wGBL65DZ+yA==", + "S4RvORcJ3m6WhnAgV4YfYA==", + "UAqf4owQ+EmrE45hBcUMEw==", + "4aPU6053cfMLHgLwAZJRNg==", + "3Y6/HqS1trYc9Dh778sefg==", + "ck86G8HsbXflyrK7MBntLg==", + "GLmWLXURlUOJ+PMjpWEXVA==", + "jNJQ6otieHBYIXA9LjXprg==", + "AsAHrIkMgc3RRWnklY9lJw==", + "FCLQocqxxhJeleARZ6kSPg==", + "3Leu2Sc+YOntJFlrvhaXeg==", + "hSkY45CeB6Ilvh0Io4W6cg==", + "DwrNdmU5VFFf3TwCCcptPA==", + "u2WQlcMxOACy6VbJXK4FwA==", + "E9IlDyULLdeaVUzN6eky8g==", + "EXveRXjzsjh8zbbQY2pM9g==", + "5VO1inwXMvLDBQSOahT6rg==", + "HaHTsLzx7V3G1SFknXpGxA==", + "MMaegl2Md9s/wOx5o9564w==", + "mpWNaUH9kn4WY26DWNAh3Q==", + "w3G+qXXqqKi8F5s+qvkBUg==", + "wM8tnXO4PDlLVHspZFcjYw==", + "LFcpCtnSnsCPD2gT/RA+Zg==", + "bhVbgJ4Do4v56D9mBuR/EA==", + "yU3N0HMSP5etuHPNrVkZtg==", + "FzqIpOcTsckSNHExrl+9jg==", + "BYz52gYI/Z6AbYbjWefcEA==", + "h3vYYI9yhpSZV2MQMJtwFQ==", + "adJAjAFyR2ne1puEgRiH+g==", + "eDcyiPaB954q5cPXcuxAQw==", + "40gCrW4YWi+2lkqMSPKBPg==", + "ulLuTZqhEDkX0EJ3xwRP9A==", + "y4iBxAMn/KzMmaWShdYiIw==", + "ilBBNK/IV69xKTShvI94fQ==", + "0HN6MIGtkdzNPsrGs611xA==", + "twPn6wTGqI0aR//0wP3xtA==", + "3UNJ37f+gnNyYk9yLFeoYA==", + "4SdHWowXgCpCDL28jEFpAw==", + "Mr5mCtC53+wwmwujOU/fWw==", + "81pAhreEPxcKse+++h1qBg==", + "KmcGEE0pacQ/HDUgjlt7Pg==", + "Gt4/MMrLBErhbFjGbiNqQQ==", + "lf1fwA0YoWUZaEybE+LyMQ==", + "RIVYGO2smx9rmRoDVYMPXw==", + "rJ9qVn8/2nOxexWzqIHlcQ==", + "lfOLLyZNbsWQgHRhicr4ag==", + "wgH1GlUxWi6/yLLFzE76uQ==", + "Qg1ubGl+orphvT990e5ZPA==", + "Z5B+uOmPZbpbFWHpI9WhPw==", + "snGTzo540cCqgBjxrfNpKw==", + "ZqkmoGB0p5uT5J6XBGh7Tw==", + "uPi8TsGY3vQsMVo/nsbgVQ==", + "Y5XR8Igvau/h+c1pRgKayg==", + "ZmVpw1TUVuT13Zw/MNI5hQ==", + "60suecbWRfexSh7C67RENA==", + "kZ/mZZg9YSDmk2rCGChYAg==", + "OpL+vHwPasW30s2E1TYgpA==", + "ZVnErH1Si4u51QoT0OT7pA==", + "3pi3aNVq1QNJmu1j0iyL0g==", + "tb5+2dmYALJibez1W4zXgA==", + "jOPdd330tB6+7C29a9wn0Q==", + "5oD/aGqoakxaezq43x0Tvw==", + "HdB7Se47cWjPgpJN0pZuiA==", + "6WhHPWlqEUqXC52rHGRHjA==", + "WLwpjgr9KzevuogoHZaVUw==", + "E8yMPK7W0SIGTK6gIqhxiQ==", + "1/Hxu8M9N/oNwk8bCj4FNQ==", + "Uo1ebgsOxc3eDRds1ah3ag==", + "5pqqzC/YmRIMA9tMFPi7rg==", + "ri4AOITPdB1YHyXV+5S51g==", + "HfvsiCQN/3mT0FabCU5ygQ==", + "UQTQk5rrs6lEb1a+nkLwfg==", + "VH70dN82yPCRctmAHMfCig==", + "yD3Dd4ToRrl53k/2NSCJiw==", + "fO0+6TsjL+45p9mSsMRiIg==", + "fM5uYpkvJFArnYiQ3MrQnA==", + "V+QzdKh5gxTPp2yPC9ZNEg==", + "XHHEg/8KZioW/4/wgSEkbQ==", + "2abfl3N46tznOpr+94VONQ==", + "gxwbqZDHLbQVqXjaq42BCg==", + "WnHK5ZQDR6Da5cGODXeo0A==", + "SChDh/Np1HyTPWfICfE1uA==", + "yhexr/OFKfZl0o3lS70e4w==", + "N65PqIWiQeS082D6qpfrAg==", + "RM5CpIiB94Sqxi462G7caA==", + "CBAGa5l95f3hVzNi6MPWeQ==", + "OHJBT2SEv5b5NxBpiAf7oQ==", + "p48i7AfSSAyTdJSyHvOONw==", + "/SP6pOdYFzcAl2OL05z4uQ==", + "N8dXCawxSBX40fgRRSDqlQ==", + "bMWFvjM8eVezU1ZXKmdgqw==", + "Um1ftRBycvb+363a90Osog==", + "QAz7FA+jpz9GgLvwdoNTEQ==", + "qO4HlyHMK5ygX+6HbwQe8w==", + "UgvtdE2eBZBUCAJG/6c0og==", + "q5g3c8tnQTW2EjNfb2sukw==", + "gsC/mWD8KFblxB0JxNuqJw==", + "SVFbcjXbV7HRg+7jUrzpwg==", + "bz294kSG4egZnH2dJ8HwEg==", + "ybpTgPr3SjJ12Rj5lC/IMA==", + "yDrAd1ot38soBk7zKdnT8A==", + "BB/R8oQOcoE4j63Hrh8ifg==", + "GNrMvNXQkW7PydlyJa+f1w==", + "w0PKdssv+Zc5J/BbphoxpA==", + "D5ibbo8UJMfFZ48RffuhgQ==", + "MdvhC1cuXqni/0mtQlSOCw==", + "wQKL8Ga6JQkpZ7yymDkC3w==", + "o1uhaQg5/zfne84BFAINUQ==", + "Ft2wXUokFdUf6d2Y/lwriw==", + "sLJrshdEANp0qk2xOUtTnQ==", + "jx7rpxbm1NaUMcE2ktg5sA==", + "ZQ0ZnTsZKWxbRj7Tilh24Q==", + "KhrIIHfqXl9zGE9aGrkRVg==", + "jS0JuioLGAVaHdo/96JFoQ==", + "tr+U/vt+MIGXPRQYYWJfRg==", + "TXab/hqNGWaSK+fXAoB2bg==", + "0K4NBxqEa3RYpnrkrD/XjQ==", + "3oMTbWf7Bv83KRlfjNWQZA==", + "yLAhLNezvqVHmN1SfMRrPw==", + "ZYW30FfgwHmW6nAbUGmwzA==", + "CZNoTy26VUQirvYxSPc/5A==", + "CF1sAlhjDQY/KWOBnSSveA==", + "+CLf5witKkuOvPCulTlkqw==", + "1m1yD4L9A7Q1Ot+wCsrxJQ==", + "2E41e0MgM3WhFx2oasIQeA==", + "mDXHuOmI4ayjy2kLSHku1Q==", + "sCLMrLjEUQ6P1L8tz90Kxg==", + "zDUZCzQesFjO1JI3PwDjfg==", + "x/BIDm6TKMhqu/gtb3kGyw==", + "DEaZD/8aWV6+zkiLSVN/gA==", + "7dz+W494zwU5sg63v5flCg==", + "Y5iDQySR2c3MK7RPMCgSrw==", + "GglPoW5fvr4JSM3Zv99oiA==", + "myzvc+2MfxGD9uuvZYdnqQ==", + "V9G1we3DOIQGKXjjPqIppQ==", + "gYvdNJCDDQmNhtJ6NKSuTA==", + "rXtGpN17Onx8LnccJnXwJQ==", + "/a+bLXOq02sa/s8h7PhUTg==", + "htNVAogFakQkTX6GHoCVXg==", + "eshD40tvOA6bXb0Fs/cH3A==", + "K1CGbMfhlhIuS0YHLG30PQ==", + "aOeJZUIZM9YWjIEokFPnzQ==", + "r0hAwlS0mPZVfCSB+2G6uQ==", + "0q+erphtrB+6HBnnYg7O6w==", + "bkRdUHAksJZGzE1gugizYQ==", + "J8v2f6hWFu8oLuwhOeoQjA==", + "qkvEep4vvXhc2ZJ6R449Mg==", + "6HGeEPyTAu9oiKhNVLjQnA==", + "JoATsk/aJH0UcDchFMksWA==", + "QozQL0DTtr+PXNKifv6l6g==", + "HiAgt86AyznvbI2pnLalVQ==", + "lY+tivtsfvU0LJzBQ6itYQ==", + "EfXDc6h69aBPE6qsB+6+Ig==", + "gnAIpoCyl3mQytLFgBEgGA==", + "p2JPOX8yDQ0agG+tUyyT/g==", + "zeELfk015D5krExLKRUYtg==", + "wDiGoFEfIVEDyyc4VpwhWQ==", + "7Ephy+mklG2Y3MFdqmXqlA==", + "8ZFPMJJYVJHsfRpU4DigSg==", + "ocRh5LR1ZIN9Johnht8fhQ==", + "l5f3I6osM9oxLRAwnUnc5A==", + "yxCyBXqGWA735JEyljDP7Q==", + "qE/h/Z+6buZWf+cmPdhxog==", + "HCu4ZMrcLMZbPXbTlWuvvQ==", + "TDrq23VUdzEU/8L5i8jRJQ==", + "L+N/6geuokiLPPSDXM9Qkg==", + "v6jZicMNM3ysm3U5xu0HoQ==", + "b85nxzs8xiHxaqezuDVWvg==", + "ca+kx+kf7JuZ3pfYKDwFlg==", + "KlY5TGg0pR/57TVX+ik1KQ==", + "3jmCreW5ytSuGfmeLv7NfQ==", + "ucLMWnNDSqE4NOCGWvcGWw==", + "NSrzwNlB0bde3ph8k6ZQcQ==", + "nL4iEd3b5v4Y9fHWDs+Lrw==", + "W2x0SBzSIsTRgyWUCOZ/lg==", + "ifZM0gBm9g9L09YlL+vXBg==", + "4WcFEswYU/HHQPw77DYnyA==", + "TLJbasOoVO435E5NE5JDcA==", + "WyCFB4+6lVtlzu3ExHAGbQ==", + "BW0A06zoQw7S+YMGaegT7g==", + "qP1cCE4zsKGTPhjbcpczMw==", + "UVEZPoH9cysC+17MKHFraw==", + "eQ45Mvf5in9xKrP6/qjYbg==", + "fOARCnIg/foF/6tm7m9+3w==", + "lK2xe+OuPutp4os0ZAZx5w==", + "Tug3eh+28ttyf+U7jfpg5w==", + "ENFfP93LA257G6pXQkmIdg==", + "FuWspiqu5g8Eeli5Az+BkA==", + "kIGxCUxSlNgsKZ45Al1lWw==", + "RzeH+G3gvuK1z+nJGYqARQ==", + "0ofMbUCA3/v5L8lHnX4S5w==", + "VI8pgqBZeGWNaxkuqQVe7g==", + "x6lNRGgJcRxgKTlzhc1WPg==", + "La0gzdbDyXUq6YAXeKPuJA==", + "dAq8/1JSQf1f4QPLUitp0g==", + "WN7lFJfw4lSnTCcbmt5nsg==", + "2aDK0tGNgMLyxT+BQPDE8Q==", + "9W57pTzc572EvSURqwrRhw==", + "37Nkh06O979nt7xzspOFyQ==", + "4TQkMnRsXBobbtnBmfPKnA==", + "f/BjtP5fmFw2dRHgocbFlg==", + "9vEgJVJLEfed6wJ7hBUGgQ==", + "HRWYX2XOdsOqYzCcqkwIyw==", + "StDtLMlCI75g4XC59mESEQ==", + "99+SBN45LwKCPfrjUKRPmw==", + "HbT6W1Ssd3W7ApKzrmsbcg==", + "l8/KMItWaW3n4g1Yot/rcQ==", + "s7iW1M6gkAMp+D/3jHY58w==", + "GWwJ32SZqD5wldrXUdNTLA==", + "YhLEPsi/TNyeUJw69SPYzQ==", + "g0aTR8aJ0uVy3YvGYu5xrw==", + "m6get5wjq5j1i5abnpXuZQ==", + "ymtA8EMPMgmMcimWZZ0A1Q==", + "HEcOaEd9zCoOVbEmroSvJg==", + "F8l+Qd9TZgzV+r8G584lKA==", + "3yDD+xT8iRfUVdxcc7RxKw==", + "1eRUCdIJe3YGD5jOMbkkOg==", + "DO1/jfP/xBI9N0RJNqB2Rw==", + "SiSlasZ+6U2IZYogqr2UPg==", + "tBQDfy48FnIOZI04rxfdcA==", + "HEghmKg3GN60K7otpeNhaA==", + "mTLBkP+yGHsdk5g7zLjVUw==", + "RgtwfY5pTolKrUGT+6Pp6g==", + "EyIsYQxgFa4huyo/Lomv7g==", + "HwLSUie8bzH+pOJT3XQFyg==", + "7Tauesu7bgs5lJmQROVFiQ==", + "ojugpLIfzflgU2lonfdGxA==", + "ZqjnqxZE/BjOUY0CMdVl0g==", + "oQjugfjraFziga1BcwRLRA==", + "JXCYeWjFqcdSf6QwB54G+A==", + "TeBGJCqSqbzvljIh9viAqA==", + "1Gpj4TPXhdPEI4zfQFsOCg==", + "asouSfUjJa8yfMG7BBe+fA==", + "ccy3Ke2k4+evIw0agHlh3w==", + "CzSumIcYrZlxOUwUnLR2Zw==", + "9QFYrCXsGsInUb4SClS3cQ==", + "3RTtSaMp1TZegJo5gFtwwA==", + "aTWiWjyeSDVY/q8y9xc2zg==", + "UK+R+hAoVeZ4xvsoZjdWpw==", + "rHagXw+CkF3uEWPWDKXvog==", + "MfkyURTBfkNZwB+wZKjP4g==", + "Qf7JFJJuuacSzl6djUT2EQ==", + "K1RL+tLjICBvMupe7QppIQ==", + "R2OOV18CV/YpWL1xzr/VQg==", + "o+areESiXgSO0Lby56cBeg==", + "VPqyIomYm7HbK5biVDvlpw==", + "pw1jplCdTC+b0ThX0FXOjw==", + "gTnsH3IzALFscTZ1JkA9pw==", + "JYJvOZ4CHktLrYJyAbdOnA==", + "P8lUiLFoL100c9YSQWYqDA==", + "LATQEY7f47i77M6p11wjWA==", + "U9kE50Wq5/EHO03c5hE4Ug==", + "pFKzcRHSUBqSMtkEJvrR1Q==", + "vHVXsAMQqc0qp7HA5Q+YkA==", + "3XyoREdvhmSbyvAbgw2y/A==", + "qOEIUWtGm5vx/+fg4tuazg==", + "a6IszND1m+6w+W+CvseC7g==", + "KuNY8qAJBce+yUIluW8AYw==", + "5Wcq+6hgnWsQZ/bojERpUw==", + "l2ZB9TvT68rn8AAN4MdxWw==", + "h5HsEsObPuPFqREfynVblw==", + "fvm0IQfnbfZFETg9v3z/Fg==", + "QV0OG5bpjrjku4AzDvp9yw==", + "nMuMtK/Zkb3Xr34oFuX/Lg==", + "jMZKSMP2THqwpWqJNJRWdw==", + "fX4G68hFL7DmEmjbWlCBJQ==", + "ZlBNHAiYsfaEEiPQ1z+rCA==", + "ckugAisBNX18eQz+EnEjjw==", + "Dt6hvhPJu94CJpiyJ5uUkg==", + "eYE9No9sN5kUZ5ePEyS3+Q==", + "Tp52d1NndiC9w3crFqFm9g==", + "MBjMU/17AXBK0tqyARZP5w==", + "1EI9aa955ejNo1dJepcZJw==", + "FqWLkhWl0iiD/u2cp+XK9A==", + "j8nMH8mK/0Aae7ZkqyPgdg==", + "ZtmnX24AwYAXHb2ZDC6MeQ==", + "who8uUamlHWHXnBf7dwy4A==", + "CmkmWcMK4eqPBcRbdnQvhw==", + "61V74uIjaSfZM8au1dxr1A==", + "778O1hdVKHLG2q9dycUS0Q==", + "IdadoCPmSgHDHzn1zyf8Jw==", + "Z2rwGmVEMCY6nCfHO3qOzw==", + "Q3TpCE+wnmH/1h/EPWsBtQ==", + "HnVfyqgJ+1xSsN4deTXcIA==", + "XgPHx2+ULpm14IOZU2lrDg==", + "IbN736G1Px5bsYqE5gW1JQ==", + "nY/H7vThZ+dDxoPRyql+Cg==", + "wlWxtQDJ+siGhN2fJn3qtw==", + "MrbEUlTagbesBNg0OemHpw==", + "LJtRcR70ug6UHiuqbT6NGw==", + "hSNZWNKUtDtMo6otkXA/DA==", + "LawT9ZygiVtBk0XJ+KkQgQ==", + "DLzHkTjjuH6LpWHo2ITD0Q==", + "i8XXN7jcrmhnrOVDV8a2Hw==", + "ogcuGHUZJkmv+vCz567a2g==", + "rUp5Mfc57+A8Q29SPcvH/Q==", + "6706ncrH1OANFnaK6DUMqQ==", + "gK7dhke5ChQzlYc/bcIkcg==", + "t3Txxjq43e/CtQmfQTKwWg==", + "6ZMs9vCzK9lsbS6eyzZlIA==", + "uTHBqApdKOAgdwX3cjrCYQ==", + "zirOtGUXeRL22ezfotZfQg==", + "iK0dWKHjVVexuXvMWJV9pg==", + "uzEgwx1iAXAvWPKSVwYSeQ==", + "FHvI0IVNvih8tC7JgzvCOw==", + "jjNMPXbmpFNsCpWY0cv3eg==", + "/cJ0Nn5YbXeUpOHMfWXNHQ==", + "WkSJpxBa45XJRWWZFee7hw==", + "edlXkskLx287vOBZ9+gVYg==", + "+Pl0bSMBAdXpRIA+zE02JA==", + "3xw8+0/WU51Yz4TWIMK8mw==", + "GdTanUprpE3X/YjJDPpkhQ==", + "qnsBdl050y9cUaWxbCczRw==", + "pnJnBzAJlO4j3IRqcfmhkQ==", + "USq1iF90eUv41QBebs3bhw==", + "QH3lAwOYBAJ0Fd5pULAZqw==", + "gvvyX5ATi4q9NhnwxRxC8w==", + "7xDIG/80SnhgxAYPL9YJtg==", + "WVhfn2yJZ43qCTu0TVWJwA==", + "twjiDKJM7528oIu/el4Zbg==", + "6sBemZt4qY/TBwqk3YcLOQ==", + "m3XYojKO+I6PXlVRUQBC3w==", + "gUNP5w7ANJm257qjFxSJrA==", + "mMLhjdWNnZ8zts9q+a2v3g==", + "kjWYVC7Eok2w2YT4rrI+IA==", + "ZzT5b0dYQXkQHTXySpWEaA==", + "YzTV0esAxBFVls3e0qRsnA==", + "9xmtuClkFlpz/X5E9JBWBA==", + "nhAnHuCGXcYlqzOxrrEe1g==", + "cbBXgB1WQ/i8Xul0bYY2fg==", + "AkAes5oErTaJiGD2I4A1Pw==", + "Wx9jh/teM0LJHrvTScssyQ==", + "fU5ZZ1bIVsV+eXxOpGWo/Q==", + "k8eZxqwxiN/ievXdLSEL/w==", + "E2LR1aZ3DcdCBuVT7BhReA==", + "1eCHcz4swFH+uRhiilOinQ==", + "JipruVZx4ban3Zo5nNM37g==", + "IPLD9nT5EEYG9ioaSIYuuA==", + "pHozgRyMiEmyzThtJnY4MQ==", + "p0eNK7zJd7D/HEGaVOrtrQ==", + "dGjcKAOGBd4gIjJq7fL+qQ==", + "uMq8cDVWFD+tpn8aeP8Pqg==", + "gC7gUwGumN7GNlWwfIOjJQ==", + "It+K/RCYMOfNrDZxo7lbcA==", + "4CfEP8TeMKX33ktwgifGgA==", + "nxDGRpePV3H4NChn4eLwag==", + "300hoYyMR/mk1mfWJxS8/w==", + "DmxgZsQg+Qy1GP0fPkW3VA==", + "1vqRt79ukuvdJNyIlIag8Q==", + "RWI0HfpP7643OSEZR8kxzw==", + "zZtYkKU50PPEj6qSbO5/Sw==", + "UNRlg6+CYVOt68NwgufGNA==", + "kkbX+a00dfiTgbMI+aJpMg==", + "VIC7inSiqzM6v9VqtXDyCw==", + "l+x2QhxG8wb5AQbcRxXlmA==", + "GUiinC3vgBjbQC2ybMrMNQ==", + "6uMF5i0b/xsk55DlPumT7A==", + "aK9nybtiIBUvxgs1iQFgsw==", + "BLbTFLSb4mkxMaq4/B2khg==", + "mTAqtg6oi0iytHQCaSVUsA==", + "eBapvE+hdyFTsZ0y5yrahg==", + "lHN2dn2cUKJ8ocVL3vEhUQ==", + "Mj87ajJ/yR41XwAbFzJbcA==", + "FA+nK6mpFWdD0kLFcEdhxA==", + "FrTgaF5YZCNkyfR1kVzTLQ==", + "5eHStFN7wEmIE+uuRwIlPQ==", + "AyWlT+EGzIXc395zTlEU5Q==", + "I+wVQA+jpPTJ6xEsAlYucg==", + "Y1flEyZZAYxauMo4cmtJ1w==", + "1AeReq55UQotRQVKJ66pmg==", + "xzGzN5Hhbh0m/KezjNvXbQ==", + "meHzY9dIF7llDpFQo1gyMg==", + "RnOXOygwJFqrD+DlM3R5Ew==", + "JKg64m6mU7C/CkTwVn4ASg==", + "gGLz3Ss+amU7y6JF09jq7A==", + "Pu9pEf+Tek3J+3jmQNqrKw==", + "EATnlYm0p3h04cLAL95JgA==", + "o64LDtKq/Fulf1PkVfFcyg==", + "hUWqqG1QwYgGC5uXJpCvJw==", + "RfSwpO/ywQx4lfgeYlBr2w==", + "VaJc9vtYlqJbRPGb5Tf0ow==", + "9JKIJrlQjhNSC46H3Cstcw==", + "6Z9myGCF5ylWljgIYAmhqw==", + "9bAWYElyRN1oJ6eJwPtCtQ==", + "ohK6EftXOqBzIMI+5XnESw==", + "AVjwqrTBQH1VREuBlOyUOg==", + "G2UponGde3/Z+9b2m9abpQ==", + "DoiItHSms0B9gYmunVbRkQ==", + "vUC0HlTTHj6qNHwfviDtAw==", + "hq35Fjgvrcx6I9e6egWS4w==", + "sw+bmpzqsM4gEQtnqocQLQ==", + "ApiuEPWr8UjuRyJjsYZQBw==", + "VXu4ARjq7DS2IR/gT24Pfw==", + "3TbRZtFtsh9ez8hqZuTDeA==", + "CazLJMJjQMeHhYLwXW7YNg==", + "ROSt+NlEoiPFtpRqKtDUrQ==", + "IUwVHH6+8/0c+nOrjclOWA==", + "lkzFdvtBx5bV6xZO0cxK7g==", + "4ekt4m38G9m599xJCmhlug==", + "fzkmVWKhJsxyCwiqB/ULnQ==", + "LZAKplVoNjeQgfaHqkyEJA==", + "91vfsZ7Lx9x5gqWTOdM4sg==", + "MVoxyIA+emaulH8Oks8Weg==", + "oGH7SMLI2/qjd9Vnhi3s0A==", + "vmqfGJE6r4yDahtU/HLrxw==", + "Y5KKN7t/v9JSxG/m1GMPSA==", + "gXlb7bbRqHXusTE5deolGA==", + "/2c4oNniwhL3z5IOngfggg==", + "HgIFX42oUdRPu7sKAXhNWg==", + "A3dX2ShyL9+WOi6MNJBoYQ==", + "hN9bmMHfmnVBVr+7Ibd2Ng==", + "DB706G73NpBSRS8TKQOVZw==", + "JSyq2MIuObPnEgEUDyALjQ==", + "kSUectNPXpXNg+tIveTFRw==", + "XVVy3e6dTnO3HpgD6BtwQw==", + "td7nDgTDmKPSODRusMcupw==", + "Lt/pVD4TFRoiikmgAxEWEw==", + "mmRob7iyTkTLDu8ObmTPow==", + "Fd0c8f2eykUp9GYhqOcKoA==", + "18RKixTv12q3xoBLz6eKiA==", + "RClzwwKh51rbB4ekl99EZA==", + "oONlXCW4aAqGczQ/bUllBw==", + "foPAmiABJ3IXBoed2EgQXA==", + "wEJDulZafLuXCvcqBYioFQ==", + "K1RgR6HR5uDEQgZ32TAFgA==", + "SEIZhyguLoyH7So0p1KY0A==", + "ggIfX1J4dX3xQoHnHUI7VA==", + "HBRzLacCVYfwUVGzrefZYg==", + "aWZRql2IUPVe9hS3dxgVfQ==", + "Err1mbWJud80JNsDEmXcYg==", + "Z2MkqmpQXdlctCTCUDPyzw==", + "JnE6BK0vpWIhNkaeaYNUzw==", + "5dUry23poD+0wxZ3hH6WmA==", + "DwP0MQf71VsqvAbAMtC3QQ==", + "kHcBZXoxnFJ+GMwBZ/xhfQ==", + "SUAwMWLMml8uGqagz5oqhQ==", + "79uTykH43voFC3XhHHUzKg==", + "P5fucOJhtcRIoElFJS4ffg==", + "s8NpalwgPdHPla7Zi9FJ3w==", + "8cXqZub6rjgJXmh1CYJBOg==", + "tY916jrSySzrL+YTcVmYKQ==", + "DRiFNojs7wM8sfkWcmLnhQ==", + "wqUJ1Gq1Yz2cXFkbcCmzHQ==", + "0u+0WHr7WI6IlVBBgiRi6w==", + "GCYI9Dn1h3gOuueKc7pdKA==", + "nVDxVhaa2o38gd1XJgE3aw==", + "5I/heFSQG/UpWGx0uhAqGQ==", + "1PvTn90xwZJPoVfyT5/uIQ==", + "jHOoSl3ldFYr9YErEBnD3w==", + "swJhrPwllq5JORWiP5EkDA==", + "tj2rWvF2Fl+XIccctj8Mhw==", + "QvYZxsLdu+3nV/WhY1DsYg==", + "fKalNdhsyxTt1w08bv9fJA==", + "CHLHizLruvCrVi9chj9sXA==", + "sa2DECaqYH1z1/AFhpHi+g==", + "LbPp1oL0t3K2BAlIN+l8DA==", + "5SbwLDNT6sBOy6nONtUcTg==", + "AfVPdxD3FyfwwNrQnVNQ7A==", + "jt9Ocr9D8EwGRgrXVz//aQ==", + "KkwQL0DeUM3nPFfHb2ej+A==", + "WwraoO97OTalvavjUsqhxQ==", + "fAKFfwlCOyhtdBK6yNnsNg==", + "EqMlrz1to7HG4GIFTPaehQ==", + "YmjZJyNfHN5FaTL/HAm8ww==", + "L2D7G0btrwxl9V4dP3XM5Q==", + "oUqO4HrBvkpSL781qAC9+w==", + "c6Yhwy/q3j7skXq52l36Ww==", + "FWphIPZMumqnXr1glnbK4w==", + "AcKwfS8FRVqb72uSkDNY/Q==", + "uSIiF1r9F18avZczmlEuMQ==", + "XrFDomoH2qFjQ2jJ2yp9lA==", + "N2X7KWekNN+fMmwyXgKD5w==", + "IdmcpJXyVDajzeiGZixhSA==", + "Wf2olJCYZRGTTZxZoBePuQ==", + "oVlG+0rjrg2tdFImxIeVBA==", + "7w4PDRJxptG8HMe/ijL6cQ==", + "rueNryrchijjmWaA3kljYg==", + "ZybIEGf1Rn/26vlHmuMxhw==", + "yYVW07lOZHdgtX42xJONIA==", + "4ifNsmjYf1iOn2YpMfzihg==", + "KTjwL+qswa+Bid8xLdjMTg==", + "THfzE2G2NVKKfO+A2TjeFw==", + "QoqHzpHDHTwQD5UF30NruQ==", + "dTMoNd6DDr1Tu8tuZWLudw==", + "wOc4TbwQGUwOC1B3BEZ4OQ==", + "gfhkPuMvjoC3CGcnOvki3Q==", + "vljJciS+uuIvL7XXm5688g==", + "EGLOaMe6Nvzs/cmb7pNpbg==", + "oLWWIn/2AbKRHnddr2og9g==", + "7l0RMKbONGS/goW/M+gnMQ==", + "eFkXKRd2dwu/KWI5ZFpEzw==", + "jWsC7kdp2YmIZpfXGUimiA==", + "Jcxjli2tcIAjCe+5LyvqdQ==", + "MUkRa/PjeWMhbCTq43g6Aw==", + "g2nh2xENCFOpHZfdEXnoQA==", + "x6M66krXSi0EhppwmDmsxA==", + "26Wmdp6SkKN74W0/XPcnmA==", + "ycjv4XkS5O7zcF3sqq9MwQ==", + "gfnbviaVhKvv1UvlRGznww==", + "aIPde9CtyZrhbHLK740bfw==", + "0p8YbEMxeb73HbAfvPLQRw==", + "Is3uxoSNqoIo5I15z6Z2UQ==", + "NZtcY8fIpSKPso/KA6ZfzA==", + "iQ304I1hmLZktA1d1cuOJA==", + "0QB0OUW5x2JLHfrtmpZQ+w==", + "kgyUtd8MFe0tuuxDEUZA9w==", + "AcbG0e6xN8pZfYAv7QJe1Q==", + "bb/U8UynPHwczew/hxLQxw==", + "NuBYjwlxadAH+vLWYRZ3bg==", + "Ao1Zc0h5AdSHtYt1caWZnQ==", + "FL/j3GJBuXdAo54JYiWklQ==", + "E2v8Kk60qVpQ232YzjS2ow==", + "zVupSPz7cD0v/mD/eUIIjg==", + "sEeblUmISi1HK4omrWuPTA==", + "xQpYjaAmrQudWgsdu24J0A==", + "vCekQ2nOQKiN/q8Be/qwZg==", + "8g08gjG/QtvAYer32xgNAg==", + "miiOqnhtef1ODjFzMHnxjA==", + "sXlFMSTBFnq0STHj6cS/8w==", + "+SclwwY8R2RPrnX54Z+A6w==", + "g8TcogVxHpw7uhgNFt5VCQ==", + "9viAzLFGYYudBYFu7kFamg==", + "BAJ+/jbk2HyobezZyB9LiQ==", + "/DJgKE9ouibewuZ2QEnk6w==", + "fxg/vQq9WPpmQsqQ4RFYaA==", + "lM/EhwTsbivA7MDecaVTPw==", + "pVgjGg4TeTNhKimyOu3AAw==", + "gYnznEt9r97haD/j2Cko7g==", + "/ngbFuKIAVpdSwsA3VxvNw==", + "VCL3xfPVCL5RjihQM59fgg==", + "eDWsx4isnr2xPveBOGc7Hw==", + "FIOCTEbzb2+KMCnEdJ7jZw==", + "40HzgVKYnqIb6NJhpSIF0A==", + "ccK42Lm8Tsv73YMVZRwL6A==", + "MpAwWMt7bcs4eL7hCSLudQ==", + "zxsSqovedB3HT99jVblCnQ==", + "4erEA42TqGA9K4iFKkxMMA==", + "BaRwTrc5ulyKbW4+QqD0dw==", + "CT3ldhWpS1SEEmPtjejR/Q==", + "lkl6XkrTMUpXi46dPxTPxg==", + "3EhLkC9NqD3A6ApV6idmgg==", + "fsW2DaKYTCC7gswCT+ByQQ==", + "pW4gDKtVLj48gNz6V17QdA==", + "KjfL7YyVqmCJGBGDFdJ0gw==", + "bGGUhiG9SqJMHQWitXTcYQ==", + "8RtLlzkGEiisy1v9Xo0sbw==", + "R81DX/5a7DYKkS4CU+TL+w==", + "Tu6w6DtX2RJJ3Ym3o3QAWw==", + "nx/U4Tode5ILux4DSR+QMg==", + "mjQS8CpyGnsZIDOIEdYUxg==", + "wJpepvmtQQ3sz3tVFDnFqw==", + "a4rPqbDWiMivVzaRxvAj7g==", + "6o5g9JfKLKQ2vBPqKs6kjg==", + "UzPPFSXgeV7KW4CN5GIQXA==", + "NdVyHoTbBhX6Umz/9vbi0g==", + "Fzuq+Wg7clo6DTujNrxsSA==", + "XXFr0WUuGsH5nXPas7hR3Q==", + "JVSLiwurnCelNBiG2nflpQ==", + "NiawWuMBDo0Q3P2xK/vnLQ==", + "nNaGqigseHw30DaAhjBU3g==", + "+edqJYGvcy1AH2mEjJtSIg==", + "1WIi4I62GqkjDXOYqHWJfQ==", + "rwplpbNJz0ADUHTmzAj15Q==", + "iWNlSnwrtCmVF89B+DZqOQ==", + "tHDbi43e6k6uBgO0hA+Uiw==", + "fHNpW230mNib08aB7IM3XQ==", + "OChiB4BzcRE8Qxilu6TgJg==", + "d+ctfXU0j07rpRRzb5/HDA==", + "GDMqfhPQN0PxfJPnK1Bb9A==", + "bLd38ZNkVeuhf0joEAxnBQ==", + "nvUKoKfC6j8fz3gEDQrc/w==", + "fhcbn9xE/6zobqQ2niSBgA==", + "HGxe+5/kkh6R9GXzEOOFHA==", + "mPwCyD0yrIDonVi+fhXyEQ==", + "5PfGtbH9fmVuNnq83xIIgQ==", + "XePy/hhnQwHXFeXUQQ55Vg==", + "yfAaL0MMtSXPQ37pBdmHxQ==", + "NiQ/m4DZXUbpca9aZdzWAw==", + "uT6WRh5UpVdeABssoP2VTg==", + "oxoZP897lgMg/KLcZAtkAg==", + "oKt57TPe4PogmsGssc3Cbg==", + "RxmdoO8ak8y/HzMSIm+yBQ==", + "6leyDVmC5jglAa98NQ3+Hg==", + "+QosBAnSM2h4lsKuBlqEZw==", + "hy303iin+Wm7JA6MeelwiQ==", + "m9iuy4UtsjmyPzy6FTTZvw==", + "f6Ye5F0Lkn34uLVDCzogFQ==", + "iGykaF+h4p46HhrWqL8Ffg==", + "LPYFDbTEp5nGtG6uO8epSw==", + "t2vWMIh2BvfDSQaz5T1TZw==", + "OONAvFS/kmH7+vPhAGTNSg==", + "g/z9yk94XaeBRFj4hqPzdw==", + "2wesXiib76wM9sqRZ7JYwQ==", + "n7h9v2N1gOcvMuBEf8uThw==", + "ITYL3tDwddEdWSD6J6ULaA==", + "inrUwXyKikpOW0y2Kl1wGw==", + "iwKBOGDTFzV4aXgDGfyUkw==", + "+fcjH2kZKNj8quOytUk4nQ==", + "Srl4HivgHMxMOUHyM3jvNw==", + "qngzBJbiTB4fivrdnE5gOg==", + "G0MlFNCbRjXk4ekcPO/chQ==", + "t+bYn9UqrzKiuxAYGF7RLA==", + "RVD3Ij6sRwwxTUDAxwELtA==", + "RNdyt6ZRGvwYG5Ws3QTuEA==", + "9DRHdyX8ECKHUoEsGuqR4Q==", + "oMJLQTH1wW7LvOV0KRx/dw==", + "bjLZ7ot/X/vWSVx4EYwMCg==", + "+p8pofUlwn8vV6Rp6+sz9g==", + "cchuqe+CWCJpoakjHLvUfA==", + "NvurnIHin4O+wNP7MnrZ1w==", + "RBMv0IxXEO3o7MnV47Bzow==", + "xTizUioizbMQxD0T6fy/EQ==", + "ZCdad3AwhVArttapWFwT/Q==", + "Hy1nqC40l5ItxumkIC2LAA==", + "W/5ThNLu43uT1O+fg0Fzwg==", + "b3BQG9/9qDNC/bNSTBY/sQ==", + "neQoa8pvETr07blVMN3pgA==", + "oR8rvIZoeoaZ/ufpo0htfQ==", + "zEzWZ6l7EKoVUxvk/l78Mw==", + "IHyIeMad23fSDisblwyfpA==", + "m6srF+pMehggHB1tdoxlPg==", + "kggaIvN2tlbZdZRI8S5Apw==", + "2RFaMPlSbVuoEqKXgkIa5A==", + "//eHwmDOQRSrv+k9C/k3ZQ==", + "X/Gha4Ajjm/GStp/tv+Jvw==", + "+H0Rglt/HnhZwdty2hsDHg==", + "a1aL8zQ+ie3YPogE3hyFFg==", + "HxEU37uBMeiR5y8q/pM42g==", + "68nqDtXOuxF7DSw6muEZvg==", + "s5+78jS4hQYrFtxqTW3g1Q==", + "drfODfDI6GyMW7hzkmzQvA==", + "pT1raq2fChffFSIBX3fRiA==", + "sfowXUMdN2mCoBVrUzulZg==", + "AV/YJfdoDUdRcrXVwinhQg==", + "3AKEYQqpkfW7CZMFQZoxOw==", + "PHwJ5ZAqqftZ4ypr8H1qiQ==", + "AoN/pnK4KEUaGw4V9SFjpg==", + "soBA65OmZdfBGJkBmY/4Iw==", + "mSstwJq7IkJ0JBJ5T8xDKg==", + "h13Xuonj+0dD1xH86IhSyQ==", + "HK9xG03FjgCy8vSR+hx8+Q==", + "oFanDWdePmmZN0xqwpUukA==", + "zCRZgVsHbQZcVMHd9pGD3A==", + "EvSB+rCggob2RBeXyDQRvQ==", + "tXuu7YpZOuMLTv87NjKerA==", + "DJ+a37tCaGF5OgUhG+T0NA==", + "KkXlgPJPen6HLxbNn5llBw==", + "2W6lz1Z7PhkvObEAg2XKJw==", + "n+xYzfKmMoB3lWkdZ+D3rg==", + "CPDs+We/1wvsGdaiqxzeCQ==", + "2Wvk/kouEEOY0evUkQLhOQ==", + "ezsm4aFd6+DO9FUxz0A8Pg==", + "9sYLg75/hudZaBA3FrzKHw==", + "Pp1ZMxJ8yajdbfKM4HAQxA==", + "xiyRfVG0EfBA+rCk+tgWRQ==", + "/IarsLzJB8bf0AupJJ+/Eg==", + "LJeLdqmriyAQp+QjZGFkdQ==", + "IhHyHbHGyQS+VawxteLP0w==", + "nGzPc0kI/EduVjiK7bzM6Q==", + "m06wctjNc3o7iyBHDMZs2w==", + "mSJF9dJnxZ15lTC6ilbJ2A==", + "xdmY+qyoxxuRZa9kuNpDEg==", + "oNOI17POQCAkDwj6lJsYOA==", + "p73gSu4d+4T/ZNNkIv9Nlw==", + "vOJ55zFdgPPauPyFYBf01w==", + "4A+RHIw+aDzw0rSRYfbc7g==", + "/gi3UZmunVOIXhZSktZ8zQ==", + "a6vem8n6WmRZAalDrHNP0g==", + "kGeXrHEN6o7h5qJYcThCPw==", + "wrewZ0hoHODf7qmoGcOd7g==", + "Z0sjccxzKylgEiPCFBqPSA==", + "LKyOFgUKKGUU/PxpFYMILw==", + "L2RofFWDO0fVgSz4D2mtdw==", + "KI7tQFYW38zYHOzkKp9/lQ==", + "ewe/P3pJLYu/kMb5tpvVog==", + "IADk81pIu8NIL/+9Fi94pA==", + "0L0FVcH5Dlj3oL8+e9Na7g==", + "tdiTXKrkqxstDasT0D5BPA==", + "R906Kxp2VFVR3VD+o6Vxcw==", + "wc+8ohFWgOF4VlSYiZIGwQ==", + "wJKFMqh6MGctWfasjHrPEg==", + "UHpge5Bldt9oPGo2oxnYvQ==", + "vX7RIhatQeXAMr1+OjzhZw==", + "s2AKVTwrY65/SWqQxDGJQg==", + "Q4bfQslDSqU64MOQbBQEUw==", + "mVT74Eht+gAowINoMKV7IQ==", + "EuGWtIbyKToOe6DN3NkVpQ==", + "ALlGgVDO8So71ccX0D6u2g==", + "Rww3qkF3kWSd+AaMT0kfdw==", + "hlvtFGW8r0PkbUAYXEM+Hw==", + "Oc3BqTF3ZBW3xE0QsnFn/A==", + "3j0kFUZ6g+yeeEljx+WXGg==", + "8BLkvEkfnOizJq0OTCYGzw==", + "Lqel4GdU0ZkfoJVXI5WC/Q==", + "rvE64KQGkVkbl07y7JwBqw==", + "HbXv8InyZqFT7i3VrllBgg==", + "zwQ/3MzTJ9rfBmrANIh14w==", + "gglLMohmJDPRGMY1XKndjQ==", + "lyfqic/AbEJbCiw+wA01FA==", + "XqUO7ULEYhDOuT/I2J8BOA==", + "wPhJcp7U7IVX83szbIOOxQ==", + "1gA65t5FiBTEgMELTQFUPQ==", + "ll2M0QQzBsj5OFi02fv3Yg==", + "wt+qDLU38kzNU75ZYi3Hbw==", + "a4EYNljinYTx9vb1VvUA6A==", + "T6LA+daQqRI38iDKZTdg1A==", + "gwyVIrTk5o0YMKQq4lpJ+Q==", + "bPRX2zl+K1S0iWAWUn1DZw==", + "KQw25X4LnQ9is+qdqfxo0w==", + "6tfM6dx3R5TiVKaqYQjnCg==", + "OlwHO6Sg2zIwsCOCRu0HiQ==", + "mr1qjhliRfl87wPOrJbFQg==", + "8c+lvG5sZNimvx9NKNH3ug==", + "5Nk2Z94DhlIdfG5HNgvBbQ==", + "F50iXjRo1aSTr37GQQXuJA==", + "tfgO55QqUyayjDfQh+Zo1Q==", + "h7Fc+eT/GuC8iWI+YTD0UQ==", + "3TjntNWtpG7VqBt3729L6Q==", + "+DWs0vvFGt6d3mzdcsdsyA==", + "VJt2kPVBLEBpGpgvuv1oUw==", + "XLq/nWX8lQqjxsK9jlCqUg==", + "9s3ar9q32Y5A3tla5GW/2Q==", + "51yLpfEdvqXmtB6+q27/AQ==", + "AiMtfedwGcddA+XYNc+21g==", + "p/48hurJ1kh2FFPpyChzJg==", + "CRiL6zpjfznhGXhCIbz8pQ==", + "/jDVt9dRIn+o4IQ1DPwbsg==", + "UNdKik7Vy23LjjPzEdzNsg==", + "Koiog/hpN7ew5kgJbty34A==", + "4itEKfbRCJvqlgKnyEdIOQ==", + "zi04Yc01ZheuFAQc59E45A==", + "etRjRvfL/IwceY/IJ1tgzQ==", + "3sNJJIx1NnjYcgJhjOLJOg==", + "4yVqq66iHYQjiTSxGgX2oA==", + "Q8RVI/kRbKuXa8HAQD7zUA==", + "OERGn45uzfDfglzFFn6JAg==", + "JGEy6VP3sz3LHiyT2UwNHQ==", + "1zDfWw5LdG20ClNP1HYxgw==", + "TGB+FIzzKnouLh5bAiVOQg==", + "n5GA+pA9mO/f4RN9NL9lNg==", + "bUxQBaqKyvlSHcuRL9whjg==", + "tOdlnsE3L3XCBDJRmb/OqA==", + "XdkxmYYooeDKzy7PXVigBQ==", + "PMvG4NqJP76kMRAup6TSZA==", + "qpFJZqzkklby+u1UT3c1iA==", + "fW3QZyq5UixIA1mP6eWgqQ==", + "9nMltdrrBmM5ESBY2FRjGA==", + "1Vtrv6QUAfiYQjlLTpNovg==", + "ur9JDCVNwzSH4q4ngDlHNQ==", + "4u3eyKc+y3uRnkASrgBVUw==", + "XddlSluOH6VkR7spFIFmdQ==", + "NOmu8oZc6CcKLu+Wfz2YOQ==", + "3Ejtsqw3Iep/UQd0tXnSlg==", + "y/e3HSdg7T19FanRpJ7+7Q==", + "YodhkayN5wsgPZEYN7/KNA==", + "pZfn6IiG+V28fN8E2hawDQ==", + "jGHMJqbj6X1NdTDyWmXYAQ==", + "olTSlmirL9MFhKORiOKYkQ==", + "CrJDgdfzOea2M2hVedTrIg==", + "fpXijBOM3Ai1RkmHven5Ww==", + "eLYKLr4labZeLiRrDJ9mnA==", + "9vmJUS7WIVOlhMqwipAknQ==", + "G7J/za99BFbAZH+Q+/B8WA==", + "Hb+pdSavvJ9lUXkSVZW8Og==", + "gTB2zM3RPm27mUQRXc/YRg==", + "e5KCqQ/1GAyVMRNgQpYf6g==", + "1ApqwW7pE+XUB2Cs2M6y7g==", + "/wiA2ltAuWyBhIvQAYBTQw==", + "HFCQEiZf7/SNc+oNSkkwlA==", + "JFi6N1PlrpKaYECOnI7GFg==", + "E4ojRDwGsIiyuxBuXHsKBA==", + "+25t/2lo0FUEtWYK8LdQZQ==", + "up2MVDi9ve+s83/nwNtZ7Q==", + "cXpfd6Io6Glj2/QzrDMCvA==", + "DCvI9byhw0wOFwF1uP6xIQ==", + "PibGJQNw7VHPTgqeCzGUGA==", + "0ZRGz+oj2infCAkuKKuHiQ==", + "2QS/6OBA1T01NlIbfkTYJg==", + "P14k+fyz0TG9yIPdojp52w==", + "g5EzTJ0KA4sO3+Opss3LMg==", + "R5oOM58zdbVxFSDQnNWqeA==", + "Vg2E5qEDfC+QxZTZDCu9yQ==", + "YPgMthbpcBN2CMkugV60hQ==", + "gZWTFt5CuLqMz6OhWL+hqQ==", + "YrEP9z2WPQ8l7TY1qWncDA==", + "7p4NpnoNSQR7ISg+w+4yFg==", + "9L6yLO93sRN70+3qq3ObfA==", + "QH36wzyIhh6I56Vnx79hRA==", + "9DtM1vls4rFTdrSnQ7uWXw==", + "ZlOAnCLV1PkR0kb3E+Nfuw==", + "9UhKmKtr4vMzXTEn74BEhg==", + "Ndx5LDiVyyTz/Fh3oBTgvA==", + "mXZ4JeBwT2WJQL4a/Tm4jQ==", + "N9nD7BGEM7LDwWIMDB+rEQ==", + "dmAfbd9F0OJHRAhNMEkRsA==", + "jV/D2B11NLXZRH77sG9lBw==", + "1C50kisi9nvyVJNfq2hOEQ==", + "NMbAjbnuK7EkVeY3CQI5VA==", + "J1nYqJ7tIQK1+a/3sMXI/Q==", + "m416yrrAlv+YPClGvGh+qQ==", + "rLZII1R6EGus+tYCiUtm6g==", + "xktOghh1S9nIX6fXWnT+Ug==", + "FcFcn4qmPse5mJCX5yNlsA==", + "xAAipGfHTGTjp9Qk1MR8RQ==", + "RQOlmzHwQKFpafKPJj0D8w==", + "WRjYdKdtnd1G9e/vFXCt0g==", + "z0BU//aSjYHAkGGk3ZSGNg==", + "M55eersiJuN9v61r8DoAjQ==", + "l2mAbuFF3QBIUILDODiUHQ==", + "IhpXs1TK7itQ3uTzZPRP5Q==", + "t2EkpUsLOEOsrnep0nZSmA==", + "lMaO8Yf+6YNowGyhDkPhQA==", + "UbSFw5jtyLk5MealqJw++A==", + "5u2PdDcIY3RQgtchSGDCGg==", + "MQYM3BT77i35LG9HcqxY2Q==", + "8AfCSZC0uasVON9Y/0P2Pw==", + "evaWFoxZNQcRszIRnxqB+A==", + "+8PiQt6O7pJI/nIvQpDaAg==", + "eRwaYiog2DdlGQyaltCMJg==", + "JyUJEnU6hJu8x2NCnGrYFw==", + "l0E0U/CJsyCVSTsXW4Fp+w==", + "XV13yK0QypJXmgI+dj4KYw==", + "jrRH0aTUYCOpPLZwzwPRfQ==", + "N3YDSkBUqSmrmNvZZx4a1Q==", + "0yJ7TQYzcp3DXVSvwavr+w==", + "rhgtLQh0F9bRA6IllM7AGw==", + "IWZnTJ3Hb9qw9HAK/M9gTw==", + "izeyFvXOumNgVyLrbKW45g==", + "xYD8jrCDmuQna+p1ebnKDQ==", + "SOdpdrk2ayeyv0xWdNuy9g==", + "HYylUirJRqLm+dkp39fSOQ==", + "q4z6A4l3nhX3smTmXr+Sig==", + "Zyo0fzewcqXiKe2mAwKx5g==", + "LMEtzh0+J27+4zORfcjITw==", + "LoUv/f2lcWpjftzpdivMww==", + "mXBfDUt/sBW5OUZs2sihvw==", + "PggVPQL5YKqSU/1asihcrg==", + "mI0eT4Rlr7QerMIngcu/ng==", + "NmQrsmb8PVP05qnSulPe5Q==", + "TcyyXrSsQsnz0gJ36w4Dxw==", + "y4mfEDerrhaqApDdhP5vjA==", + "ynaj4XjU27b7XbqPyxI8Ig==", + "Ua6aO6HwM+rY4sPR19CNFA==", + "3go7bJ9WqH/PPUTjNP3q/Q==", + "n1ixvP7SfwYT3L2iWpJg6A==", + "W8y32OLHihfeV0XFw7LmOg==", + "uzkNhmo2d08tv5AmnyqkoQ==", + "hJ8leLNuJ6DK5V8scnDaZQ==", + "KodYHHN62zESrXUye7M01g==", + "H+yPRiooEh5J7lAJB4RZ7Q==", + "dZg5w8rFETMp9SgW7m0gfg==", + "LsmsPokAwWNCuC74MaqFCQ==", + "1QGhj9NONF2rC44UdO+Izw==", + "uwGivY3/C9WK+dirRPJZ4A==", + "rXGWY/Gq+ZEsmvBHUfFMmQ==", + "j4FBMnNfdBwx0VsDeTvhFg==", + "81nkjWtpBhqhvOp6K8dcWg==", + "dCDaYYrgASXPMGFRV0RCGg==", + "Kj1QI+s9261S3lTtPKd9eg==", + "LblwOqNiciHmt2NXjd89tg==", + "46piyANQVvvLqcoMq5G8tQ==", + "XJihma9zSRrXLC+T+VcFDA==", + "K3NBEG8jJTJbSrYSOC3FKw==", + "cT3PwwS6ALZA/na9NjtdzA==", + "wJ4uCrl4DPg70ltw1dZO3w==", + "JATLdpQm//SQnkyCfI5x7Q==", + "X1PaCfEDScclLtOTiF5JUw==", + "444F9T6Y7J67Y9sULG81qg==", + "8JVHFRwAd/SCLU0CRJYofg==", + "aLh1XEUrfR9W82gzusKcOg==", + "U+bB5NjFIuQr/Y5UpXHwxA==", + "Egs14xVbRWjfBBX7X5Z60g==", + "KSorNz/PLR/YYkxaj1fuqw==", + "RDgGGxTtcPvRg/5KRRlz4w==", + "5T39s5CtSrK5awMPUcEWJg==", + "+PUVXkoTqHxJHO18z4KMfw==", + "Bvk8NX4l6WktLcRDRKsK/A==", + "kNGIV3+jQmJlZDTXy1pnyA==", + "E3jMjAgXwvwR8PA53g4+PQ==", + "MbI04HlTGCoc/6WDejwtaQ==", + "aEnHUfn7UE/Euh6jsMuZ7g==", + "z4Bft++f72QeDh4PWGr/sw==", + "1lCcQWGDePPYco4vYrA5vw==", + "iu5csar0IQQBOTgw5OvJwQ==", + "raKMXnnX6PFFsbloDqyVzQ==", + "uPnL9tboMZo0Kl2fe24CmA==", + "8OFxXwnPmrogpNoueZlC4Q==", + "V6CRKrKezPwsRdbm0DJ2Yg==", + "xmGgK3W5y+oCd0K2u8XjZQ==", + "Ry3zgZ6KHrpNyb7+Tt2Pkw==", + "IwLbkL33z+LdTjaFYh93kg==", + "caepyBOAFu0MxbcXrGf6TA==", + "iIWxFdolLcnXqIjPMg+5kQ==", + "P430CeF2MDkuq11YdjvV8A==", + "yCu+DVU/ceMTOZ5h/7wQTg==", + "4mQVNv7FHj+/O6XFqWFt/Q==", + "OEJ40VmMDYzc2ESEMontRA==", + "D66Suu3tWBD+eurBpPXfjA==", + "RNK9G1hfuz3ETY/RmA9+aA==", + "BYpHADmEnzBsegdYTv8B5Q==", + "DBKrdpCE0awppxST4o/zzg==", + "KOmdvm+wJuZ/nT/o1+xOuw==", + "gDxqUdxxeXDYhJk9zcrNyA==", + "UPzS4LR3p/h0u69+7YemrQ==", + "hf9HFxWRNX2ucH8FLS7ytA==", + "ozVqYsmUueKifb4lDyVyrg==", + "TfHvdbl2M4deg65QKBTPng==", + "SzCGM8ypE58FLaR1+1ccxQ==", + "3nthUmLZ30HxQrzr2d7xFA==", + "1jBaRO8Bg5l6TH7qJ8EPiw==", + "eJlcN+gJnqAnctbWSIO9uA==", + "G8LFBop8u6IIng+gQuVg3w==", + "3JhnM6G4L06NHt31lR0zXA==", + "342VOUOxoLHUqtHANt83Hw==", + "hRxbdeniAVFgKUgB9Q3Y+g==", + "cFFE2R4GztNoftYkqalqUQ==", + "YmaksRzoU+OwlpiEaBDYaQ==", + "jon1y9yMEGfiIBjsDeeJdA==", + "oSnrpW4UmmVXtUGWqLq+tQ==", + "zaqyy3GaJ7cp8qDoLJWcTw==", + "luO1R8dUM9gy1E2lojRQoA==", + "YHM6NNHjmodv+G0mRLK7kw==", + "ZSmN8mmI9lDEHkJqBBg0Nw==", + "520wTzrysiRi2Td92Zq0HQ==", + "RAAw14BA1ws5Wu/rU7oegw==", + "vb6Agwzk4JG0Nn7qRPPFMQ==", + "joDXdLpXvRjOqkRiYaD/Sw==", + "dK2DU3t1ns+DWDwfBvH3SQ==", + "gZNJ1Qq6OcnwXqc+jXzMLQ==", + "R8ULpSNu9FcCwXZM0QedSg==", + "mc45FSMtzdw2PTcEBwHWPw==", + "d0qvm3bl38rRCpYdWqolCQ==", + "o9tdzmIu+3J/EYU4YWyTkA==", + "5eXpiczlRdmqMYSaodOUiQ==", + "KYuUNrkTvjUWQovw9dNakA==", + "02im2RooJQ/9UfUrh5LO+A==", + "kWPUUi7x9kKKa6nJ+FDR5Q==", + "6z8CRivao3IMyV4p4gMh7g==", + "SmRWEzqddY9ucGAP5jXjAg==", + "DJscTYNFPyPmTb57g/1w+Q==", + "uOHrw37yF9oLLVd16nUpeg==", + "HaIRV9SNPRTPDOSX9sK/bg==", + "K4yZNVoqHjXNhrZzz2gTew==", + "bTNRjJm+FfSQVfd56nNNqQ==", + "x5lyMArsv1MuJmEFlWCnNw==", + "cxpZ4bloGv734LBf4NpVhA==", + "kUudvRfA33uJDzHIShQd3Q==", + "3Wfj05vCLFAB9vII5AU9tw==", + "FUQySDFodnRhr+NUsWt0KA==", + "eC/RcoCVQBlXdE9WtcgXIw==", + "NoX8lkY+kd2GPuGjp+s0tQ==", + "EzjbinBHx3Wr08eXpH3HXA==", + "0VsaJHR0Ms8zegsCpAKoyg==", + "e2xLFVavnZIUUtxJx+qa1g==", + "Kt6BTG1zdeBZ3nlVk+BZKQ==", + "EUXQZwLgnDG+C8qxVoBNdw==", + "0SkC/4PtnX1bMYgD6r6CLA==", + "rzj6mjHCcMEouL66083BAg==", + "V5HEaY3v9agOhsbYOAZgJA==", + "tJt6VDdAPEemBUvnoc4viA==", + "g0lWrzEYMntVIahC7i0O2g==", + "zCpibjrZOA3FQ4lYt0WoVA==", + "4Xh/B3C16rrjbES+FM1W8g==", + "GHEdXgGWOeOa6RuPMF0xXg==", + "3kREs/qaMX0AwFXN0LO5ow==", + "GLDNTSwygNBmuFwCIm7HtA==", + "JBkbaBiorCtFq9M9lSUdMg==", + "rJCuanCy51ydVD4nInf9IQ==", + "OzFRv+PzPqTNmOnvZGoo5g==", + "7mxU5fJl/c6dXss9H3vGcQ==", + "9J53kk+InE3CKa7cPyCXMw==", + "x9TIZ9Ua++3BX+MpjgTuWA==", + "h0MH5NGFfChgmRJ3E/R3HQ==", + "25w3ZRUzCvJwAVHYCIO5uw==", + "1Wc8jQlDSB4Dp32wkL2odw==", + "ipPPjxpXHS1tcykXmrHPMQ==", + "r95wJtP5rsTExKMS7QhHcw==", + "TZT86wXfzFffjt0f95UF5w==", + "VpmBstwR7qPVqPgKYQTA3g==", + "3++dZXzZ6AFEz7hK+i5hww==", + "mAiD16zf+rCc7Qzxjd5buA==", + "1JI9bT92UzxI8txjhst9LQ==", + "TNyvLixb03aP2f8cDozzfA==", + "spHVvA/pc7nF9Q4ON020+w==", + "GA8k6GQ20DGduVoC+gieRA==", + "T7waQc3PvTFr0yWGKmFQdQ==", + "P0Pc8owrqt6spdf7FgBFSw==", + "DKApp/alXiaPSRNm3MfSuA==", + "UreSZCIdDgloih8KLeX7gg==", + "xJi0T+psHOXMivSOVpMWeQ==", + "cNsC9bH30eM1EZS6IdEdtQ==", + "XjjrIpsmATV/lyln4tPb+g==", + "qt5CsMts2aD4lw/4Q6bHYQ==", + "h+KRDKIvyVUBmRjv1LcCyg==", + "2j83jrPwPfYlpJJ2clEBYQ==", + "ZrCezGLz38xKmzAom6yCTQ==", + "SEGu+cSbeeeZg4xWwsSErQ==", + "Duz/8Ebbd0w6oHwOs0Wnwg==", + "Ci7sS7Yi1+IwAM3VMAB4ew==", + "DG2Qe2DqPs5MkZPOqX363Q==", + "v0Bvws1WYVoEgDt8xmVKew==", + "CtDj/h2Q/lRey20G8dzSgA==", + "WRoJMO0BCJyn5V6qnpUi4Q==", + "RQywrOLZEKw9+kG6qTzr3g==", + "mU4CqbAwpwqegxJaOz9ofQ==", + "aN5x46Gw1VihRalwCt1CGg==", + "U6VQghxOXsydh3Naa5Nz4A==", + "YA+zdEC+yEgFWRIgS1Eiqw==", + "oPcxgoismve6+jXyIKK6AQ==", + "PqLCd/pwc+q5GkL6MB0jTg==", + "fHL+fHtDxhALZFb9W/uHuw==", + "dhTevyxTYAuKbdLWhG47Kw==", + "VllbOAjeW3Dpbj5lp2OSmA==", + "3itfXtlLPRmPCSYaSvc39Q==", + "GNak/LFeoHWlTdLW1iU4eg==", + "HuDuxs2KiGqmeyY1s1PjpQ==", + "xs8J3cesq7lDhP/dNltqOw==", + "foXSDEUwMhfHWJSmSejsQg==", + "6fWom3YoKvW6NIg6y9o9CQ==", + "NhZbSq0CjDNOAIvBHBM9zA==", + "5w4FbRhWACP7k2WnNitiHg==", + "0UeRwDID2RBIikInqFI7uw==", + "/y/jHHEpUu5TR+R2o96kXA==", + "voO3krg4sdy4Iu+MZEr8+g==", + "hdzol5dk//Q6tCm4+OndIA==", + "Nc5kiwXCAyjpzt43G5RF1A==", + "3UBYBMejKInSbCHRoJJ7dg==", + "dRFCIbVu0Y8XbjG5i+UFCQ==", + "t8pjhdyNJirkvYgWIO/eKg==", + "FAXzjjIr8l1nsQFPpgxM/g==", + "SPGpjEJrpflv1hF0qsFlPw==", + "9Y1ZmfiHJd9vCiZ6KfO1xQ==", + "7Eqzyb+Kep+dIahYJWNNxQ==", + "9rL8nC/VbSqrvnUtH9WsxQ==", + "H4FZ5Wcnb40hQM1DMGGe8A==", + "AjoXWGb/l9xH/hscgEc6kQ==", + "6nzFl41uutgDdC30oOeCqg==", + "3jo1jRy3MybXtoLR+JIbJw==", + "mXdE08dv+OlIhlcqMBH2Gg==", + "Ifd7DI6o8N5gnyAKqZTlRw==", + "JNUvg/kxL3rdcZnD4IqUxw==", + "ry8B+sAHNeFIZHCCDynFyw==", + "TXaEd5lIKhzjcncfNcBgSg==", + "Mr3ehuDMUimOSn+FlkchdA==", + "cwiGhjmX9v8I7E/ekQ0h+g==", + "I/r5+1jnqumCPprKC/2BqA==", + "S4V3MfGYk8I4fd3WH09yYw==", + "A+crVyUeynAkEMYKbnFjZw==", + "vtyHcNQPcUTRuZcQvRUX4Q==", + "UNKx1ZVv3HNp21zrUSm6ew==", + "rsAlvGLv2D0swd6ol3WlvA==", + "2qwqb8ENAR2fpQnw55sPDw==", + "xBJJuYYnsTJOeFggZSKC4Q==", + "omvtZZKruPiEt6fV0YXTdg==", + "JZEgKUhUN+USJsvtF4HZOg==", + "euG/kpJ5elSDOGNbWWDfNQ==", + "DiiVmM6/WNcp0MUjSaFq6w==", + "QCNS8gAml1M2pJ+MxZsueg==", + "M6+pggFsHfM3alFxcMOFNQ==", + "YLoWpDTwXnszEQm8FA164Q==", + "N08oUZtlXbQvO9t3vXnGog==", + "jkjuJowWuOa4CLY+RZiErQ==", + "mPf+S+6oAoVIYEVveaiNFA==", + "R0iVyo5qreP/68uZlZphDA==", + "GYlqhQgp03B0mXpUhQ+ZCA==", + "lQNbmWD7PhwNGye+zbc3GQ==", + "cNeaOJEOzUSDdRmenPQyuw==", + "Gp66/Txv6ebv5bn85TuQtA==", + "xAda6DVkcvvqhI8vWZeGyA==", + "Ggk1Qa0lEdAgCXG6SmCkBA==", + "MYuO7ZURXtyaf56q7hH4Zw==", + "RUIdZRTgJBudWUZQFgiFaQ==", + "bgFJxLirUom2zT0h7LdOpw==", + "A2gaOpIlrS7TKVQgy9XMSw==", + "zevXp0lqqnXv9X6Bgmjtqg==", + "a5iuFqWAdFFsRgp7SFYwNg==", + "TxTy0TaDsWTcRH3wdBEQLQ==", + "jephVdKDeJIhXPrdMOJ4qA==", + "C4KdamfqUPuJ3RGFdpIEdw==", + "zl6l2Ioz1qovRUIWrSyxVA==", + "+gGaDxUe0UnNrf3PPg1qQQ==", + "1HgbrlaLMHS6Qj/0kkaJxg==", + "eGxTly6Pnu7eV/MKYMmuYw==", + "RAMKfnlrzNjpyh2BWt6JHg==", + "4pZQm9ogCZ/EAR9pjJm1eA==", + "l1zv3erwXIegQFd02NlCag==", + "uHGyRZchuA4ulmuD5LqquQ==", + "/vFu89tsV+lbcoiqM/XWog==", + "63SUgqfQimrmjvy/bEDQ0w==", + "JLHuf+FlChFDa9LYfTQ4Eg==", + "I+ZnPePTFX8ZODe14bxgyA==", + "CtoK1k3U82BkvzuPfQ4pjQ==", + "6nqQm4C7y+wZ+qX0kVjwmA==", + "+C3kBxRXIjqBk0EJxe3Xfg==", + "qVu748pIxEZtiywg4/4qhw==", + "07o+sKjjRCYkwy/ACyoYhg==", + "CiLF4dkbLURekBcQbwPUVA==", + "W/N5/nkp4iQIPYfAagVV7A==", + "3PJOphhEjw0E4arTfVVwdg==", + "YdMbARHwB+bSOd0PlTlXiA==", + "41hbx5Yr7UWxsV6+bWUYUA==", + "SqJHXD0MorNwHtHL9TbWLg==", + "pWKGUzm/muwOiBtzkRMnRg==", + "az9zZ7HTa4FJGRQMcamvEw==", + "zavAAN8C9Wo8oBLyztp63Q==", + "yBAnPmwrMJ8kpPP292S/Lw==", + "E6szQhjuUAz2e0h9ffQfEQ==", + "Fs3cQxQyS9kM4T8j5R7rWw==", + "GB5fRLZxnjRUfEe0SwcePQ==", + "+9OY8xkT9dM/rb2T6ACtOQ==", + "If2xFBD1p91iDD7ZrsfgjA==", + "QCFfoMhy8EleZAOpfRY88w==", + "NobWPk1Z6bHt5s9NHXt/pg==", + "nK6T4vV4384OIcqO5tQMhA==", + "Zov1EzK+VomiuwT1+ulQ8g==", + "pF98OKDvLUlnTzo7wmlpOw==", + "Wrq9YDsieAMC3Y2DSY5Rcg==" + ] +} diff --git a/browser/base/content/newtab/newTab.js b/browser/base/content/newtab/newTab.js new file mode 100644 index 000000000..bbd2ef39d --- /dev/null +++ b/browser/base/content/newtab/newTab.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var Cu = Components.utils; +var Ci = Components.interfaces; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/PageThumbs.jsm"); +Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); +Cu.import("resource:///modules/DirectoryLinksProvider.jsm"); +Cu.import("resource://gre/modules/NewTabUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Rect", + "resource://gre/modules/Geometry.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", + "resource://gre/modules/PrivateBrowsingUtils.jsm"); + +var { + links: gLinks, + allPages: gAllPages, + linkChecker: gLinkChecker, + pinnedLinks: gPinnedLinks, + blockedLinks: gBlockedLinks, + gridPrefs: gGridPrefs +} = NewTabUtils; + +XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { + return Services.strings. + createBundle("chrome://browser/locale/newTab.properties"); +}); + +function newTabString(name, args) { + let stringName = "newtab." + name; + if (!args) { + return gStringBundle.GetStringFromName(stringName); + } + return gStringBundle.formatStringFromName(stringName, args, args.length); +} + +function inPrivateBrowsingMode() { + return PrivateBrowsingUtils.isContentWindowPrivate(window); +} + +const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; +const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-tiles-work-firefox"; +const TILES_INTRO_LINK = "https://www.mozilla.org/firefox/tiles/"; +const TILES_PRIVACY_LINK = "https://www.mozilla.org/privacy/"; + +#include transformations.js +#include page.js +#include grid.js +#include cells.js +#include sites.js +#include drag.js +#include dragDataHelper.js +#include drop.js +#include dropTargetShim.js +#include dropPreview.js +#include updater.js +#include undo.js +#include search.js +#include customize.js + +// Everything is loaded. Initialize the New Tab Page. +gPage.init(); diff --git a/browser/base/content/newtab/newTab.xhtml b/browser/base/content/newtab/newTab.xhtml new file mode 100644 index 000000000..07fb0093e --- /dev/null +++ b/browser/base/content/newtab/newTab.xhtml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html [ + <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd"> + %newTabDTD; + <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> + %browserDTD; + <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> + %globalDTD; +]> + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>&newtab.pageTitle;</title> + + <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/" /> + <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/contentSearchUI.css" /> + <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/newtab/newTab.css" /> + <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/newtab/newTab.css" /> +</head> + +<body dir="&locale.dir;"> + <div id="newtab-customize-overlay"></div> + + <div class="newtab-customize-panel-container"> + <div id="newtab-customize-panel" orient="vertical"> + <div id="newtab-customize-panel-anchor"></div> + <div id="newtab-customize-panel-inner-wrapper"> + <div id="newtab-customize-title" class="newtab-customize-panel-item"> + <label>&newtab.customize.cog.title2;</label> + </div> + + <div class="newtab-customize-complex-option"> + <div id="newtab-customize-classic" class="newtab-customize-panel-superitem newtab-customize-panel-item selectable"> + <label>&newtab.customize.classic;</label> + </div> + <div id="newtab-customize-enhanced" class="newtab-customize-panel-subitem"> + <label class="checkbox"></label> + <label>&newtab.customize.cog.enhanced;</label> + </div> + </div> + <div id="newtab-customize-blank" class="newtab-customize-panel-item selectable"> + <label>&newtab.customize.blank2;</label> + </div> + <div id="newtab-customize-learn" class="newtab-customize-panel-item"> + <label>&newtab.customize.cog.learn;</label> + </div> + </div> + </div> + </div> + + <div id="newtab-vertical-margin"> + <div id="newtab-margin-top"/> + + <div id="newtab-margin-undo-container"> + <div id="newtab-undo-container" undo-disabled="true"> + <label id="newtab-undo-label">&newtab.undo.removedLabel;</label> + <button id="newtab-undo-button" tabindex="-1" + class="newtab-undo-button">&newtab.undo.undoButton;</button> + <button id="newtab-undo-restore-button" tabindex="-1" + class="newtab-undo-button">&newtab.undo.restoreButton;</button> + <button id="newtab-undo-close-button" tabindex="-1" title="&newtab.undo.closeTooltip;"/> + </div> + </div> + + <div id="newtab-search-container"> + <div id="newtab-search-form"> + <div id="newtab-search-icon"/> + <input type="text" name="q" value="" id="newtab-search-text" + aria-label="&contentSearchInput.label;" maxlength="256"/> + <input id="newtab-search-submit" type="button" + title="&contentSearchSubmit.tooltip;"/> + </div> + </div> + + <div id="newtab-horizontal-margin"> + <div class="newtab-side-margin"/> + <div id="newtab-grid"> + <h1 id="topsites-heading"/> + </div> + <div class="newtab-side-margin"/> + </div> + + <div id="newtab-margin-bottom"/> + </div> + <input id="newtab-customize-button" type="button" dir="&locale.dir;" + value="⚙" + title="&newtab.customize.title;"/> +</body> +<script type="text/javascript;version=1.8" src="chrome://browser/content/contentSearchUI.js"/> +<script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/> +</html> diff --git a/browser/base/content/newtab/page.js b/browser/base/content/newtab/page.js new file mode 100644 index 000000000..f7626ced2 --- /dev/null +++ b/browser/base/content/newtab/page.js @@ -0,0 +1,297 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +// The amount of time we wait while coalescing updates for hidden pages. +const SCHEDULE_UPDATE_TIMEOUT_MS = 1000; + +/** + * This singleton represents the whole 'New Tab Page' and takes care of + * initializing all its components. + */ +var gPage = { + /** + * Initializes the page. + */ + init: function Page_init() { + // Add ourselves to the list of pages to receive notifications. + gAllPages.register(this); + + // Listen for 'unload' to unregister this page. + addEventListener("unload", this, false); + + // XXX bug 991111 - Not all click events are correctly triggered when + // listening from xhtml nodes -- in particular middle clicks on sites, so + // listen from the xul window and filter then delegate + addEventListener("click", this, false); + + // Check if the new tab feature is enabled. + let enabled = gAllPages.enabled; + if (enabled) + this._init(); + + this._updateAttributes(enabled); + + // Initialize customize controls. + gCustomize.init(); + }, + + /** + * Listens for notifications specific to this page. + */ + observe: function Page_observe(aSubject, aTopic, aData) { + if (aTopic == "nsPref:changed") { + gCustomize.updateSelected(); + + let enabled = gAllPages.enabled; + this._updateAttributes(enabled); + + // Update thumbnails to the new enhanced setting + if (aData == "browser.newtabpage.enhanced") { + this.update(); + } + + // Initialize the whole page if we haven't done that, yet. + if (enabled) { + this._init(); + } else { + gUndoDialog.hide(); + } + } else if (aTopic == "page-thumbnail:create" && gGrid.ready) { + for (let site of gGrid.sites) { + if (site && site.url === aData) { + site.refreshThumbnail(); + } + } + } + }, + + /** + * Updates the page's grid right away for visible pages. If the page is + * currently hidden, i.e. in a background tab or in the preloader, then we + * batch multiple update requests and refresh the grid once after a short + * delay. Accepts a single parameter the specifies the reason for requesting + * a page update. The page may decide to delay or prevent a requested updated + * based on the given reason. + */ + update(reason = "") { + // Update immediately if we're visible. + if (!document.hidden) { + // Ignore updates where reason=links-changed as those signal that the + // provider's set of links changed. We don't want to update visible pages + // in that case, it is ok to wait until the user opens the next tab. + if (reason != "links-changed" && gGrid.ready) { + gGrid.refresh(); + } + + return; + } + + // Bail out if we scheduled before. + if (this._scheduleUpdateTimeout) { + return; + } + + this._scheduleUpdateTimeout = setTimeout(() => { + // Refresh if the grid is ready. + if (gGrid.ready) { + gGrid.refresh(); + } + + this._scheduleUpdateTimeout = null; + }, SCHEDULE_UPDATE_TIMEOUT_MS); + }, + + /** + * Internally initializes the page. This runs only when/if the feature + * is/gets enabled. + */ + _init: function Page_init() { + if (this._initialized) + return; + + this._initialized = true; + + // Set submit button label for when CSS background are disabled (e.g. + // high contrast mode). + document.getElementById("newtab-search-submit").value = + document.body.getAttribute("dir") == "ltr" ? "\u25B6" : "\u25C0"; + + if (Services.prefs.getBoolPref("browser.newtabpage.compact")) { + document.body.classList.add("compact"); + } + + // Initialize search. + gSearch.init(); + + if (document.hidden) { + addEventListener("visibilitychange", this); + } else { + setTimeout(() => this.onPageFirstVisible()); + } + + // Initialize and render the grid. + gGrid.init(); + + // Initialize the drop target shim. + gDropTargetShim.init(); + +#ifdef XP_MACOSX + // Workaround to prevent a delay on MacOSX due to a slow drop animation. + document.addEventListener("dragover", this, false); + document.addEventListener("drop", this, false); +#endif + }, + + /** + * Updates the 'page-disabled' attributes of the respective DOM nodes. + * @param aValue Whether the New Tab Page is enabled or not. + */ + _updateAttributes: function Page_updateAttributes(aValue) { + // Set the nodes' states. + let nodeSelector = "#newtab-grid, #newtab-search-container"; + for (let node of document.querySelectorAll(nodeSelector)) { + if (aValue) + node.removeAttribute("page-disabled"); + else + node.setAttribute("page-disabled", "true"); + } + + // Enables/disables the control and link elements. + let inputSelector = ".newtab-control, .newtab-link"; + for (let input of document.querySelectorAll(inputSelector)) { + if (aValue) + input.removeAttribute("tabindex"); + else + input.setAttribute("tabindex", "-1"); + } + }, + + /** + * Handles unload event + */ + _handleUnloadEvent: function Page_handleUnloadEvent() { + gAllPages.unregister(this); + // compute page life-span and send telemetry probe: using milli-seconds will leave + // many low buckets empty. Instead we use half-second precision to make low end + // of histogram linear and not lose the change in user attention + let delta = Math.round((Date.now() - this._firstVisibleTime) / 500); + if (this._suggestedTilePresent) { + Services.telemetry.getHistogramById("NEWTAB_PAGE_LIFE_SPAN_SUGGESTED").add(delta); + } + else { + Services.telemetry.getHistogramById("NEWTAB_PAGE_LIFE_SPAN").add(delta); + } + }, + + /** + * Handles all page events. + */ + handleEvent: function Page_handleEvent(aEvent) { + switch (aEvent.type) { + case "load": + this.onPageVisibleAndLoaded(); + break; + case "unload": + this._handleUnloadEvent(); + break; + case "click": + let {button, target} = aEvent; + // Go up ancestors until we find a Site or not + while (target) { + if (target.hasOwnProperty("_newtabSite")) { + target._newtabSite.onClick(aEvent); + break; + } + target = target.parentNode; + } + break; + case "dragover": + if (gDrag.isValid(aEvent) && gDrag.draggedSite) + aEvent.preventDefault(); + break; + case "drop": + if (gDrag.isValid(aEvent) && gDrag.draggedSite) { + aEvent.preventDefault(); + aEvent.stopPropagation(); + } + break; + case "visibilitychange": + // Cancel any delayed updates for hidden pages now that we're visible. + if (this._scheduleUpdateTimeout) { + clearTimeout(this._scheduleUpdateTimeout); + this._scheduleUpdateTimeout = null; + + // An update was pending so force an update now. + this.update(); + } + + setTimeout(() => this.onPageFirstVisible()); + removeEventListener("visibilitychange", this); + break; + } + }, + + onPageFirstVisible: function () { + // Record another page impression. + Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true); + + for (let site of gGrid.sites) { + if (site) { + // The site may need to modify and/or re-render itself if + // something changed after newtab was created by preloader. + // For example, the suggested tile endTime may have passed. + site.onFirstVisible(); + } + } + + // save timestamp to compute page life-span delta + this._firstVisibleTime = Date.now(); + + if (document.readyState == "complete") { + this.onPageVisibleAndLoaded(); + } else { + addEventListener("load", this); + } + }, + + onPageVisibleAndLoaded() { + // Send the index of the last visible tile. + this.reportLastVisibleTileIndex(); + // Maybe tell the user they can undo an initial automigration + this.maybeShowAutoMigrationUndoNotification(); + }, + + reportLastVisibleTileIndex() { + let cwu = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + + let rect = cwu.getBoundsWithoutFlushing(gGrid.node); + let nodes = cwu.nodesFromRect(rect.left, rect.top, 0, rect.width, + rect.height, 0, true, false); + + let i = -1; + let lastIndex = -1; + let sites = gGrid.sites; + + for (let node of nodes) { + if (node.classList && node.classList.contains("newtab-cell")) { + if (sites[++i]) { + lastIndex = i; + if (sites[i].link.targetedSite) { + // record that suggested tile is shown to use suggested-tiles-histogram + this._suggestedTilePresent = true; + } + } + } + } + + DirectoryLinksProvider.reportSitesAction(sites, "view", lastIndex); + }, + + maybeShowAutoMigrationUndoNotification() { + sendAsyncMessage("NewTab:MaybeShowAutoMigrationUndoNotification"); + }, +}; diff --git a/browser/base/content/newtab/search.js b/browser/base/content/newtab/search.js new file mode 100644 index 000000000..cbbb6e243 --- /dev/null +++ b/browser/base/content/newtab/search.js @@ -0,0 +1,15 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +var gSearch = { + init: function () { + document.getElementById("newtab-search-submit") + .addEventListener("click", e => this._contentSearchController.search(e)); + let textbox = document.getElementById("newtab-search-text"); + this._contentSearchController = + new ContentSearchUIController(textbox, textbox.parentNode, "newtab", "newtab"); + }, +}; diff --git a/browser/base/content/newtab/sites.js b/browser/base/content/newtab/sites.js new file mode 100644 index 000000000..9d103ce9b --- /dev/null +++ b/browser/base/content/newtab/sites.js @@ -0,0 +1,440 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +const THUMBNAIL_PLACEHOLDER_ENABLED = + Services.prefs.getBoolPref("browser.newtabpage.thumbnailPlaceholder"); + +/** + * This class represents a site that is contained in a cell and can be pinned, + * moved around or deleted. + */ +function Site(aNode, aLink) { + this._node = aNode; + this._node._newtabSite = this; + + this._link = aLink; + + this._render(); + this._addEventHandlers(); +} + +Site.prototype = { + /** + * The site's DOM node. + */ + get node() { return this._node; }, + + /** + * The site's link. + */ + get link() { return this._link; }, + + /** + * The url of the site's link. + */ + get url() { return this.link.url; }, + + /** + * The title of the site's link. + */ + get title() { return this.link.title || this.link.url; }, + + /** + * The site's parent cell. + */ + get cell() { + let parentNode = this.node.parentNode; + return parentNode && parentNode._newtabCell; + }, + + /** + * Pins the site on its current or a given index. + * @param aIndex The pinned index (optional). + * @return true if link changed type after pin + */ + pin: function Site_pin(aIndex) { + if (typeof aIndex == "undefined") + aIndex = this.cell.index; + + this._updateAttributes(true); + let changed = gPinnedLinks.pin(this._link, aIndex); + if (changed) { + // render site again to remove suggested/sponsored tags + this._render(); + } + return changed; + }, + + /** + * Unpins the site and calls the given callback when done. + */ + unpin: function Site_unpin() { + if (this.isPinned()) { + this._updateAttributes(false); + gPinnedLinks.unpin(this._link); + gUpdater.updateGrid(); + } + }, + + /** + * Checks whether this site is pinned. + * @return Whether this site is pinned. + */ + isPinned: function Site_isPinned() { + return gPinnedLinks.isPinned(this._link); + }, + + /** + * Blocks the site (removes it from the grid) and calls the given callback + * when done. + */ + block: function Site_block() { + if (!gBlockedLinks.isBlocked(this._link)) { + gUndoDialog.show(this); + gBlockedLinks.block(this._link); + gUpdater.updateGrid(); + } + }, + + /** + * Gets the DOM node specified by the given query selector. + * @param aSelector The query selector. + * @return The DOM node we found. + */ + _querySelector: function Site_querySelector(aSelector) { + return this.node.querySelector(aSelector); + }, + + /** + * Updates attributes for all nodes which status depends on this site being + * pinned or unpinned. + * @param aPinned Whether this site is now pinned or unpinned. + */ + _updateAttributes: function (aPinned) { + let control = this._querySelector(".newtab-control-pin"); + + if (aPinned) { + this.node.setAttribute("pinned", true); + control.setAttribute("title", newTabString("unpin")); + } else { + this.node.removeAttribute("pinned"); + control.setAttribute("title", newTabString("pin")); + } + }, + + _newTabString: function(str, substrArr) { + let regExp = /%[0-9]\$S/g; + let matches; + while ((matches = regExp.exec(str))) { + let match = matches[0]; + let index = match.charAt(1); // Get the digit in the regExp. + str = str.replace(match, substrArr[index - 1]); + } + return str; + }, + + _getSuggestedTileExplanation: function() { + let targetedName = `<strong> ${this.link.targetedName} </strong>`; + let targetedSite = `<strong> ${this.link.targetedSite} </strong>`; + if (this.link.explanation) { + return this._newTabString(this.link.explanation, [targetedName, targetedSite]); + } + return newTabString("suggested.button", [targetedName]); + }, + + /** + * Checks for and modifies link at campaign end time + */ + _checkLinkEndTime: function Site_checkLinkEndTime() { + if (this.link.endTime && this.link.endTime < Date.now()) { + let oldUrl = this.url; + // chop off the path part from url + this.link.url = Services.io.newURI(this.url, null, null).resolve("/"); + // clear supplied images - this triggers thumbnail download for new url + delete this.link.imageURI; + delete this.link.enhancedImageURI; + // remove endTime to avoid further time checks + delete this.link.endTime; + // clear enhanced-content image that may still exist in preloaded page + this._querySelector(".enhanced-content").style.backgroundImage = ""; + gPinnedLinks.replace(oldUrl, this.link); + } + }, + + /** + * Renders the site's data (fills the HTML fragment). + */ + _render: function Site_render() { + // first check for end time, as it may modify the link + this._checkLinkEndTime(); + // setup display variables + let enhanced = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link); + let url = this.url; + let title = enhanced && enhanced.title ? enhanced.title : + this.link.type == "history" ? this.link.baseDomain : + this.title; + let tooltip = (this.title == url ? this.title : this.title + "\n" + url); + + let link = this._querySelector(".newtab-link"); + link.setAttribute("title", tooltip); + link.setAttribute("href", url); + this.node.setAttribute("type", this.link.type); + + let titleNode = this._querySelector(".newtab-title"); + titleNode.textContent = title; + if (this.link.titleBgColor) { + titleNode.style.backgroundColor = this.link.titleBgColor; + } + + // remove "suggested" attribute to avoid showing "suggested" tag + // after site was pinned or dropped + this.node.removeAttribute("suggested"); + + if (this.link.targetedSite) { + if (this.node.getAttribute("type") != "sponsored") { + this._querySelector(".newtab-sponsored").textContent = + newTabString("suggested.tag"); + } + + this.node.setAttribute("suggested", true); + let explanation = this._getSuggestedTileExplanation(); + this._querySelector(".newtab-suggested").innerHTML = + `<div class='newtab-suggested-bounds'> ${explanation} </div>`; + } + + if (this.isPinned()) + this._updateAttributes(true); + // Capture the page if the thumbnail is missing, which will cause page.js + // to be notified and call our refreshThumbnail() method. + this.captureIfMissing(); + // but still display whatever thumbnail might be available now. + this.refreshThumbnail(); + }, + + /** + * Called when the site's tab becomes visible for the first time. + * Since the newtab may be preloaded long before it's displayed, + * check for changed conditions and re-render if needed + */ + onFirstVisible: function Site_onFirstVisible() { + if (this.link.endTime && this.link.endTime < Date.now()) { + // site needs to change landing url and background image + this._render(); + } + else { + this.captureIfMissing(); + } + }, + + /** + * Captures the site's thumbnail in the background, but only if there's no + * existing thumbnail and the page allows background captures. + */ + captureIfMissing: function Site_captureIfMissing() { + if (!document.hidden && !this.link.imageURI) { + BackgroundPageThumbs.captureIfMissing(this.url); + } + }, + + /** + * Refreshes the thumbnail for the site. + */ + refreshThumbnail: function Site_refreshThumbnail() { + // Only enhance tiles if that feature is turned on + let link = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link) || + this.link; + + let thumbnail = this._querySelector(".newtab-thumbnail.thumbnail"); + if (link.bgColor) { + thumbnail.style.backgroundColor = link.bgColor; + } + let uri = link.imageURI || PageThumbs.getThumbnailURL(this.url); + thumbnail.style.backgroundImage = 'url("' + uri + '")'; + + if (THUMBNAIL_PLACEHOLDER_ENABLED && + link.type == "history" && + link.baseDomain) { + let placeholder = this._querySelector(".newtab-thumbnail.placeholder"); + let charCodeSum = 0; + for (let c of link.baseDomain) { + charCodeSum += c.charCodeAt(0); + } + const COLORS = 16; + let hue = Math.round((charCodeSum % COLORS) / COLORS * 360); + placeholder.style.backgroundColor = "hsl(" + hue + ",80%,40%)"; + placeholder.textContent = link.baseDomain.substr(0,1).toUpperCase(); + } + + if (link.enhancedImageURI) { + let enhanced = this._querySelector(".enhanced-content"); + enhanced.style.backgroundImage = 'url("' + link.enhancedImageURI + '")'; + + if (this.link.type != link.type) { + this.node.setAttribute("type", "enhanced"); + this.enhancedId = link.directoryId; + } + } + }, + + _ignoreHoverEvents: function(element) { + element.addEventListener("mouseover", () => { + this.cell.node.setAttribute("ignorehover", "true"); + }); + element.addEventListener("mouseout", () => { + this.cell.node.removeAttribute("ignorehover"); + }); + }, + + /** + * Adds event handlers for the site and its buttons. + */ + _addEventHandlers: function Site_addEventHandlers() { + // Register drag-and-drop event handlers. + this._node.addEventListener("dragstart", this, false); + this._node.addEventListener("dragend", this, false); + this._node.addEventListener("mouseover", this, false); + + // Specially treat the sponsored icon & suggested explanation + // text to prevent regular hover effects + let sponsored = this._querySelector(".newtab-sponsored"); + let suggested = this._querySelector(".newtab-suggested"); + this._ignoreHoverEvents(sponsored); + this._ignoreHoverEvents(suggested); + }, + + /** + * Speculatively opens a connection to the current site. + */ + _speculativeConnect: function Site_speculativeConnect() { + let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect); + let uri = Services.io.newURI(this.url, null, null); + try { + // This can throw for certain internal URLs, when they wind up in + // about:newtab. Be sure not to propagate the error. + sc.speculativeConnect(uri, null); + } catch (e) {} + }, + + /** + * Record interaction with site using telemetry. + */ + _recordSiteClicked: function Site_recordSiteClicked(aIndex) { + if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") || + Services.prefs.prefHasUserValue("browser.newtabpage.columns") || + aIndex > 8) { + // We only want to get indices for the default configuration, everything + // else goes in the same bucket. + aIndex = 9; + } + Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED") + .add(aIndex); + }, + + _toggleLegalText: function(buttonClass, explanationTextClass) { + let button = this._querySelector(buttonClass); + if (button.hasAttribute("active")) { + let explain = this._querySelector(explanationTextClass); + explain.parentNode.removeChild(explain); + + button.removeAttribute("active"); + } + else { + let explain = document.createElementNS(HTML_NAMESPACE, "div"); + explain.className = explanationTextClass.slice(1); // Slice off the first character, '.' + this.node.appendChild(explain); + + let link = '<a href="' + TILES_EXPLAIN_LINK + '">' + + newTabString("learn.link") + "</a>"; + let type = (this.node.getAttribute("suggested") && this.node.getAttribute("type") == "affiliate") ? + "suggested" : this.node.getAttribute("type"); + let icon = '<input type="button" class="newtab-control newtab-' + + (type == "enhanced" ? "customize" : "control-block") + '"/>'; + explain.innerHTML = newTabString(type + (type == "sponsored" ? ".explain2" : ".explain"), [icon, link]); + + button.setAttribute("active", "true"); + } + }, + + /** + * Handles site click events. + */ + onClick: function Site_onClick(aEvent) { + let action; + let pinned = this.isPinned(); + let tileIndex = this.cell.index; + let {button, target} = aEvent; + + // Handle tile/thumbnail link click + if (target.classList.contains("newtab-link") || + target.parentElement.classList.contains("newtab-link")) { + // Record for primary and middle clicks + if (button == 0 || button == 1) { + this._recordSiteClicked(tileIndex); + action = "click"; + } + } + // Handle sponsored explanation link click + else if (target.parentElement.classList.contains("sponsored-explain")) { + action = "sponsored_link"; + } + else if (target.parentElement.classList.contains("suggested-explain")) { + action = "suggested_link"; + } + // Only handle primary clicks for the remaining targets + else if (button == 0) { + aEvent.preventDefault(); + if (target.classList.contains("newtab-control-block")) { + // Notify DirectoryLinksProvider of suggested tile block, this may + // affect if and how suggested tiles are recommended and needs to + // be reported before pages are updated inside block() call + if (this.link.targetedSite) { + DirectoryLinksProvider.handleSuggestedTileBlock(); + } + this.block(); + action = "block"; + } + else if (target.classList.contains("sponsored-explain") || + target.classList.contains("newtab-sponsored")) { + this._toggleLegalText(".newtab-sponsored", ".sponsored-explain"); + action = "sponsored"; + } + else if (pinned && target.classList.contains("newtab-control-pin")) { + this.unpin(); + action = "unpin"; + } + else if (!pinned && target.classList.contains("newtab-control-pin")) { + if (this.pin()) { + // suggested link has changed - update rest of the pages + gAllPages.update(gPage); + } + action = "pin"; + } + } + + // Report all link click actions + if (action) { + DirectoryLinksProvider.reportSitesAction(gGrid.sites, action, tileIndex); + } + }, + + /** + * Handles all site events. + */ + handleEvent: function Site_handleEvent(aEvent) { + switch (aEvent.type) { + case "mouseover": + this._node.removeEventListener("mouseover", this, false); + this._speculativeConnect(); + break; + case "dragstart": + gDrag.start(this, aEvent); + break; + case "dragend": + gDrag.end(this, aEvent); + break; + } + } +}; diff --git a/browser/base/content/newtab/transformations.js b/browser/base/content/newtab/transformations.js new file mode 100644 index 000000000..f7db0ad84 --- /dev/null +++ b/browser/base/content/newtab/transformations.js @@ -0,0 +1,270 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * This singleton allows to transform the grid by repositioning a site's node + * in the DOM and by showing or hiding the node. It additionally provides + * convenience methods to work with a site's DOM node. + */ +var gTransformation = { + /** + * Returns the width of the left and top border of a cell. We need to take it + * into account when measuring and comparing site and cell positions. + */ + get _cellBorderWidths() { + let cstyle = window.getComputedStyle(gGrid.cells[0].node, null); + let widths = { + left: parseInt(cstyle.getPropertyValue("border-left-width")), + top: parseInt(cstyle.getPropertyValue("border-top-width")) + }; + + // Cache this value, overwrite the getter. + Object.defineProperty(this, "_cellBorderWidths", + {value: widths, enumerable: true}); + + return widths; + }, + + /** + * Gets a DOM node's position. + * @param aNode The DOM node. + * @return A Rect instance with the position. + */ + getNodePosition: function Transformation_getNodePosition(aNode) { + let {left, top, width, height} = aNode.getBoundingClientRect(); + return new Rect(left + scrollX, top + scrollY, width, height); + }, + + /** + * Fades a given node from zero to full opacity. + * @param aNode The node to fade. + * @param aCallback The callback to call when finished. + */ + fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) { + this._setNodeOpacity(aNode, 1, function () { + // Clear the style property. + aNode.style.opacity = ""; + + if (aCallback) + aCallback(); + }); + }, + + /** + * Fades a given node from full to zero opacity. + * @param aNode The node to fade. + * @param aCallback The callback to call when finished. + */ + fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) { + this._setNodeOpacity(aNode, 0, aCallback); + }, + + /** + * Fades a given site from zero to full opacity. + * @param aSite The site to fade. + * @param aCallback The callback to call when finished. + */ + showSite: function Transformation_showSite(aSite, aCallback) { + this.fadeNodeIn(aSite.node, aCallback); + }, + + /** + * Fades a given site from full to zero opacity. + * @param aSite The site to fade. + * @param aCallback The callback to call when finished. + */ + hideSite: function Transformation_hideSite(aSite, aCallback) { + this.fadeNodeOut(aSite.node, aCallback); + }, + + /** + * Allows to set a site's position. + * @param aSite The site to re-position. + * @param aPosition The desired position for the given site. + */ + setSitePosition: function Transformation_setSitePosition(aSite, aPosition) { + let style = aSite.node.style; + let {top, left} = aPosition; + + style.top = top + "px"; + style.left = left + "px"; + }, + + /** + * Freezes a site in its current position by positioning it absolute. + * @param aSite The site to freeze. + */ + freezeSitePosition: function Transformation_freezeSitePosition(aSite) { + if (this._isFrozen(aSite)) + return; + + let style = aSite.node.style; + let comp = getComputedStyle(aSite.node, null); + style.width = comp.getPropertyValue("width"); + style.height = comp.getPropertyValue("height"); + + aSite.node.setAttribute("frozen", "true"); + this.setSitePosition(aSite, this.getNodePosition(aSite.node)); + }, + + /** + * Unfreezes a site by removing its absolute positioning. + * @param aSite The site to unfreeze. + */ + unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) { + if (!this._isFrozen(aSite)) + return; + + let style = aSite.node.style; + style.left = style.top = style.width = style.height = ""; + aSite.node.removeAttribute("frozen"); + }, + + /** + * Slides the given site to the target node's position. + * @param aSite The site to move. + * @param aTarget The slide target. + * @param aOptions Set of options (see below). + * unfreeze - unfreeze the site after sliding + * callback - the callback to call when finished + */ + slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) { + let currentPosition = this.getNodePosition(aSite.node); + let targetPosition = this.getNodePosition(aTarget.node) + let callback = aOptions && aOptions.callback; + + let self = this; + + function finish() { + if (aOptions && aOptions.unfreeze) + self.unfreezeSitePosition(aSite); + + if (callback) + callback(); + } + + // We need to take the width of a cell's border into account. + targetPosition.left += this._cellBorderWidths.left; + targetPosition.top += this._cellBorderWidths.top; + + // Nothing to do here if the positions already match. + if (currentPosition.left == targetPosition.left && + currentPosition.top == targetPosition.top) { + finish(); + } else { + this.setSitePosition(aSite, targetPosition); + this._whenTransitionEnded(aSite.node, ["left", "top"], finish); + } + }, + + /** + * Rearranges a given array of sites and moves them to their new positions or + * fades in/out new/removed sites. + * @param aSites An array of sites to rearrange. + * @param aOptions Set of options (see below). + * unfreeze - unfreeze the site after rearranging + * callback - the callback to call when finished + */ + rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) { + let batch = []; + let cells = gGrid.cells; + let callback = aOptions && aOptions.callback; + let unfreeze = aOptions && aOptions.unfreeze; + + aSites.forEach(function (aSite, aIndex) { + // Do not re-arrange empty cells or the dragged site. + if (!aSite || aSite == gDrag.draggedSite) + return; + + batch.push(new Promise(resolve => { + if (!cells[aIndex]) { + // The site disappeared from the grid, hide it. + this.hideSite(aSite, resolve); + } else if (this._getNodeOpacity(aSite.node) != 1) { + // The site disappeared before but is now back, show it. + this.showSite(aSite, resolve); + } else { + // The site's position has changed, move it around. + this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: resolve}); + } + })); + }, this); + + if (callback) { + Promise.all(batch).then(callback); + } + }, + + /** + * Listens for the 'transitionend' event on a given node and calls the given + * callback. + * @param aNode The node that is transitioned. + * @param aProperties The properties we'll wait to be transitioned. + * @param aCallback The callback to call when finished. + */ + _whenTransitionEnded: + function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) { + + let props = new Set(aProperties); + aNode.addEventListener("transitionend", function onEnd(e) { + if (props.has(e.propertyName)) { + aNode.removeEventListener("transitionend", onEnd); + aCallback(); + } + }); + }, + + /** + * Gets a given node's opacity value. + * @param aNode The node to get the opacity value from. + * @return The node's opacity value. + */ + _getNodeOpacity: function Transformation_getNodeOpacity(aNode) { + let cstyle = window.getComputedStyle(aNode, null); + return cstyle.getPropertyValue("opacity"); + }, + + /** + * Sets a given node's opacity. + * @param aNode The node to set the opacity value for. + * @param aOpacity The opacity value to set. + * @param aCallback The callback to call when finished. + */ + _setNodeOpacity: + function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) { + + if (this._getNodeOpacity(aNode) == aOpacity) { + if (aCallback) + aCallback(); + } else { + if (aCallback) { + this._whenTransitionEnded(aNode, ["opacity"], aCallback); + } + + aNode.style.opacity = aOpacity; + } + }, + + /** + * Moves a site to the cell with the given index. + * @param aSite The site to move. + * @param aIndex The target cell's index. + * @param aOptions Options that are directly passed to slideSiteTo(). + */ + _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) { + this.freezeSitePosition(aSite); + this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions); + }, + + /** + * Checks whether a site is currently frozen. + * @param aSite The site to check. + * @return Whether the given site is frozen. + */ + _isFrozen: function Transformation_isFrozen(aSite) { + return aSite.node.hasAttribute("frozen"); + } +}; diff --git a/browser/base/content/newtab/undo.js b/browser/base/content/newtab/undo.js new file mode 100644 index 000000000..b856914d2 --- /dev/null +++ b/browser/base/content/newtab/undo.js @@ -0,0 +1,116 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * Dialog allowing to undo the removal of single site or to completely restore + * the grid's original state. + */ +var gUndoDialog = { + /** + * The undo dialog's timeout in miliseconds. + */ + HIDE_TIMEOUT_MS: 15000, + + /** + * Contains undo information. + */ + _undoData: null, + + /** + * Initializes the undo dialog. + */ + init: function UndoDialog_init() { + this._undoContainer = document.getElementById("newtab-undo-container"); + this._undoContainer.addEventListener("click", this, false); + this._undoButton = document.getElementById("newtab-undo-button"); + this._undoCloseButton = document.getElementById("newtab-undo-close-button"); + this._undoRestoreButton = document.getElementById("newtab-undo-restore-button"); + }, + + /** + * Shows the undo dialog. + * @param aSite The site that just got removed. + */ + show: function UndoDialog_show(aSite) { + if (this._undoData) + clearTimeout(this._undoData.timeout); + + this._undoData = { + index: aSite.cell.index, + wasPinned: aSite.isPinned(), + blockedLink: aSite.link, + timeout: setTimeout(this.hide.bind(this), this.HIDE_TIMEOUT_MS) + }; + + this._undoContainer.removeAttribute("undo-disabled"); + this._undoButton.removeAttribute("tabindex"); + this._undoCloseButton.removeAttribute("tabindex"); + this._undoRestoreButton.removeAttribute("tabindex"); + }, + + /** + * Hides the undo dialog. + */ + hide: function UndoDialog_hide() { + if (!this._undoData) + return; + + clearTimeout(this._undoData.timeout); + this._undoData = null; + this._undoContainer.setAttribute("undo-disabled", "true"); + this._undoButton.setAttribute("tabindex", "-1"); + this._undoCloseButton.setAttribute("tabindex", "-1"); + this._undoRestoreButton.setAttribute("tabindex", "-1"); + }, + + /** + * The undo dialog event handler. + * @param aEvent The event to handle. + */ + handleEvent: function UndoDialog_handleEvent(aEvent) { + switch (aEvent.target.id) { + case "newtab-undo-button": + this._undo(); + break; + case "newtab-undo-restore-button": + this._undoAll(); + break; + case "newtab-undo-close-button": + this.hide(); + break; + } + }, + + /** + * Undo the last blocked site. + */ + _undo: function UndoDialog_undo() { + if (!this._undoData) + return; + + let {index, wasPinned, blockedLink} = this._undoData; + gBlockedLinks.unblock(blockedLink); + + if (wasPinned) { + gPinnedLinks.pin(blockedLink, index); + } + + gUpdater.updateGrid(); + this.hide(); + }, + + /** + * Undo all blocked sites. + */ + _undoAll: function UndoDialog_undoAll() { + NewTabUtils.undoAll(function() { + gUpdater.updateGrid(); + this.hide(); + }.bind(this)); + } +}; + +gUndoDialog.init(); diff --git a/browser/base/content/newtab/updater.js b/browser/base/content/newtab/updater.js new file mode 100644 index 000000000..2bab74d70 --- /dev/null +++ b/browser/base/content/newtab/updater.js @@ -0,0 +1,177 @@ +#ifdef 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#endif + +/** + * This singleton provides functionality to update the current grid to a new + * set of pinned and blocked sites. It adds, moves and removes sites. + */ +var gUpdater = { + /** + * Updates the current grid according to its pinned and blocked sites. + * This removes old, moves existing and creates new sites to fill gaps. + * @param aCallback The callback to call when finished. + */ + updateGrid: function Updater_updateGrid(aCallback) { + let links = gLinks.getLinks().slice(0, gGrid.cells.length); + + // Find all sites that remain in the grid. + let sites = this._findRemainingSites(links); + + // Remove sites that are no longer in the grid. + this._removeLegacySites(sites, () => { + // Freeze all site positions so that we can move their DOM nodes around + // without any visual impact. + this._freezeSitePositions(sites); + + // Move the sites' DOM nodes to their new position in the DOM. This will + // have no visual effect as all the sites have been frozen and will + // remain in their current position. + this._moveSiteNodes(sites); + + // Now it's time to animate the sites actually moving to their new + // positions. + this._rearrangeSites(sites, () => { + // Try to fill empty cells and finish. + this._fillEmptyCells(links, aCallback); + + // Update other pages that might be open to keep them synced. + gAllPages.update(gPage); + }); + }); + }, + + /** + * Takes an array of links and tries to correlate them to sites contained in + * the current grid. If no corresponding site can be found (i.e. the link is + * new and a site will be created) then just set it to null. + * @param aLinks The array of links to find sites for. + * @return Array of sites mapped to the given links (can contain null values). + */ + _findRemainingSites: function Updater_findRemainingSites(aLinks) { + let map = {}; + + // Create a map to easily retrieve the site for a given URL. + gGrid.sites.forEach(function (aSite) { + if (aSite) + map[aSite.url] = aSite; + }); + + // Map each link to its corresponding site, if any. + return aLinks.map(function (aLink) { + return aLink && (aLink.url in map) && map[aLink.url]; + }); + }, + + /** + * Freezes the given sites' positions. + * @param aSites The array of sites to freeze. + */ + _freezeSitePositions: function Updater_freezeSitePositions(aSites) { + aSites.forEach(function (aSite) { + if (aSite) + gTransformation.freezeSitePosition(aSite); + }); + }, + + /** + * Moves the given sites' DOM nodes to their new positions. + * @param aSites The array of sites to move. + */ + _moveSiteNodes: function Updater_moveSiteNodes(aSites) { + let cells = gGrid.cells; + + // Truncate the given array of sites to not have more sites than cells. + // This can happen when the user drags a bookmark (or any other new kind + // of link) onto the grid. + let sites = aSites.slice(0, cells.length); + + sites.forEach(function (aSite, aIndex) { + let cell = cells[aIndex]; + let cellSite = cell.site; + + // The site's position didn't change. + if (!aSite || cellSite != aSite) { + let cellNode = cell.node; + + // Empty the cell if necessary. + if (cellSite) + cellNode.removeChild(cellSite.node); + + // Put the new site in place, if any. + if (aSite) + cellNode.appendChild(aSite.node); + } + }, this); + }, + + /** + * Rearranges the given sites and slides them to their new positions. + * @param aSites The array of sites to re-arrange. + * @param aCallback The callback to call when finished. + */ + _rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) { + let options = {callback: aCallback, unfreeze: true}; + gTransformation.rearrangeSites(aSites, options); + }, + + /** + * Removes all sites from the grid that are not in the given links array or + * exceed the grid. + * @param aSites The array of sites remaining in the grid. + * @param aCallback The callback to call when finished. + */ + _removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) { + let batch = []; + + // Delete sites that were removed from the grid. + gGrid.sites.forEach(function (aSite) { + // The site must be valid and not in the current grid. + if (!aSite || aSites.indexOf(aSite) != -1) + return; + + batch.push(new Promise(resolve => { + // Fade out the to-be-removed site. + gTransformation.hideSite(aSite, function () { + let node = aSite.node; + + // Remove the site from the DOM. + node.parentNode.removeChild(node); + resolve(); + }); + })); + }); + + Promise.all(batch).then(aCallback); + }, + + /** + * Tries to fill empty cells with new links if available. + * @param aLinks The array of links. + * @param aCallback The callback to call when finished. + */ + _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) { + let {cells, sites} = gGrid; + + // Find empty cells and fill them. + Promise.all(sites.map((aSite, aIndex) => { + if (aSite || !aLinks[aIndex]) + return null; + + return new Promise(resolve => { + // Create the new site and fade it in. + let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); + + // Set the site's initial opacity to zero. + site.node.style.opacity = 0; + + // Flush all style changes for the dynamically inserted site to make + // the fade-in transition work. + window.getComputedStyle(site.node).opacity; + gTransformation.showSite(site, resolve); + }); + })).then(aCallback).catch(console.exception); + } +}; |