CSS Snippet to Target Phones, Tablets, and Desktops Separately

@media (min-width:20em)  { /* iPhones, Androids portrait 480x320 phones */ }
@media (min-width:30em)  { /* e-readers (Nook/Kindle), mini tablets 600x600 */ }
@media (min-width:40em)  { /* medium tablets, iPads, and e-readers, landscape 800x480 */ }
@media (min-width:60em)  { /* standard tablets, landscape iPads, lo-resolution laptops ands desktops */ }
@media (min-width:64em) { /* larger tablets, standard laptops, and 24" monitors */ }
@media (min-width:80em) { /* High resolution laptops and desktops */ }

How To Upgrade NextCloud 22.1.1 to 22.2.0 When Deployed with Kubernetes & Helm

Step 1:

Navigate to nextcloud > html > edit version.php

<?php 
$OC_Version = array(22,1,1,2);
$OC_VersionString = '22.1.1';
$OC_Edition = '';
$OC_Channel = 'stable';
$OC_VersionCanBeUpgradedFrom = array (
  'nextcloud' => 
  array (
    '21.0' => true,
    '22.0' => true,
    '22.1' => true,
    '22.2' => true,   # Add this line 
  ),
  'owncloud' => 
  array (
    '10.5' => true,
  ),
);
$OC_Build = '2021-08-26T13:27:46+00:00 1eea64f2c3eb0e110391c24830cea5f8d9c3e6a1';
$vendor = 'nextcloud';

Step 2: Run the ‘helm upgrade…’ command with the desired NextCloud version

# Example:
helm upgrade nextcloud nextcloud/nextcloud \
  --set image.tag=22.2.0-fpm \
  --set nginx.enabled=true \
  --set nextcloud.host=dragoncoin.com \
  --set nextcloud.username=dragon,nextcloud.password=SOMEVERYCOMPLEXANDVERYVERYLONGPASSWORD \
  --set internalDatabase.enabled=false \
  --set externalDatabase.existingSecret.enabled=true \
  --set externalDatabase.type=postgresql \
  --set externalDatabase.host='nextcloud-db-postgresql.default.svc.cluster.local' \
  --set persistence.enabled=true \
  --set persistence.existingClaim=nextcloud-claim \
  --set persistence.size=100Ti \
  --set livenessProbe.enabled=false \
  --set readinessProbe.enabled=false \
  --set nextcloud.phpConfigs.upload_max_size=40G \
  --set nextcloud.phpConfigs.upload_max_filesize=40G \
  --set nextcloud.phpConfigs.post_max_size=40G \
  --set nextcloud.phpConfigs.memory_limit=80G

Step 3: Check the logs and wait for the upgrading process to complete

Previous pods terminated to make way for new pods

admin@controller:~$ k get pod
NAME                                              READY   STATUS        RESTARTS   AGE
nextcloud-67855fc94c-lc2xr                        0/2     Terminating   0          74m
nextcloud-db-postgresql-0                         1/1     Running       0          91m
admin@controller:~$ k get pod
NAME                                              READY   STATUS    RESTARTS   AGE
nextcloud-79b5b775fd-2s4bj                        2/2     Running   0          56s
nextcloud-db-postgresql-0                         1/1     Running   0          92m

Expected 502 errors during pod upgrades

admin@controller:~$ k logs nextcloud-79b5b775fd-2s4bj nextcloud-nginx
2021/11/01 05:36:49 [error] 32#32: *24 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.0.95, server: , request: "GET /status.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "dragoncoin.com"
10.10.0.95 - dragon [01/Nov/2021:05:36:49 +0000] "GET /status.php HTTP/1.1" 502 157 "-" "Mozilla/5.0 (Linux) mirall/3.2.2git (build 5903) (Nextcloud, linuxmint-5.4.0-89-generic ClientArchitecture: x86_64 OsArchitecture: x86_64)" "192.168.0.164"

Logs showing that the upgrading process has progressed… and eventually completed

admin@controller:~$ kubectl logs nextcloud-79b5b775fd-2s4bj nextcloud

Initializing nextcloud 22.2.0.2 ...
Upgrading nextcloud from 22.1.1.2 ...
Initializing finished
Nextcloud or one of the apps require upgrade - only a limited number of commands are available
You may use your browser or the occ upgrade command to do the upgrade
Setting log level to debug
Turned on maintenance mode
Updating database schema
Updated database
Updating <lookup_server_connector> ...
Updated <lookup_server_connector> to 1.10.0
Updating <oauth2> ...
Updated <oauth2> to 1.10.0
Updating <files> ...
Updated <files> to 1.17.0
Updating <cloud_federation_api> ...
Updated <cloud_federation_api> to 1.5.0
Updating <dav> ...
Fix broken values of calendar objects

 Starting ...

Updated <dav> to 1.19.0
Updating <files_sharing> ...
Updated <files_sharing> to 1.14.0
Updating <files_trashbin> ...
Updated <files_trashbin> to 1.12.0
Updating <files_versions> ...
Updated <files_versions> to 1.15.0
Updating <sharebymail> ...
Updated <sharebymail> to 1.12.0
Updating <workflowengine> ...
Updated <workflowengine> to 2.4.0
Updating <systemtags> ...
Updated <systemtags> to 1.12.0
Updating <theming> ...
Updated <theming> to 1.13.0
Updating <accessibility> ...
Migrate old user config

    0/0 [>---------------------------]   0% Starting ...
    0/0 [->--------------------------]   0%
 Starting ...

Updated <accessibility> to 1.8.0
Updating <contactsinteraction> ...
Updated <contactsinteraction> to 1.3.0
Updating <federatedfilesharing> ...
Updated <federatedfilesharing> to 1.12.0
Updating <provisioning_api> ...
Updated <provisioning_api> to 1.12.0
Updating <settings> ...
Updated <settings> to 1.4.0
Updating <twofactor_backupcodes> ...
Updated <twofactor_backupcodes> to 1.11.0
Updating <updatenotification> ...
Updated <updatenotification> to 1.12.0
Updating <user_status> ...
Updated <user_status> to 1.2.0
Updating <weather_status> ...
Updated <weather_status> to 1.2.0
Checking for update of app accessibility in appstore
Checked for update of app "accessibility" in App Store
Checking for update of app activity in appstore
Checked for update of app "activity" in App Store
Checking for update of app audioplayer in appstore
Checked for update of app "audioplayer" in App Store
Checking for update of app breezedark in appstore
Checked for update of app "breezedark" in App Store
Checking for update of app bruteforcesettings in appstore
Checked for update of app "bruteforcesettings" in App Store
Checking for update of app camerarawpreviews in appstore
Checked for update of app "camerarawpreviews" in App Store
Checking for update of app cloud_federation_api in appstore
Checked for update of app "cloud_federation_api" in App Store
Checking for update of app cms_pico in appstore
Checked for update of app "cms_pico" in App Store
Checking for update of app contactsinteraction in appstore
Checked for update of app "contactsinteraction" in App Store
Checking for update of app dav in appstore
Checked for update of app "dav" in App Store
Checking for update of app documentserver_community in appstore
Checked for update of app "documentserver_community" in App Store
Checking for update of app drawio in appstore
Checked for update of app "drawio" in App Store
Checking for update of app external in appstore
Checked for update of app "external" in App Store
Checking for update of app federatedfilesharing in appstore
Checked for update of app "federatedfilesharing" in App Store
Checking for update of app files in appstore
Checked for update of app "files" in App Store
Checking for update of app files_antivirus in appstore
Checked for update of app "files_antivirus" in App Store
Checking for update of app files_markdown in appstore
Checked for update of app "files_markdown" in App Store
Checking for update of app files_mindmap in appstore
Checked for update of app "files_mindmap" in App Store
Checking for update of app files_pdfviewer in appstore
Checked for update of app "files_pdfviewer" in App Store
Checking for update of app files_rightclick in appstore
Checked for update of app "files_rightclick" in App Store
Checking for update of app files_sharing in appstore
Checked for update of app "files_sharing" in App Store
Checking for update of app files_trashbin in appstore
Checked for update of app "files_trashbin" in App Store
Checking for update of app files_versions in appstore
Checked for update of app "files_versions" in App Store
Checking for update of app files_videoplayer in appstore
Checked for update of app "files_videoplayer" in App Store
Checking for update of app forms in appstore
Checked for update of app "forms" in App Store
Checking for update of app logreader in appstore
Checked for update of app "logreader" in App Store
Checking for update of app lookup_server_connector in appstore
Checked for update of app "lookup_server_connector" in App Store
Checking for update of app maps in appstore
Checked for update of app "maps" in App Store
Checking for update of app music in appstore
Checked for update of app "music" in App Store
Checking for update of app news in appstore
Checked for update of app "news" in App Store
Checking for update of app notifications in appstore
Checked for update of app "notifications" in App Store
Checking for update of app oauth2 in appstore
Checked for update of app "oauth2" in App Store
Checking for update of app password_policy in appstore
Checked for update of app "password_policy" in App Store
Checking for update of app photos in appstore
Checked for update of app "photos" in App Store
Checking for update of app privacy in appstore
Checked for update of app "privacy" in App Store
Checking for update of app provisioning_api in appstore
Checked for update of app "provisioning_api" in App Store
Checking for update of app quicknotes in appstore
Checked for update of app "quicknotes" in App Store
Checking for update of app recommendations in appstore
Checked for update of app "recommendations" in App Store
Checking for update of app registration in appstore
Checked for update of app "registration" in App Store
Checking for update of app richdocuments in appstore
Checked for update of app "richdocuments" in App Store
Checking for update of app serverinfo in appstore
Checked for update of app "serverinfo" in App Store
Checking for update of app settings in appstore
Checked for update of app "settings" in App Store
Checking for update of app sharebymail in appstore
Checked for update of app "sharebymail" in App Store
Checking for update of app spreed in appstore
Checked for update of app "spreed" in App Store
Checking for update of app support in appstore
Checked for update of app "support" in App Store
Checking for update of app survey_client in appstore
Checked for update of app "survey_client" in App Store
Checking for update of app systemtags in appstore
Checked for update of app "systemtags" in App Store
Checking for update of app tasks in appstore
Checked for update of app "tasks" in App Store
Checking for update of app text in appstore
Checked for update of app "text" in App Store
Checking for update of app theming in appstore
Checked for update of app "theming" in App Store
Checking for update of app twofactor_backupcodes in appstore
Checked for update of app "twofactor_backupcodes" in App Store
Checking for update of app updatenotification in appstore
Checked for update of app "updatenotification" in App Store
Checking for update of app user_status in appstore
Checked for update of app "user_status" in App Store
Checking for update of app video_converter in appstore
Checked for update of app "video_converter" in App Store
Checking for update of app viewer in appstore
Checked for update of app "viewer" in App Store
Checking for update of app weather_status in appstore
Checked for update of app "weather_status" in App Store
Checking for update of app workflowengine in appstore
Checked for update of app "workflowengine" in App Store
Starting code integrity check...

After about 5 minutes (depending on the system hardware), NextCloud should be rendered back online. At this point, the upgrade has completed.

WordPress: How To Add Custom XML Sitemap to Yoast SEO

Step 1: Install Yoast SEO

Step 2: Install Code Snippets

Step 3: Add this Snippet

/* Add External Sitemap to Yoast Sitemap Index
 * Credit: Paul https:// wordpress.org/support/users/paulmighty/
 * Last Tested: Aug 25 2017 using Yoast SEO 5.3.2 on WordPress 4.8.1
 * Please note that changes will be applied upon next sitemap update.
 * To manually refresh the sitemap, please disable and enable the sitemaps.
 * Code obtained from https:// gist.github.com/amboutwe/8cfb7a3d8f05e580867341d4ff84141d
 */
add_filter( 'wpseo_sitemap_index', 'add_sitemap_custom_items' );
function add_sitemap_custom_items() {
   $sitemap_custom_items = '
<sitemap>
<loc>https://sexcenter.com/wp-content/gallery-pages.xml</loc>
<lastmod>2021-10-30T23:12:27+00:00</lastmod>
</sitemap>';
 
/* DO NOT REMOVE ANYTHING BELOW THIS LINE
 * Send the information to Yoast SEO
 */
return $sitemap_custom_items;
}

Step 4: Create an XML Sitemap and Place It Under wp-content

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brunette-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/asian-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/blonde-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/redhead-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/brown-hair-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/latinas/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/lesbians/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/sex-toys/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/gay-guys/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/group-sex/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/mature-women/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/black-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>

  <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/2</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/3</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/4</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/5</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/6</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/7</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/8</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/9</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
    <url>
    <loc>https://sexcenter.com/porn-galleries/white-girls/porn-gallery/page/10</loc>
    <lastmod>2021-10-30</lastmod>
  </url>
</urlset>

Linux: How To Use Dig

Checking Name Server(s)

kim@kim-linux:~$ dig @8.8.8.8 microsoft.com

; <<>> DiG 9.16.1-Ubuntu <<>> @8.8.8.8 microsoft.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47352
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;microsoft.com.			IN	A

;; ANSWER SECTION:
microsoft.com.		816	IN	A	104.215.148.63
microsoft.com.		816	IN	A	40.76.4.15
microsoft.com.		816	IN	A	40.112.72.205
microsoft.com.		816	IN	A	40.113.200.201
microsoft.com.		816	IN	A	13.77.161.179

;; Query time: 4 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Oct 29 09:41:15 PDT 2021
;; MSG SIZE  rcvd: 122

Looking Up TXT Records (Including SPF)

kim@kim-linux:~$ dig @8.8.8.8 microsoft.com txt

; <<>> DiG 9.16.1-Ubuntu <<>> @8.8.8.8 microsoft.com txt
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42064
;; flags: qr rd ra; QUERY: 1, ANSWER: 16, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;microsoft.com.			IN	TXT

;; ANSWER SECTION:
microsoft.com.		2127	IN	TXT	"docusign=d5a3737c-c23c-4bd0-9095-d2ff621f2840"
microsoft.com.		2127	IN	TXT	"v=spf1 include:_spf-a.microsoft.com include:_spf-b.microsoft.com include:_spf-c.microsoft.com include:_spf-ssg-a.microsoft.com include:spf-a.hotmail.com include:_spf1-meo.microsoft.com -all"
microsoft.com.		2127	IN	TXT	"google-site-verification=Zv1IvEEZg4N9wbEXpBSSyAiIjDyyB3S-fzfFClb7D1E"
microsoft.com.		2127	IN	TXT	"adobe-sign-verification=c1fea9b4cdd4df0d5778517f29e0934"
microsoft.com.		2127	IN	TXT	"docusign=52998482-393d-46f7-95d4-15ac6509bfdd"
microsoft.com.		2127	IN	TXT	"google-site-verification=8-zFCaUXhhPcvN29EVw2RvtASDCaDPQ02L1HJ8Om8I0"
microsoft.com.		2127	IN	TXT	"adobe-idp-site-verification=8aa35c528af5d72beb19b1bd3ed9b86d87ea7f24b2ba3c99ffcd00c27e9d809c"
microsoft.com.		2127	IN	TXT	"d365mktkey=4d8bnycx40fy3581petta4gsf"
microsoft.com.		2127	IN	TXT	"8RPDXjBzBS9tu7Pbysu7qCACrwXPoDV8ZtLfthTnC4y9VJFLd84it5sQlEITgSLJ4KOIA8pBZxmyvPujuUvhOg=="
microsoft.com.		2127	IN	TXT	"google-site-verification=1TeK8q0OziFl4T1tF-QR65JkzHZ1rcdgNccDFp78iTk"
microsoft.com.		2127	IN	TXT	"d365mktkey=3uc1cf82cpv750lzk70v9bvf2"
microsoft.com.		2127	IN	TXT	"facebook-domain-verification=fwzwhbbzwmg5fzgotc2go51olc3566"
microsoft.com.		2127	IN	TXT	"apple-domain-verification=0gMeaYyYy6GLViGo"
microsoft.com.		2127	IN	TXT	"google-site-verification=pjPOauSPcrfXOZS9jnPPa5axowcHGCDAl1_86dCqFpk"
microsoft.com.		2127	IN	TXT	"fg2t0gov9424p2tdcuo94goe9j"
microsoft.com.		2127	IN	TXT	"t7sebee51jrj7vm932k531hipa"

;; Query time: 4 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Oct 29 09:39:39 PDT 2021
;; MSG SIZE  rcvd: 1261

Domain Name Records Overview: A-record, MX, DKIM, SPF, SRV

A RECORD (A-host):

– What: address record (A-record) specifies the IP address(es) of a given domain. In the case of IPv6, this is called an AAAA record.
– Why: name to address translation is necessary for users to type in a name to get to an IP address of the web server
– Who: domain admin sets these up, and these affect all users of the domain
– How:
kimconnect.com record type: value: TTL
@ A x.x.x.x 14400

MX (Mail Exchange):

– What: mail exchange (MX) records direct emails toward designated mail servers. These are like CNAME records for name servers with the difference in their marking as designated for mailings
– Why: these entries control how email messages should be routed in accordance with the Simple Mail Transfer Protocol (SMTP)
– Who: domain admins can edit these records
– How: below is an example of setting mail records of a domain toward 2 mail servers with different priorities
kimconnect.com record type: priority: value: TTL
@ MX 10 mail1.kimconnect.com 45000
@ MX 20 mail2.kimconnect.com 45000

SPF (Sender Policy Framework):

– What: Sender Policy Framework (spf) is a type of TXT record in your DNS zone
– Why: SPF records help identify which mail servers are permitted to send email on behalf of your domain. These records prevent spammers from sending emails with a forged ‘From’ addresses of your domain
– Who: domain admins can make these changes. Users benefit from not receiving forged emails, and would correctly receive emails being sent from company servers.
– How (examples):
a. Simple:
- v=spf1 include:_spf.google.com ~all (Google)
- v=spf1 include:spf.protection.outlook.com ~all (Microsoft)
b. Complex:
- v=spf1 ip4:IP.ADDRESS.HERE/NETMASK include:_spf.google.com ~all (Google)
- v=spf1 ip4:IP.ADDRESS.HERE/NETMASK include:spf.protection.outlook.com ~all (Microsoft)
- v=spf1 ip4:IP.ADDRESS.HERE/NETMASK include:spf.protection.outlook.com include:_spf.google.com include:aem.autotask.net include:customers.clickdimensions.com ~all (Google, Microsoft, ClickDimensions, Autotask)

Explanations

  • v=spf1 : marks spf protocol version (version 1 is the most commonly used protocol by email servers as of this writing)
  • ip4 or ip6 : specifies the IP address versioning. A single IP or a summarized subnet/supernet are acceptable
  • mx : allows the MX servers to send mail
  • include : allows a third-party to send emails on your domain’s behalf
  • a : allows the current IP to send mail
  • +all : allows any IP to send emails on this domain’s behalf
  • -all : allows no other IP’s to send emails on the domain’s behalf
  • ~all : allows all IP’s to send emails on your domain’s behalf, while messages would be marked
DKIM  (DomainKeys Identified Mail):

– What: it’s an email record associated with certain domains. These are composed of a selector and a public key. There is a private key that is installed on the email server, and is its alternate hashes are attached to email headers. Only the public key is added as the domain’s DNS record. The receiving email server performs keys matching to determine if the email is legitimate (not spam)
– Why: to prevent email spoofing
– Who: domain admins make these changes
– How: (source: Google)

  1.  Generate the domain key for your domain (For Google: https:// support.google.com/a/answer/174126?hl=en&ref_topic=2752442)
  2.  Add the public key to your domain’s DNS records
    • Example: kimconnect.com. 300 IN TXT "v=DKIM1; k=rsa; p=SOMEHASH" "MOREHASH"
  3.  Add DKIM onto email server(s) to start adding a DKIM signature to all outgoing messages
    • Example: DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
      d=kimconnect.com; s=google;
      h=sender:mime-version:from:to:date:subject:message-id
      :x-original-sender:x-original-authentication-results:precedence
      :mailing-list:list-id:list-post:list-help:list-archive
      :list-unsubscribe;
      bh=SOMELONGHASH
SRV (Service Records):

– What: service (SRV) records specify hosts and ports for services such as VoIP, instant messaging, domain proof of ownership, etc.
– Why: these records include IP address and port information that other type of DNS records do not have the option. Some Internet protocols require the use of SRV records in order to function.
– Who: domain admins manage these at DNS zone control panels
– How: SRV records must point to an A record (in IPv4) or an AAAA record (in IPv6), not CNAME. Below are some examples
_sip._tls.@ 100 1 443 sipdir.online.lync.com. (Microsoft Lync)
_sipfederationtls._tcp.@ 100 1 5061 sipfed.online.lync.com. (Microsoft Lync)
_xmpp._tcp.kimconnect.com. 86400 IN SRV 10 5 5223 xmpp.kimconnect.com. (xmpp server)

How To Improve WordPress Website Rendering Speed

These are the recommended plug-ins to enable caching, database cleaning, image compressing, and minify codes to enhance a website’s response time.

Wp-Optimize: primary function is to optimize the WordPress Database and remove unnecessary data from tables (post revisions, auto-drafts, trashed). This may speed up SQL query response time. Other functions include page caching, minify JS/CSS/HTML (less matured than Autoptimize).

WP Super Cache: primary function is caching. It is different from other plugins in way that it converts all the php to html, thus reducing the server execution time everytime a request is made.

WP total cache: primary function is to provide caching. This is a contender to WP Optimize.

Autoptimize: primary function is minify JS/CSS/HTML, which optimizes delivery of of CSS and JQuery files on your website. This has been known to improve website speed.
– Optimize javascript code, Aggregate JS files, Also inline JS
– Optimize CSS Code, Aggregate CSS files, Also aggregate inline CSS
– Optimize HTML code
– Images: lazy load images, optimize images (glossy)
– Extra: Remove emojis, Remove query strings

Ewww image optimizer: primary function is to compress images for faster delivery.

a3 lazy load: primary function is to enable a ‘lazy load’ function to only trigger downloading of images when site scroller is in view. This does save bandwidth; thus, faster initial rendering is expected.

The 5 Rules of Thumb of Business Presentation (To Attract Investors)

I’m noting this here so that I can revisit these bullet points in the future…

1. Define the problem or need of the users in the market
2. Present your team or yourself as the expert who can solve this problem
3. Present the solution from the users’ perspective
4. Show a growth chart: users, revenue, traffic
5. Vision: must be realistic and credible

Credit: Justin Kan (one of the heros on my list)

WordPress PHP Fatal error: Maximum execution time of 30 seconds exceeded

Error:
[Tue Oct 12 05:57:03.088314 2021] [php7:error] [pid 167] [client 172.16.90.64:39776] PHP Fatal error: Maximum execution time of 30 seconds exceeded in /bitnami/wordpress/wp-content/plugins/nextgen-gallery/vendor/imagely/pope-framework/lib/class.extensibleobject.php on line 0, referer: https://dragoncoin.com/wp-admin/admin.php?page=ngg_addgallery
Resolution:

Option A: Edit php.ini

// Case: Kubernetes Pod / Docker Container
containerName=sexcenter-wordpress-6f59c9fbff-5b99h
kubectl exec --stdin --tty $containerName -- /bin/bash

// Check execution time
grep max_execution_time /opt/bitnami/php/etc/php.ini

// Change execution time duration to 1 hour
sed -i '/max_execution_time/c\max_execution_time = 3600' /opt/bitnami/php/etc/php.ini

// Check memory limit
grep memory_limit /opt/bitnami/php/etc/php.ini

// Set memory limit to 20GB
sed -i '/memory_limit/c\memory_limit = 20G' /opt/bitnami/php/etc/php.ini

// Change execution time duration and memory to unlimited (for testing purposes)
sed -i '/max_execution_time/c\max_execution_time = 999999' /opt/bitnami/php/etc/php.ini
sed -i '/memory_limit/c\memory_limit = -1' /opt/bitnami/php/etc/php.ini

// Restart apache
apachectl restart

Option B: Edit wp-config.php

// Set execution time to 24 hours
ini_set('max_execution_time', '5184000');
set_time_limit(5184000);

Option C: Edit .htaccess

php_value max_execution_time 120

How To Increase WordPress Memory Limit

It’s are lesser known fact that WordPress overrides PHP’s memory_limit settings. Thus, it is possible to edit the wp-config.php file to increase WordPress’ memory allocation.

Depending on the deployment configs, wp-config.php usually is located in the /public/html directory. In the case of docker, it’s one level above wp-content directory.

A. Increasing the Administrative Memory

For admin pages, WordPress ignores PHP’s memory limit and configures its own. To change it to something other than 256 megabytes, you must set WP_MAX_MEMORY_LIMIT in wp-config.php.

To increase the memory to the administrative side of your site, add the following line in your wp-config.php:

// This sets the admin panel memory limit to 4GB
define('WP_MAX_MEMORY_LIMIT', '4G');

B. Increasing Public Memory

To increase the memory limit for your public-facing pages, open your app’s wp‑config.php and add the following line:

// This configures the public users' view memory limit to 8GB
define('WP_MEMORY_LIMIT', '8G');

Kubernetes: Use Helm to Deploy WordPress

Deploying WordPress in a Kubernetes cluster isn’t as straight-forward is one might expect. As the whole infrastructure is controlled by K8s, the deployed containers must be configured with the correct permissions to avoid strange issues. For example, there are certain plugins such as NextGen Gallery that would seem to work fine (initially) in a WordPress instance; however, it would suddenly ‘break’ as CSS and JS (JavaScripts) are showing 404’s. Specially, lightbox and other gallery views functions would become orphanated to leave a Gallery view without slideshow, multi-column views, and other animations. Hence, there are many lesion-learned prior issuing this blog post…

Step 1: Prepare an SSL Cert using LetsEncrypt

# Create SSL Cert, assuming the certmanager has been deployed in the Kubernetes cluster
appName=kimconnect
domainName=kimconnect.com
cat <<EOF > $appName-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: $appName-cert
  namespace: $appName
  annotations:
    kubernetes.io/ingress.class: "nginx"
    acme.cert-manager.io/http01-edit-in-place: "true"
    kubernetes.io/tls-acme: "true"
spec:
  dnsNames:
    - $domainName
  secretName: $appName-cert
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
EOF
kubectl apply -f $appName-cert.yaml

Step 2: Deploy WordPress

# Create name space and enter it
appName=kimconnect
kubectl create namespace $appName
kubectl config set-context --current --namespace=$appName

# Install WordPress with Dynamic NFS Provisioning

# Documentation: https://hub.kubeapps.com/charts/bitnami/wordpress
# Set variables
appName=kimconnect
domainName=kimconnect.com
wordpressusername=kimconnect
wordpressPassword=SOMEVERYCOMPLEXPASSWORD
rootPassword=SOMEVERYCOMPLEXPASSWORD
storageClass=nfs-client

helm install $appName bitnami/wordpress \
  --set readinessProbe.enabled=false \
  --set image.tag=latest \
  --set persistence.accessMode=ReadWriteMany \
  --set persistence.storageClass=$storageClass \
  --set persistence.size=10Ti \
  --set mariadb.primary.persistence.storageClass=$storageClass \
  --set mariadb.primary.persistence.size=300Gi \
  --set wordpressUsername=$wordpressusername \
  --set wordpressPassword=$wordpressPassword \
  --set mariadb.auth.rootPassword=$rootPassword \
  --set mariadb.auth.password=$rootPassword \
  --set ingress.enabled=true,ingress.hostname=$domainName \
  --set volumePermissions.enabled=true \
  --set allowEmptyPassword=false \
  --set service.externalTrafficPolicy=Local # this setting is to make sure the source IP address is preserved.

Step 3: Patch Ingress

# Patch the deployed ingress with an existing SSL cert
appName=kimconnect
domainName=kimconnect.com
certName=$appName-cert
serviceName=$appName-wordpress
cat <<EOF> $appName-patch.yaml
metadata:
  Annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      location ~ /wp-admin$ {
           return 301 /wp-admin/;
       }    
spec:
  tls:
  - hosts:
    - $domainName
    secretName: $certName
  rules:
  - host: $domainName
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: $serviceName
            port:
              number: 80
EOF
kubectl patch ingress/$appName-wordpress -p "$(cat $appName-patch.yaml)"

Step 4: Patch WordPress to Add Libraries and Frameworks

# Still researching this item
# Add iocube loader while inside the container

sed -i '/\[PHP\]/a zend_extension=/bitnami/wordpress/extensions/ioncube_loader_lin_7.4.so' /opt/bitnami/php/etc/php.ini && httpd -k restart

How To Hide Featured Image in Blog Posts in Single View

In WordPress, single posts can have a featured image to help with displaying in List views as well as search engine excerpts (previews). However, such featured images could overwhelm a blog post when they would dominate the upper section of an article. Therefore, it’s often necessary to hide featured images while retaining their prevalence in list views. Here are the steps:

  1. Option 1: install plugin ‘Hide featured image on all single page/post’ 


  2. Option 2: install plugin ‘Conditionally display featured image on singular posts and pages’

    Edit certain pages where featured images should be removed by placing a check mark next to ‘display featured image in post lists only’

Lightbox Javascript Contents

These JavaScript codes are open source and part of many applications, such as NextGen Gallery. Their contents are pasted here for ease of reading and referencing.

// lightbox_context.min.js
"use strict";function nextgen_lightbox_filter_selector(e,t){if(nextgen_lightbox_settings&&nextgen_lightbox_settings.context){var n=nextgen_lightbox_settings.context;"all_images"==n?t=t.add(e("a > img").parent()):"all_images_direct"==n?t=t.add(e("a[href] > img").parent().filter(function(){var t=e(this).attr("href").toLowerCase(),n=t.substring(t.length-3),g=t.substring(t.length-4);return"jpg"==n||"gif"==n||"png"==n||"tiff"==g||"jpeg"==g||"webp"==g})):"nextgen_and_wp_images"==n&&(t=t.add(e('a > img[class*="wp-image-"]').parent())),t=t.not(".gallery_link"),t=t.not(".use_imagebrowser_effect")}return t}
// common.js
(function($) {
    window.NggPaginatedGallery = function(displayed_gallery_id, container) {
        this.displayed_gallery_id = displayed_gallery_id;
        this.container            = $(container);
        this.container_name       = container;

        this.get_displayed_gallery_obj = function() {
            var index = 'gallery_' + this.displayed_gallery_id;
            if (typeof(window.galleries[index]) == 'undefined') {
                return false;
            } else {
                return window.galleries[index];
            }
        };

        this.enable_ajax_pagination = function() {
            var self = this;
            // Attach a click event handler for each pagination link to adjust the request to be sent via XHR
            $('body').on('click', 'a.ngg-browser-prev, a.ngg-browser-next', function (event) {

                var skip = true;
                $(this).parents(container).each(function() {
                    if ($(this).data('nextgen-gallery-id') != self.displayed_gallery_id) {
                        return true;
                    }
                    skip = false;
                });

                if (!skip) {
                    event.preventDefault();
                } else {
                    return;
                }

                // Adjust the user notification
                window['ngg_ajax_operaton_count']++;
                $('body, a').css('cursor', 'wait');

                // Send the AJAX request
                $.get($(this).attr('href'), function (response) {
                    window['ngg_ajax_operaton_count']--;
                    if (window['ngg_ajax_operaton_count'] <= 0) {
                        window['ngg_ajax_operaton_count'] = 0;
                        $('body, a').css('cursor', 'auto');
                    }

                    if (response) {
                        var html = $(response);
                        var replacement = false;
                        html.find(self.container_name).each(function() {
                            if (replacement) {
                                return true;
                            }
                            if ($(this).data('nextgen-gallery-id') != self.displayed_gallery_id) {
                                return true;
                            }
                            replacement = $(this);
                        });
                        if (replacement) {
                            self.container.each(function () {
                                var $this = $(this);

                                if ($this.data('nextgen-gallery-id') != self.displayed_gallery_id) {
                                    return true;
                                }

                                // If the image gallery makes up the bulk of the post/page content the .html() call
                                // below will empty the contents causing the browser's scroll position to be reset to
                                // zero as the browser believes it has been pushed back to the top of the page. Here
                                // we give the parent container a min-height equal to the gallery's height to prevent
                                // this flicker and resetting of the scroll position.
                                var $new_element = $(replacement.html());
                                var promises = $new_element.find('img').toArray().map(function(img){
                                    return new Promise(function(resolve, reject){
                                        var i = new Image();
                                        i.src = img.src;
                                        $(i).on('load', resolve);
                                    });
                                });

                                Promise.all(promises).then(function(){
                                        $this.html($new_element);

                                        // Let the user know that we've refreshed the content
                                        $(document).trigger('refreshed');
                                });

                                return true;
                            });
                        }
                    }
                });
            });
        };

        // Initialize
        var displayed_gallery = this.get_displayed_gallery_obj();
        if (displayed_gallery) {
            if (typeof(displayed_gallery.display_settings['ajax_pagination']) != 'undefined') {
                if (parseInt(displayed_gallery.display_settings['ajax_pagination'])) {
                    this.enable_ajax_pagination();
                }
            }
        }

        // We maintain a count of all the current AJAX actions initiated
        if (typeof(window['ngg_ajax_operation_count']) == 'undefined') {
            window['ngg_ajax_operaton_count'] = 0;
        }
    };

    // Polyfill for older browsers
    Object.setPrototypeOf = Object.setPrototypeOf || function(obj, proto) {
        obj.__proto__ = proto;
        return obj;
    };

    if (typeof window.galleries !== 'undefined') {
    Object.setPrototypeOf(
        window.galleries,
        {
            get_api_version: function() {
                return '0.1';
            },

            get_from_id: function (gallery_id) {
                var self   = this;
                var retval = null;
                var keys   = Object.keys(this);

                for (var i = 1; i <= keys.length; i++) {
                    var gallery = self[keys[i - 1]];
                    if (gallery.ID === gallery_id || gallery.ID === 'gallery_' + gallery_id || gallery.ID === parseInt(gallery_id)) {
                        retval = gallery;
                        break;
                    }
                }

                return retval;
            },

            get_from_slug: function (slug) {
                var self   = this;
                var retval = null;
                var keys   = Object.keys(this);

                for (var i = 1; i <= keys.length; i++) {
                    var gallery = self[keys[i - 1]];
                    if (gallery.slug === slug) {
                        retval = gallery;
                        break;
                    }
                }

                return retval;
            },

            get_setting: function(gallery_id, name, def) {
                var tmp = '';
                var gallery = this.get_from_id(gallery_id);
                if (gallery && typeof gallery[name] !== 'undefined') {
                    tmp = gallery[name];
                } else {
                    tmp = def;
                }

                if (tmp === 1)       tmp = true;
                if (tmp === 0)       tmp = false;
                if (tmp === '1')     tmp = true;
                if (tmp === '0')     tmp = false;
                if (tmp === 'false') tmp = false;
                if (tmp === 'true')  tmp = true;

                return tmp;
            },

            get_display_setting: function(gallery_id, name, def) {
                var tmp = '';
                var gallery = this.get_from_id(gallery_id);
                if (gallery && typeof gallery.display_settings[name] !== 'undefined') {
                    tmp = gallery.display_settings[name];
                } else {
                    tmp = def;
                }

                if (tmp === 1)       tmp = true;
                if (tmp === 0)       tmp = false;
                if (tmp === '1')     tmp = true;
                if (tmp === '0')     tmp = false;
                if (tmp === 'false') tmp = false;
                if (tmp === 'true')  tmp = true;

                return tmp;
            },

            is_widget: function(gallery_id) {
                var retval  = false;
                var gallery = this.get_from_id(gallery_id);
                var slug    = gallery.slug;

                if (slug) {
                    return slug.indexOf('widget-ngg-images') !== -1;
                }

                return retval;
            }
        }
    ); }

})(jQuery);
// lightbox_context.js
function nextgen_lightbox_filter_selector($, selector) 
{
		if (nextgen_lightbox_settings && nextgen_lightbox_settings.context) {
			var context = nextgen_lightbox_settings.context;
			
			if (context == 'all_images') {
				 selector = selector.add($('a > img').parent());
			}
			else if (context == 'all_images_direct') {
				selector = selector.add($('a[href] > img').parent()
				 		.filter(function() {
							var href = $(this).attr('href').toLowerCase();
							var ext = href.substring(href.length - 3);
							var ext2 = href.substring(href.length - 4);
							
							return (ext == 'jpg' || ext == 'gif' || ext == 'png' || ext2 == 'tiff' || ext2 == 'jpeg' || ext2 == 'webp');
						}));
			}
			else if (context == 'nextgen_and_wp_images') {
				 selector = selector.add($('a > img[class*="wp-image-"]').parent());
			}
			
			selector = selector.not('.gallery_link');
            selector = selector.not('.use_imagebrowser_effect');
		}
		
		return selector;
}
// simple_lightbox.js
/*!
	By André Rinas, www.andrerinas.de
	Documentation, www.simplelightbox.de
	Available for use under the MIT License
	Version 2.1.5
*/
"use strict";

function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var SimpleLightbox = /*#__PURE__*/function () {
  function SimpleLightbox(elements, options) {
    var _this = this;

    _classCallCheck(this, SimpleLightbox);

    _defineProperty(this, "defaultOptions", {
      sourceAttr: 'href',
      overlay: true,
      spinner: true,
      nav: true,
      navText: ['&lsaquo;', '&rsaquo;'],
      captions: true,
      captionDelay: 0,
      captionSelector: 'img',
      captionType: 'attr',
      captionsData: 'title',
      captionPosition: 'bottom',
      captionClass: '',
      close: true,
      closeText: '&times;',
      swipeClose: true,
      showCounter: true,
      fileExt: 'png|jpg|jpeg|gif|webp',
      animationSlide: true,
      animationSpeed: 250,
      preloading: true,
      enableKeyboard: true,
      loop: true,
      rel: false,
      docClose: true,
      swipeTolerance: 50,
      className: 'simple-lightbox',
      widthRatio: 0.8,
      heightRatio: 0.9,
      scaleImageToRatio: false,
      disableRightClick: false,
      disableScroll: true,
      alertError: true,
      alertErrorMessage: 'Image not found, next image will be loaded',
      additionalHtml: false,
      history: true,
      throttleInterval: 0,
      doubleTapZoom: 2,
      maxZoom: 10,
      htmlClass: 'has-lightbox',
      rtl: false
    });

    _defineProperty(this, "transitionPrefix", void 0);

    _defineProperty(this, "transitionCapable", false);

    _defineProperty(this, "isTouchDevice", 'ontouchstart' in window);

    _defineProperty(this, "initialLocationHash", void 0);

    _defineProperty(this, "pushStateSupport", 'pushState' in history);

    _defineProperty(this, "isOpen", false);

    _defineProperty(this, "isAnimating", false);

    _defineProperty(this, "isClosing", false);

    _defineProperty(this, "urlChangedOnce", false);

    _defineProperty(this, "hashReseted", false);

    _defineProperty(this, "historyHasChanges", false);

    _defineProperty(this, "historyUpdateTimeout", null);

    _defineProperty(this, "currentImage", void 0);

    _defineProperty(this, "eventNamespace", 'simplelightbox');

    _defineProperty(this, "domNodes", {});

    _defineProperty(this, "loadedImages", []);

    _defineProperty(this, "initialImageIndex", 0);

    _defineProperty(this, "currentImageIndex", 0);

    _defineProperty(this, "initialSelector", null);

    _defineProperty(this, "globalScrollbarWidth", 0);

    _defineProperty(this, "controlCoordinates", {
      swipeDiff: 0,
      swipeYDiff: 0,
      swipeStart: 0,
      swipeEnd: 0,
      swipeYStart: 0,
      swipeYEnd: 0,
      mousedown: false,
      imageLeft: 0,
      zoomed: false,
      containerHeight: 0,
      containerWidth: 0,
      containerOffsetX: 0,
      containerOffsetY: 0,
      imgHeight: 0,
      imgWidth: 0,
      capture: false,
      initialOffsetX: 0,
      initialOffsetY: 0,
      initialPointerOffsetX: 0,
      initialPointerOffsetY: 0,
      initialPointerOffsetX2: 0,
      initialPointerOffsetY2: 0,
      initialScale: 1,
      initialPinchDistance: 0,
      pointerOffsetX: 0,
      pointerOffsetY: 0,
      pointerOffsetX2: 0,
      pointerOffsetY2: 0,
      targetOffsetX: 0,
      targetOffsetY: 0,
      targetScale: 0,
      pinchOffsetX: 0,
      pinchOffsetY: 0,
      limitOffsetX: 0,
      limitOffsetY: 0,
      scaleDifference: 0,
      targetPinchDistance: 0,
      touchCount: 0,
      doubleTapped: false,
      touchmoveCount: 0
    });

    this.options = Object.assign(this.defaultOptions, options);

    if (typeof elements === 'string') {
      this.initialSelector = elements;
      this.elements = Array.from(document.querySelectorAll(elements));
    } else {
      this.elements = typeof elements.length !== 'undefined' && elements.length > 0 ? Array.from(elements) : [elements];
    }

    this.relatedElements = [];
    this.transitionPrefix = this.calculateTransitionPrefix();
    this.transitionCapable = this.transitionPrefix !== false;
    this.initialLocationHash = this.hash; // this should be handled by attribute selector IMHO! => 'a[rel=bla]'...

    if (this.options.rel) {
      this.elements = this.getRelated(this.options.rel);
    }

    this.createDomNodes();

    if (this.options.close) {
      this.domNodes.wrapper.appendChild(this.domNodes.closeButton);
    }

    if (this.options.nav) {
      this.domNodes.wrapper.appendChild(this.domNodes.navigation);
    }

    if (this.options.spinner) {
      this.domNodes.wrapper.appendChild(this.domNodes.spinner);
    }

    this.addEventListener(this.elements, 'click.' + this.eventNamespace, function (event) {
      if (_this.isValidLink(event.currentTarget)) {
        event.preventDefault();

        if (_this.isAnimating) {
          return false;
        }

        _this.initialImageIndex = _this.elements.indexOf(event.currentTarget);

        _this.openImage(event.currentTarget);
      }
    }); // close addEventListener click addEventListener doc

    if (this.options.docClose) {
      this.addEventListener(this.domNodes.overlay, ['click.' + this.eventNamespace, 'touchstart.' + this.eventNamespace], function (event) {
        if (_this.isOpen) {
          _this.close();
        }
      });
    } // disable rightclick


    if (this.options.disableRightClick) {
      this.addEventListener(document.body, 'contextmenu.' + this.eventNamespace, function (event) {
        if (event.target.classList.contains('sl-overlay')) {
          event.preventDefault();
        }
      });
    } // keyboard-control


    if (this.options.enableKeyboard) {
      this.addEventListener(document.body, 'keyup.' + this.eventNamespace, this.throttle(function (event) {
        _this.controlCoordinates.swipeDiff = 0; // keyboard control only if lightbox is open

        if (_this.isAnimating && event.key === 'Escape') {
          _this.currentImage.setAttribute('src', '');

          _this.isAnimating = false;
          return _this.close();
        }

        if (_this.isOpen) {
          event.preventDefault();

          if (event.key === 'Escape') {
            _this.close();
          }

          if (!_this.isAnimating && ['ArrowLeft', 'ArrowRight'].indexOf(event.key) > -1) {
            _this.loadImage(event.key === 'ArrowRight' ? 1 : -1);
          }
        }
      }, this.options.throttleInterval));
    }

    this.addEvents();
  }

  _createClass(SimpleLightbox, [{
    key: "createDomNodes",
    value: function createDomNodes() {
      this.domNodes.overlay = document.createElement('div');
      this.domNodes.overlay.classList.add('sl-overlay');
      this.domNodes.overlay.dataset.opacityTarget = ".7";
      this.domNodes.closeButton = document.createElement('button');
      this.domNodes.closeButton.classList.add('sl-close');
      this.domNodes.closeButton.innerHTML = this.options.closeText;
      this.domNodes.spinner = document.createElement('div');
      this.domNodes.spinner.classList.add('sl-spinner');
      this.domNodes.spinner.innerHTML = '<div></div>';
      this.domNodes.navigation = document.createElement('div');
      this.domNodes.navigation.classList.add('sl-navigation');
      this.domNodes.navigation.innerHTML = "<button class=\"sl-prev\">".concat(this.options.navText[0], "</button><button class=\"sl-next\">").concat(this.options.navText[1], "</button>");
      this.domNodes.counter = document.createElement('div');
      this.domNodes.counter.classList.add('sl-counter');
      this.domNodes.counter.innerHTML = '<span class="sl-current"></span>/<span class="sl-total"></span>';
      this.domNodes.caption = document.createElement('div');
      this.domNodes.caption.classList.add('sl-caption', 'pos-' + this.options.captionPosition);

      if (this.options.captionClass) {
        this.domNodes.caption.classList.add(this.options.captionClass);
      }

      this.domNodes.image = document.createElement('div');
      this.domNodes.image.classList.add('sl-image');
      this.domNodes.wrapper = document.createElement('div');
      this.domNodes.wrapper.classList.add('sl-wrapper');

      if (this.options.className) {
        this.domNodes.wrapper.classList.add(this.options.className);
      }

      if (this.options.rtl) {
        this.domNodes.wrapper.classList.add('sl-dir-rtl');
      }
    }
  }, {
    key: "throttle",
    value: function throttle(func, limit) {
      var inThrottle;
      return function () {
        if (!inThrottle) {
          func.apply(this, arguments);
          inThrottle = true;
          setTimeout(function () {
            return inThrottle = false;
          }, limit);
        }
      };
    }
  }, {
    key: "isValidLink",
    value: function isValidLink(element) {
      return !this.options.fileExt || 'pathname' in element && new RegExp('(' + this.options.fileExt + ')$', 'i').test(element.pathname);
    }
  }, {
    key: "calculateTransitionPrefix",
    value: function calculateTransitionPrefix() {
      var s = (document.body || document.documentElement).style;
      return 'transition' in s ? '' : 'WebkitTransition' in s ? '-webkit-' : 'MozTransition' in s ? '-moz-' : 'OTransition' in s ? '-o' : false;
    }
  }, {
    key: "toggleScrollbar",
    value: function toggleScrollbar(type) {
      var scrollbarWidth = 0;

      if (type === 'hide') {
        var fullWindowWidth = window.innerWidth;

        if (!fullWindowWidth) {
          var documentElementRect = document.documentElement.getBoundingClientRect();
          fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left);
        }

        if (document.body.clientWidth < fullWindowWidth) {
          var scrollDiv = document.createElement('div'),
              paddingRight = parseInt(document.body.style.paddingRight || 0, 10);
          scrollDiv.classList.add('sl-scrollbar-measure');
          document.body.appendChild(scrollDiv);
          scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
          document.body.removeChild(scrollDiv);
          document.body.dataset.originalPaddingRight = paddingRight;

          if (scrollbarWidth > 0) {
            document.body.classList.add('hidden-scroll');
            document.body.style.paddingRight = paddingRight + scrollbarWidth + 'px';
          }
        }
      } else {
        document.body.classList.remove('hidden-scroll');
        document.body.style.paddingRight = document.body.dataset.originalPaddingRight;
      }

      return scrollbarWidth;
    }
  }, {
    key: "close",
    value: function close() {
      var _this2 = this;

      if (!this.isOpen || this.isAnimating || this.isClosing) {
        return false;
      }

      this.isClosing = true;
      var element = this.relatedElements[this.currentImageIndex];
      element.dispatchEvent(new Event('close.simplelightbox'));

      if (this.options.history) {
        this.historyHasChanges = false;

        if (!this.hashReseted) {
          this.resetHash();
        }
      }

      this.fadeOut(document.querySelectorAll('.sl-image img, .sl-overlay, .sl-close, .sl-navigation, .sl-image .sl-caption, .sl-counter'), 300, function () {
        if (_this2.options.disableScroll) {
          _this2.toggleScrollbar('show');
        }

        if (_this2.options.htmlClass && _this2.options.htmlClass !== '') {
          document.querySelector('html').classList.remove(_this2.options.htmlClass);
        }

        document.body.removeChild(_this2.domNodes.wrapper);
        document.body.removeChild(_this2.domNodes.overlay);
        _this2.domNodes.additionalHtml = null;
        element.dispatchEvent(new Event('closed.simplelightbox'));
        _this2.isClosing = false;
      });
      this.currentImage = null;
      this.isOpen = false;
      this.isAnimating = false; // reset touchcontrol coordinates

      for (var key in this.controlCoordinates) {
        this.controlCoordinates[key] = 0;
      }

      this.controlCoordinates.mousedown = false;
      this.controlCoordinates.zoomed = false;
      this.controlCoordinates.capture = false;
      this.controlCoordinates.initialScale = this.minMax(1, 1, this.options.maxZoom);
      this.controlCoordinates.doubleTapped = false;
    }
  }, {
    key: "preload",
    value: function preload() {
      var _this3 = this;

      var index = this.currentImageIndex,
          length = this.relatedElements.length,
          next = index + 1 < 0 ? length - 1 : index + 1 >= length - 1 ? 0 : index + 1,
          prev = index - 1 < 0 ? length - 1 : index - 1 >= length - 1 ? 0 : index - 1,
          nextImage = new Image(),
          prevImage = new Image();
      nextImage.addEventListener('load', function (event) {
        var src = event.target.getAttribute('src');

        if (_this3.loadedImages.indexOf(src) === -1) {
          //is this condition even required... setting multiple times will not change usage...
          _this3.loadedImages.push(src);
        }

        _this3.relatedElements[index].dispatchEvent(new Event('nextImageLoaded.' + _this3.eventNamespace));
      });
      nextImage.setAttribute('src', this.relatedElements[next].getAttribute(this.options.sourceAttr));
      prevImage.addEventListener('load', function (event) {
        var src = event.target.getAttribute('src');

        if (_this3.loadedImages.indexOf(src) === -1) {
          _this3.loadedImages.push(src);
        }

        _this3.relatedElements[index].dispatchEvent(new Event('prevImageLoaded.' + _this3.eventNamespace));
      });
      prevImage.setAttribute('src', this.relatedElements[prev].getAttribute(this.options.sourceAttr));
    }
  }, {
    key: "loadImage",
    value: function loadImage(direction) {
      var _this4 = this;

      var slideDirection = direction;

      if (this.options.rtl) {
        direction = -direction;
      }

      this.relatedElements[this.currentImageIndex].dispatchEvent(new Event('change.' + this.eventNamespace));
      this.relatedElements[this.currentImageIndex].dispatchEvent(new Event((direction === 1 ? 'next' : 'prev') + '.' + this.eventNamespace));
      var newIndex = this.currentImageIndex + direction;

      if (this.isAnimating || (newIndex < 0 || newIndex >= this.relatedElements.length) && this.options.loop === false) {
        return false;
      }

      this.currentImageIndex = newIndex < 0 ? this.relatedElements.length - 1 : newIndex > this.relatedElements.length - 1 ? 0 : newIndex;
      this.domNodes.counter.querySelector('.sl-current').innerHTML = this.currentImageIndex + 1;

      if (this.options.animationSlide) {
        this.slide(this.options.animationSpeed / 1000, -100 * slideDirection - this.controlCoordinates.swipeDiff + 'px');
      }

      this.fadeOut(this.domNodes.image, 300, function () {
        _this4.isAnimating = true;
        setTimeout(function () {
          var element = _this4.relatedElements[_this4.currentImageIndex];

          _this4.currentImage.setAttribute('src', element.getAttribute(_this4.options.sourceAttr));

          if (_this4.loadedImages.indexOf(element.getAttribute(_this4.options.sourceAttr)) === -1) {
            _this4.show(_this4.domNodes.spinner);
          }

          if (_this4.domNodes.image.contains(_this4.domNodes.caption)) {
            _this4.domNodes.image.removeChild(_this4.domNodes.caption);
          }

          _this4.adjustImage(slideDirection);

          if (_this4.options.preloading) _this4.preload();
        }, 100);
      });
    }
  }, {
    key: "adjustImage",
    value: function adjustImage(direction) {
      var _this5 = this;

      if (!this.currentImage) {
        return false;
      }

      var tmpImage = new Image(),
          windowWidth = window.innerWidth * this.options.widthRatio,
          windowHeight = window.innerHeight * this.options.heightRatio;
      tmpImage.setAttribute('src', this.currentImage.getAttribute('src'));
      this.currentImage.dataset.scale = 1;
      this.currentImage.dataset.translateX = 0;
      this.currentImage.dataset.translateY = 0;
      this.zoomPanElement(0, 0, 1);
      tmpImage.addEventListener('error', function (event) {
        _this5.relatedElements[_this5.currentImageIndex].dispatchEvent(new Event('error.' + _this5.eventNamespace));

        _this5.isAnimating = false;
        _this5.isOpen = false;
        _this5.domNodes.spinner.style.display = 'none';
        var dirIsDefined = direction === 1 || direction === -1;

        if (_this5.initialImageIndex === _this5.currentImageIndex && dirIsDefined) {
          return _this5.close();
        }

        if (_this5.options.alertError) {
          alert(_this5.options.alertErrorMessage);
        }

        _this5.loadImage(dirIsDefined ? direction : 1);
      });
      tmpImage.addEventListener('load', function (event) {
        if (typeof direction !== 'undefined') {
          _this5.relatedElements[_this5.currentImageIndex].dispatchEvent(new Event('changed.' + _this5.eventNamespace));

          _this5.relatedElements[_this5.currentImageIndex].dispatchEvent(new Event((direction === 1 ? 'nextDone' : 'prevDone') + '.' + _this5.eventNamespace));
        } // history


        if (_this5.options.history) {
          _this5.updateURL();
        }

        if (_this5.loadedImages.indexOf(_this5.currentImage.getAttribute('src')) === -1) {
          _this5.loadedImages.push(_this5.currentImage.getAttribute('src'));
        }

        var imageWidth = event.target.width,
            imageHeight = event.target.height;

        if (_this5.options.scaleImageToRatio || imageWidth > windowWidth || imageHeight > windowHeight) {
          var ratio = imageWidth / imageHeight > windowWidth / windowHeight ? imageWidth / windowWidth : imageHeight / windowHeight;
          imageWidth /= ratio;
          imageHeight /= ratio;
        }

        _this5.domNodes.image.style.top = (window.innerHeight - imageHeight) / 2 + 'px';
        _this5.domNodes.image.style.left = (window.innerWidth - imageWidth - _this5.globalScrollbarWidth) / 2 + 'px';
        _this5.domNodes.image.style.width = imageWidth + 'px';
        _this5.domNodes.image.style.height = imageHeight + 'px';
        _this5.domNodes.spinner.style.display = 'none';

        _this5.fadeIn(_this5.currentImage, 300);

        _this5.isOpen = true;
        var captionContainer = _this5.options.captionSelector === 'self' ? _this5.relatedElements[_this5.currentImageIndex] : _this5.relatedElements[_this5.currentImageIndex].querySelector(_this5.options.captionSelector),
            captionText;

        if (_this5.options.captions && captionContainer) {
          if (_this5.options.captionType === 'data') {
            captionText = captionContainer.dataset[_this5.options.captionsData];
          } else if (_this5.options.captionType === 'text') {
            captionText = captionContainer.innerHTML;
          } else {
            captionText = captionContainer.getAttribute(_this5.options.captionsData);
          }
        }

        if (!_this5.options.loop) {
          if (_this5.currentImageIndex === 0) {
            _this5.hide(_this5.domNodes.navigation.querySelector('.sl-prev'));
          }

          if (_this5.currentImageIndex >= _this5.relatedElements.length - 1) {
            _this5.hide(_this5.domNodes.navigation.querySelector('.sl-next'));
          }

          if (_this5.currentImageIndex > 0) {
            _this5.show(_this5.domNodes.navigation.querySelector('.sl-prev'));
          }

          if (_this5.currentImageIndex < _this5.relatedElements.length - 1) {
            _this5.show(_this5.domNodes.navigation.querySelector('.sl-next'));
          }
        }

        if (_this5.relatedElements.length === 1) {
          _this5.hide(_this5.domNodes.navigation.querySelectorAll('.sl-prev, .sl-next'));
        } else {
          _this5.show(_this5.domNodes.navigation.querySelectorAll('.sl-prev, .sl-next'));
        }

        if (direction === 1 || direction === -1) {
          if (_this5.options.animationSlide) {
            _this5.slide(0, 100 * direction + 'px');

            setTimeout(function () {
              _this5.slide(_this5.options.animationSpeed / 1000, 0 + 'px');
            }, 50);
          }

          _this5.fadeIn(_this5.domNodes.image, 300, function () {
            _this5.isAnimating = false;

            _this5.setCaption(captionText, imageWidth);
          });
        } else {
          _this5.isAnimating = false;

          _this5.setCaption(captionText, imageWidth);
        }

        if (_this5.options.additionalHtml && !_this5.domNodes.additionalHtml) {
          _this5.domNodes.additionalHtml = document.createElement('div');

          _this5.domNodes.additionalHtml.classList.add('sl-additional-html');

          _this5.domNodes.additionalHtml.innerHTML = _this5.options.additionalHtml;

          _this5.domNodes.image.appendChild(_this5.domNodes.additionalHtml);
        }
      });
    }
  }, {
    key: "zoomPanElement",
    value: function zoomPanElement(targetOffsetX, targetOffsetY, targetScale) {
      this.currentImage.style[this.transitionPrefix + 'transform'] = 'translate(' + targetOffsetX + ',' + targetOffsetY + ') scale(' + targetScale + ')';
    }
  }, {
    key: "minMax",
    value: function minMax(value, min, max) {
      return value < min ? min : value > max ? max : value;
    }
  }, {
    key: "setZoomData",
    value: function setZoomData(initialScale, targetOffsetX, targetOffsetY) {
      this.currentImage.dataset.scale = initialScale;
      this.currentImage.dataset.translateX = targetOffsetX;
      this.currentImage.dataset.translateY = targetOffsetY;
    }
  }, {
    key: "hashchangeHandler",
    value: function hashchangeHandler() {
      if (this.isOpen && this.hash === this.initialLocationHash) {
        this.hashReseted = true;
        this.close();
      }
    }
  }, {
    key: "addEvents",
    value: function addEvents() {
      var _this6 = this;

      // resize/responsive
      this.addEventListener(window, 'resize.' + this.eventNamespace, function (event) {
        //this.adjustImage.bind(this)
        if (_this6.isOpen) {
          _this6.adjustImage();
        }
      });
      this.addEventListener(this.domNodes.closeButton, ['click.' + this.eventNamespace, 'touchstart.' + this.eventNamespace], this.close.bind(this));

      if (this.options.history) {
        setTimeout(function () {
          _this6.addEventListener(window, 'hashchange.' + _this6.eventNamespace, function (event) {
            if (_this6.isOpen) {
              _this6.hashchangeHandler();
            }
          });
        }, 40);
      }

      this.addEventListener(this.domNodes.navigation.getElementsByTagName('button'), 'click.' + this.eventNamespace, function (event) {
        if (!event.currentTarget.tagName.match(/button/i)) {
          return true;
        }

        event.preventDefault();
        _this6.controlCoordinates.swipeDiff = 0;

        _this6.loadImage(event.currentTarget.classList.contains('sl-next') ? 1 : -1);
      });
      this.addEventListener(this.domNodes.image, ['touchstart.' + this.eventNamespace, 'mousedown.' + this.eventNamespace], function (event) {
        if (event.target.tagName === 'A' && event.type === 'touchstart') {
          return true;
        }

        if (event.type === 'mousedown') {
          _this6.controlCoordinates.initialPointerOffsetX = event.clientX;
          _this6.controlCoordinates.initialPointerOffsetY = event.clientY;
          _this6.controlCoordinates.containerHeight = _this6.getDimensions(_this6.domNodes.image).height;
          _this6.controlCoordinates.containerWidth = _this6.getDimensions(_this6.domNodes.image).width;
          _this6.controlCoordinates.imgHeight = _this6.getDimensions(_this6.currentImage).height;
          _this6.controlCoordinates.imgWidth = _this6.getDimensions(_this6.currentImage).width;
          _this6.controlCoordinates.containerOffsetX = _this6.domNodes.image.offsetLeft;
          _this6.controlCoordinates.containerOffsetY = _this6.domNodes.image.offsetTop;
          _this6.controlCoordinates.initialOffsetX = parseFloat(_this6.currentImage.dataset.translateX);
          _this6.controlCoordinates.initialOffsetY = parseFloat(_this6.currentImage.dataset.translateY);
          _this6.controlCoordinates.capture = true;
        } else {
          _this6.controlCoordinates.touchCount = event.touches.length;
          _this6.controlCoordinates.initialPointerOffsetX = event.touches[0].clientX;
          _this6.controlCoordinates.initialPointerOffsetY = event.touches[0].clientY;
          _this6.controlCoordinates.containerHeight = _this6.getDimensions(_this6.domNodes.image).height;
          _this6.controlCoordinates.containerWidth = _this6.getDimensions(_this6.domNodes.image).width;
          _this6.controlCoordinates.imgHeight = _this6.getDimensions(_this6.currentImage).height;
          _this6.controlCoordinates.imgWidth = _this6.getDimensions(_this6.currentImage).width;
          _this6.controlCoordinates.containerOffsetX = _this6.domNodes.image.offsetLeft;
          _this6.controlCoordinates.containerOffsetY = _this6.domNodes.image.offsetTop;

          if (_this6.controlCoordinates.touchCount === 1)
            /* Single touch */
            {
              if (!_this6.controlCoordinates.doubleTapped) {
                _this6.controlCoordinates.doubleTapped = true;
                setTimeout(function () {
                  _this6.controlCoordinates.doubleTapped = false;
                }, 300);
              } else {
                _this6.currentImage.classList.add('sl-transition');

                if (!_this6.controlCoordinates.zoomed) {
                  _this6.controlCoordinates.initialScale = _this6.options.doubleTapZoom;

                  _this6.setZoomData(_this6.controlCoordinates.initialScale, 0, 0);

                  _this6.zoomPanElement(0 + "px", 0 + "px", _this6.controlCoordinates.initialScale);

                  if (!_this6.domNodes.caption.style.opacity && _this6.domNodes.caption.style.display !== 'none') {
                    _this6.fadeOut(_this6.domNodes.caption, 200);
                  }

                  _this6.controlCoordinates.zoomed = true;
                } else {
                  _this6.controlCoordinates.initialScale = 1;

                  _this6.setZoomData(_this6.controlCoordinates.initialScale, 0, 0);

                  _this6.zoomPanElement(0 + "px", 0 + "px", _this6.controlCoordinates.initialScale);

                  _this6.controlCoordinates.zoomed = false;
                }

                setTimeout(function () {
                  if (_this6.currentImage) {
                    _this6.currentImage.classList.remove('sl-transition');
                  }
                }, 200);
                return false;
              }

              _this6.controlCoordinates.initialOffsetX = parseFloat(_this6.currentImage.dataset.translateX);
              _this6.controlCoordinates.initialOffsetY = parseFloat(_this6.currentImage.dataset.translateY);
            } else if (_this6.controlCoordinates.touchCount === 2)
            /* Pinch */
            {
              _this6.controlCoordinates.initialPointerOffsetX2 = event.touches[1].clientX;
              _this6.controlCoordinates.initialPointerOffsetY2 = event.touches[1].clientY;
              _this6.controlCoordinates.initialOffsetX = parseFloat(_this6.currentImage.dataset.translateX);
              _this6.controlCoordinates.initialOffsetY = parseFloat(_this6.currentImage.dataset.translateY);
              _this6.controlCoordinates.pinchOffsetX = (_this6.controlCoordinates.initialPointerOffsetX + _this6.controlCoordinates.initialPointerOffsetX2) / 2;
              _this6.controlCoordinates.pinchOffsetY = (_this6.controlCoordinates.initialPointerOffsetY + _this6.controlCoordinates.initialPointerOffsetY2) / 2;
              _this6.controlCoordinates.initialPinchDistance = Math.sqrt((_this6.controlCoordinates.initialPointerOffsetX - _this6.controlCoordinates.initialPointerOffsetX2) * (_this6.controlCoordinates.initialPointerOffsetX - _this6.controlCoordinates.initialPointerOffsetX2) + (_this6.controlCoordinates.initialPointerOffsetY - _this6.controlCoordinates.initialPointerOffsetY2) * (_this6.controlCoordinates.initialPointerOffsetY - _this6.controlCoordinates.initialPointerOffsetY2));
            }

          _this6.controlCoordinates.capture = true;
        }

        if (_this6.controlCoordinates.mousedown) return true;

        if (_this6.transitionCapable) {
          _this6.controlCoordinates.imageLeft = parseInt(_this6.domNodes.image.style.left, 10);
        }

        _this6.controlCoordinates.mousedown = true;
        _this6.controlCoordinates.swipeDiff = 0;
        _this6.controlCoordinates.swipeYDiff = 0;
        _this6.controlCoordinates.swipeStart = event.pageX || event.touches[0].pageX;
        _this6.controlCoordinates.swipeYStart = event.pageY || event.touches[0].pageY;
        return false;
      });
      this.addEventListener(this.domNodes.image, ['touchmove.' + this.eventNamespace, 'mousemove.' + this.eventNamespace, 'MSPointerMove'], function (event) {
        if (!_this6.controlCoordinates.mousedown) {
          return true;
        }

        event.preventDefault();

        if (event.type === 'touchmove') {
          if (_this6.controlCoordinates.capture === false) {
            return false;
          }

          _this6.controlCoordinates.pointerOffsetX = event.touches[0].clientX;
          _this6.controlCoordinates.pointerOffsetY = event.touches[0].clientY;
          _this6.controlCoordinates.touchCount = event.touches.length;
          _this6.controlCoordinates.touchmoveCount++;

          if (_this6.controlCoordinates.touchCount > 1)
            /* Pinch */
            {
              _this6.controlCoordinates.pointerOffsetX2 = event.touches[1].clientX;
              _this6.controlCoordinates.pointerOffsetY2 = event.touches[1].clientY;
              _this6.controlCoordinates.targetPinchDistance = Math.sqrt((_this6.controlCoordinates.pointerOffsetX - _this6.controlCoordinates.pointerOffsetX2) * (_this6.controlCoordinates.pointerOffsetX - _this6.controlCoordinates.pointerOffsetX2) + (_this6.controlCoordinates.pointerOffsetY - _this6.controlCoordinates.pointerOffsetY2) * (_this6.controlCoordinates.pointerOffsetY - _this6.controlCoordinates.pointerOffsetY2));

              if (_this6.controlCoordinates.initialPinchDistance === null) {
                _this6.controlCoordinates.initialPinchDistance = _this6.controlCoordinates.targetPinchDistance;
              }

              if (Math.abs(_this6.controlCoordinates.initialPinchDistance - _this6.controlCoordinates.targetPinchDistance) >= 1) {
                /* Initialize helpers */
                _this6.controlCoordinates.targetScale = _this6.minMax(_this6.controlCoordinates.targetPinchDistance / _this6.controlCoordinates.initialPinchDistance * _this6.controlCoordinates.initialScale, 1, _this6.options.maxZoom);
                _this6.controlCoordinates.limitOffsetX = (_this6.controlCoordinates.imgWidth * _this6.controlCoordinates.targetScale - _this6.controlCoordinates.containerWidth) / 2;
                _this6.controlCoordinates.limitOffsetY = (_this6.controlCoordinates.imgHeight * _this6.controlCoordinates.targetScale - _this6.controlCoordinates.containerHeight) / 2;
                _this6.controlCoordinates.scaleDifference = _this6.controlCoordinates.targetScale - _this6.controlCoordinates.initialScale;
                _this6.controlCoordinates.targetOffsetX = _this6.controlCoordinates.imgWidth * _this6.controlCoordinates.targetScale <= _this6.controlCoordinates.containerWidth ? 0 : _this6.minMax(_this6.controlCoordinates.initialOffsetX - (_this6.controlCoordinates.pinchOffsetX - _this6.controlCoordinates.containerOffsetX - _this6.controlCoordinates.containerWidth / 2 - _this6.controlCoordinates.initialOffsetX) / (_this6.controlCoordinates.targetScale - _this6.controlCoordinates.scaleDifference) * _this6.controlCoordinates.scaleDifference, _this6.controlCoordinates.limitOffsetX * -1, _this6.controlCoordinates.limitOffsetX);
                _this6.controlCoordinates.targetOffsetY = _this6.controlCoordinates.imgHeight * _this6.controlCoordinates.targetScale <= _this6.controlCoordinates.containerHeight ? 0 : _this6.minMax(_this6.controlCoordinates.initialOffsetY - (_this6.controlCoordinates.pinchOffsetY - _this6.controlCoordinates.containerOffsetY - _this6.controlCoordinates.containerHeight / 2 - _this6.controlCoordinates.initialOffsetY) / (_this6.controlCoordinates.targetScale - _this6.controlCoordinates.scaleDifference) * _this6.controlCoordinates.scaleDifference, _this6.controlCoordinates.limitOffsetY * -1, _this6.controlCoordinates.limitOffsetY);

                _this6.zoomPanElement(_this6.controlCoordinates.targetOffsetX + "px", _this6.controlCoordinates.targetOffsetY + "px", _this6.controlCoordinates.targetScale);

                if (_this6.controlCoordinates.targetScale > 1) {
                  _this6.controlCoordinates.zoomed = true;

                  if (!_this6.domNodes.caption.style.opacity && _this6.domNodes.caption.style.display !== 'none') {
                    _this6.fadeOut(_this6.domNodes.caption, 200);
                  }
                }

                _this6.controlCoordinates.initialPinchDistance = _this6.controlCoordinates.targetPinchDistance;
                _this6.controlCoordinates.initialScale = _this6.controlCoordinates.targetScale;
                _this6.controlCoordinates.initialOffsetX = _this6.controlCoordinates.targetOffsetX;
                _this6.controlCoordinates.initialOffsetY = _this6.controlCoordinates.targetOffsetY;
              }
            } else {
            _this6.controlCoordinates.targetScale = _this6.controlCoordinates.initialScale;
            _this6.controlCoordinates.limitOffsetX = (_this6.controlCoordinates.imgWidth * _this6.controlCoordinates.targetScale - _this6.controlCoordinates.containerWidth) / 2;
            _this6.controlCoordinates.limitOffsetY = (_this6.controlCoordinates.imgHeight * _this6.controlCoordinates.targetScale - _this6.controlCoordinates.containerHeight) / 2;
            _this6.controlCoordinates.targetOffsetX = _this6.controlCoordinates.imgWidth * _this6.controlCoordinates.targetScale <= _this6.controlCoordinates.containerWidth ? 0 : _this6.minMax(_this6.controlCoordinates.pointerOffsetX - (_this6.controlCoordinates.initialPointerOffsetX - _this6.controlCoordinates.initialOffsetX), _this6.controlCoordinates.limitOffsetX * -1, _this6.controlCoordinates.limitOffsetX);
            _this6.controlCoordinates.targetOffsetY = _this6.controlCoordinates.imgHeight * _this6.controlCoordinates.targetScale <= _this6.controlCoordinates.containerHeight ? 0 : _this6.minMax(_this6.controlCoordinates.pointerOffsetY - (_this6.controlCoordinates.initialPointerOffsetY - _this6.controlCoordinates.initialOffsetY), _this6.controlCoordinates.limitOffsetY * -1, _this6.controlCoordinates.limitOffsetY);

            if (Math.abs(_this6.controlCoordinates.targetOffsetX) === Math.abs(_this6.controlCoordinates.limitOffsetX)) {
              _this6.controlCoordinates.initialOffsetX = _this6.controlCoordinates.targetOffsetX;
              _this6.controlCoordinates.initialPointerOffsetX = _this6.controlCoordinates.pointerOffsetX;
            }

            if (Math.abs(_this6.controlCoordinates.targetOffsetY) === Math.abs(_this6.controlCoordinates.limitOffsetY)) {
              _this6.controlCoordinates.initialOffsetY = _this6.controlCoordinates.targetOffsetY;
              _this6.controlCoordinates.initialPointerOffsetY = _this6.controlCoordinates.pointerOffsetY;
            }

            _this6.setZoomData(_this6.controlCoordinates.initialScale, _this6.controlCoordinates.targetOffsetX, _this6.controlCoordinates.targetOffsetY);

            _this6.zoomPanElement(_this6.controlCoordinates.targetOffsetX + "px", _this6.controlCoordinates.targetOffsetY + "px", _this6.controlCoordinates.targetScale);
          }
        }
        /* Mouse Move implementation */


        if (event.type === 'mousemove' && _this6.controlCoordinates.mousedown) {
          if (event.type == 'touchmove') return true;
          if (_this6.controlCoordinates.capture === false) return false;
          _this6.controlCoordinates.pointerOffsetX = event.clientX;
          _this6.controlCoordinates.pointerOffsetY = event.clientY;
          _this6.controlCoordinates.targetScale = _this6.controlCoordinates.initialScale;
          _this6.controlCoordinates.limitOffsetX = (_this6.controlCoordinates.imgWidth * _this6.controlCoordinates.targetScale - _this6.controlCoordinates.containerWidth) / 2;
          _this6.controlCoordinates.limitOffsetY = (_this6.controlCoordinates.imgHeight * _this6.controlCoordinates.targetScale - _this6.controlCoordinates.containerHeight) / 2;
          _this6.controlCoordinates.targetOffsetX = _this6.controlCoordinates.imgWidth * _this6.controlCoordinates.targetScale <= _this6.controlCoordinates.containerWidth ? 0 : _this6.minMax(_this6.controlCoordinates.pointerOffsetX - (_this6.controlCoordinates.initialPointerOffsetX - _this6.controlCoordinates.initialOffsetX), _this6.controlCoordinates.limitOffsetX * -1, _this6.controlCoordinates.limitOffsetX);
          _this6.controlCoordinates.targetOffsetY = _this6.controlCoordinates.imgHeight * _this6.controlCoordinates.targetScale <= _this6.controlCoordinates.containerHeight ? 0 : _this6.minMax(_this6.controlCoordinates.pointerOffsetY - (_this6.controlCoordinates.initialPointerOffsetY - _this6.controlCoordinates.initialOffsetY), _this6.controlCoordinates.limitOffsetY * -1, _this6.controlCoordinates.limitOffsetY);

          if (Math.abs(_this6.controlCoordinates.targetOffsetX) === Math.abs(_this6.controlCoordinates.limitOffsetX)) {
            _this6.controlCoordinates.initialOffsetX = _this6.controlCoordinates.targetOffsetX;
            _this6.controlCoordinates.initialPointerOffsetX = _this6.controlCoordinates.pointerOffsetX;
          }

          if (Math.abs(_this6.controlCoordinates.targetOffsetY) === Math.abs(_this6.controlCoordinates.limitOffsetY)) {
            _this6.controlCoordinates.initialOffsetY = _this6.controlCoordinates.targetOffsetY;
            _this6.controlCoordinates.initialPointerOffsetY = _this6.controlCoordinates.pointerOffsetY;
          }

          _this6.setZoomData(_this6.controlCoordinates.initialScale, _this6.controlCoordinates.targetOffsetX, _this6.controlCoordinates.targetOffsetY);

          _this6.zoomPanElement(_this6.controlCoordinates.targetOffsetX + "px", _this6.controlCoordinates.targetOffsetY + "px", _this6.controlCoordinates.targetScale);
        }

        if (!_this6.controlCoordinates.zoomed) {
          _this6.controlCoordinates.swipeEnd = event.pageX || event.touches[0].pageX;
          _this6.controlCoordinates.swipeYEnd = event.pageY || event.touches[0].pageY;
          _this6.controlCoordinates.swipeDiff = _this6.controlCoordinates.swipeStart - _this6.controlCoordinates.swipeEnd;
          _this6.controlCoordinates.swipeYDiff = _this6.controlCoordinates.swipeYStart - _this6.controlCoordinates.swipeYEnd;

          if (_this6.options.animationSlide) {
            _this6.slide(0, -_this6.controlCoordinates.swipeDiff + 'px');
          }
        }
      });
      this.addEventListener(this.domNodes.image, ['touchend.' + this.eventNamespace, 'mouseup.' + this.eventNamespace, 'touchcancel.' + this.eventNamespace, 'mouseleave.' + this.eventNamespace, 'pointerup', 'pointercancel', 'MSPointerUp', 'MSPointerCancel'], function (event) {
        if (_this6.isTouchDevice && event.type === 'touchend') {
          _this6.controlCoordinates.touchCount = event.touches.length;

          if (_this6.controlCoordinates.touchCount === 0)
            /* No touch */
            {
              /* Set attributes */
              _this6.setZoomData(_this6.controlCoordinates.initialScale, _this6.controlCoordinates.targetOffsetX, _this6.controlCoordinates.targetOffsetY);

              if (_this6.controlCoordinates.initialScale === 1) {
                _this6.controlCoordinates.zoomed = false;

                if (_this6.domNodes.caption.style.display === 'none') {
                  _this6.fadeIn(_this6.domNodes.caption, 200);
                }
              }

              _this6.controlCoordinates.initialPinchDistance = null;
              _this6.controlCoordinates.capture = false;
            } else if (_this6.controlCoordinates.touchCount === 1)
            /* Single touch */
            {
              _this6.controlCoordinates.initialPointerOffsetX = event.touches[0].clientX;
              _this6.controlCoordinates.initialPointerOffsetY = event.touches[0].clientY;
            } else if (_this6.controlCoordinates.touchCount > 1)
            /* Pinch */
            {
              _this6.controlCoordinates.initialPinchDistance = null;
            }
        }

        if (_this6.controlCoordinates.mousedown) {
          _this6.controlCoordinates.mousedown = false;
          var possibleDir = true;

          if (!_this6.options.loop) {
            if (_this6.currentImageIndex === 0 && _this6.controlCoordinates.swipeDiff < 0) {
              possibleDir = false;
            }

            if (_this6.currentImageIndex >= _this6.relatedElements.length - 1 && _this6.controlCoordinates.swipeDiff > 0) {
              possibleDir = false;
            }
          }

          if (Math.abs(_this6.controlCoordinates.swipeDiff) > _this6.options.swipeTolerance && possibleDir) {
            _this6.loadImage(_this6.controlCoordinates.swipeDiff > 0 ? 1 : -1);
          } else if (_this6.options.animationSlide) {
            _this6.slide(_this6.options.animationSpeed / 1000, 0 + 'px');
          }

          if (_this6.options.swipeClose && Math.abs(_this6.controlCoordinates.swipeYDiff) > 50 && Math.abs(_this6.controlCoordinates.swipeDiff) < _this6.options.swipeTolerance) {
            _this6.close();
          }
        }
      });
      this.addEventListener(this.domNodes.image, ['dblclick'], function (event) {
        if (_this6.isTouchDevice) return;
        _this6.controlCoordinates.initialPointerOffsetX = event.clientX;
        _this6.controlCoordinates.initialPointerOffsetY = event.clientY;
        _this6.controlCoordinates.containerHeight = _this6.getDimensions(_this6.domNodes.image).height;
        _this6.controlCoordinates.containerWidth = _this6.getDimensions(_this6.domNodes.image).width;
        _this6.controlCoordinates.imgHeight = _this6.getDimensions(_this6.currentImage).height;
        _this6.controlCoordinates.imgWidth = _this6.getDimensions(_this6.currentImage).width;
        _this6.controlCoordinates.containerOffsetX = _this6.domNodes.image.offsetLeft;
        _this6.controlCoordinates.containerOffsetY = _this6.domNodes.image.offsetTop;

        _this6.currentImage.classList.add('sl-transition');

        if (!_this6.controlCoordinates.zoomed) {
          _this6.controlCoordinates.initialScale = _this6.options.doubleTapZoom;

          _this6.setZoomData(_this6.controlCoordinates.initialScale, 0, 0);

          _this6.zoomPanElement(0 + "px", 0 + "px", _this6.controlCoordinates.initialScale);

          if (!_this6.domNodes.caption.style.opacity && _this6.domNodes.caption.style.display !== 'none') {
            _this6.fadeOut(_this6.domNodes.caption, 200);
          }

          _this6.controlCoordinates.zoomed = true;
        } else {
          _this6.controlCoordinates.initialScale = 1;

          _this6.setZoomData(_this6.controlCoordinates.initialScale, 0, 0);

          _this6.zoomPanElement(0 + "px", 0 + "px", _this6.controlCoordinates.initialScale);

          _this6.controlCoordinates.zoomed = false;

          if (_this6.domNodes.caption.style.display === 'none') {
            _this6.fadeIn(_this6.domNodes.caption, 200);
          }
        }

        setTimeout(function () {
          if (_this6.currentImage) {
            _this6.currentImage.classList.remove('sl-transition');
          }
        }, 200);
        _this6.controlCoordinates.capture = true;
        return false;
      });
    }
  }, {
    key: "getDimensions",
    value: function getDimensions(element) {
      var styles = window.getComputedStyle(element),
          height = element.offsetHeight,
          width = element.offsetWidth,
          borderTopWidth = parseFloat(styles.borderTopWidth),
          borderBottomWidth = parseFloat(styles.borderBottomWidth),
          paddingTop = parseFloat(styles.paddingTop),
          paddingBottom = parseFloat(styles.paddingBottom),
          borderLeftWidth = parseFloat(styles.borderLeftWidth),
          borderRightWidth = parseFloat(styles.borderRightWidth),
          paddingLeft = parseFloat(styles.paddingLeft),
          paddingRight = parseFloat(styles.paddingRight);
      return {
        height: height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom,
        width: width - borderLeftWidth - borderRightWidth - paddingLeft - paddingRight
      };
    }
  }, {
    key: "updateHash",
    value: function updateHash() {
      var newHash = 'pid=' + (this.currentImageIndex + 1),
          newURL = window.location.href.split('#')[0] + '#' + newHash;
      this.hashReseted = false;

      if (this.pushStateSupport) {
        window.history[this.historyHasChanges ? 'replaceState' : 'pushState']('', document.title, newURL);
      } else {
        // what is the browser target of this?
        if (this.historyHasChanges) {
          window.location.replace(newURL);
        } else {
          window.location.hash = newHash;
        }
      }

      if (!this.historyHasChanges) {
        this.urlChangedOnce = true;
      }

      this.historyHasChanges = true;
    }
  }, {
    key: "resetHash",
    value: function resetHash() {
      this.hashReseted = true;

      if (this.urlChangedOnce) {
        history.back();
      } else {
        if (this.pushStateSupport) {
          history.pushState('', document.title, window.location.pathname + window.location.search);
        } else {
          window.location.hash = '';
        }
      } //
      //in case an history operation is still pending


      clearTimeout(this.historyUpdateTimeout);
    }
  }, {
    key: "updateURL",
    value: function updateURL() {
      clearTimeout(this.historyUpdateTimeout);

      if (!this.historyHasChanges) {
        this.updateHash(); // first time
      } else {
        this.historyUpdateTimeout = setTimeout(this.updateHash.bind(this), 800);
      }
    }
  }, {
    key: "setCaption",
    value: function setCaption(captionText, imageWidth) {
      var _this7 = this;

      if (this.options.captions && captionText && captionText !== '' && typeof captionText !== "undefined") {
        this.hide(this.domNodes.caption);
        this.domNodes.caption.style.width = imageWidth + 'px';
        this.domNodes.caption.innerHTML = captionText;
        this.domNodes.image.appendChild(this.domNodes.caption);
        setTimeout(function () {
          _this7.fadeIn(_this7.domNodes.caption, 300);
        }, this.options.captionDelay);
      }
    }
  }, {
    key: "slide",
    value: function slide(speed, pos) {
      if (!this.transitionCapable) {
        return this.domNodes.image.style.left = pos;
      }

      this.domNodes.image.style[this.transitionPrefix + 'transform'] = 'translateX(' + pos + ')';
      this.domNodes.image.style[this.transitionPrefix + 'transition'] = this.transitionPrefix + 'transform ' + speed + 's linear';
    }
  }, {
    key: "getRelated",
    value: function getRelated(rel) {
      var elems;

      if (rel && rel !== false && rel !== 'nofollow') {
        elems = Array.from(this.elements).filter(function (element) {
          return element.getAttribute('rel') === rel;
        });
      } else {
        elems = this.elements;
      }

      return elems;
    }
  }, {
    key: "openImage",
    value: function openImage(element) {
      var _this8 = this;

      element.dispatchEvent(new Event('show.' + this.eventNamespace));

      if (this.options.disableScroll) {
        this.globalScrollbarWidth = this.toggleScrollbar('hide');
      }

      if (this.options.htmlClass && this.options.htmlClass !== '') {
        document.querySelector('html').classList.add(this.options.htmlClass);
      }

      document.body.appendChild(this.domNodes.wrapper);
      this.domNodes.wrapper.appendChild(this.domNodes.image);

      if (this.options.overlay) {
        document.body.appendChild(this.domNodes.overlay);
      }

      this.relatedElements = this.getRelated(element.rel);

      if (this.options.showCounter) {
        if (this.relatedElements.length == 1 && this.domNodes.wrapper.contains(this.domNodes.counter)) {
          this.domNodes.wrapper.removeChild(this.domNodes.counter);
        } else if (this.relatedElements.length > 1 && !this.domNodes.wrapper.contains(this.domNodes.counter)) {
          this.domNodes.wrapper.appendChild(this.domNodes.counter);
        }
      }

      this.isAnimating = true;
      this.currentImageIndex = this.relatedElements.indexOf(element);
      var targetURL = element.getAttribute(this.options.sourceAttr);
      this.currentImage = document.createElement('img');
      this.currentImage.style.display = 'none';
      this.currentImage.setAttribute('src', targetURL);
      this.currentImage.dataset.scale = 1;
      this.currentImage.dataset.translateX = 0;
      this.currentImage.dataset.translateY = 0;

      if (this.loadedImages.indexOf(targetURL) === -1) {
        this.loadedImages.push(targetURL);
      }

      this.domNodes.image.innerHTML = '';
      this.domNodes.image.setAttribute('style', '');
      this.domNodes.image.appendChild(this.currentImage);
      this.fadeIn(this.domNodes.overlay, 300);
      this.fadeIn([this.domNodes.counter, this.domNodes.navigation, this.domNodes.closeButton], 300);
      this.show(this.domNodes.spinner);
      this.domNodes.counter.querySelector('.sl-current').innerHTML = this.currentImageIndex + 1;
      this.domNodes.counter.querySelector('.sl-total').innerHTML = this.relatedElements.length;
      this.adjustImage();

      if (this.options.preloading) {
        this.preload();
      }

      setTimeout(function () {
        element.dispatchEvent(new Event('shown.' + _this8.eventNamespace));
      }, this.options.animationSpeed);
    } // utility

  }, {
    key: "addEventListener",
    value: function addEventListener(elements, events, callback, opts) {
      elements = this.wrap(elements);
      events = this.wrap(events);

      var _iterator = _createForOfIteratorHelper(elements),
          _step;

      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var element = _step.value;

          if (!element.namespaces) {
            element.namespaces = {};
          } // save the namespaces addEventListener the DOM element itself


          var _iterator2 = _createForOfIteratorHelper(events),
              _step2;

          try {
            for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
              var event = _step2.value;
              var options = opts || false;
              element.namespaces[event] = callback;
              element.addEventListener(event.split('.')[0], callback, options);
            }
          } catch (err) {
            _iterator2.e(err);
          } finally {
            _iterator2.f();
          }
        }
      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }
    }
  }, {
    key: "removeEventListener",
    value: function removeEventListener(elements, events) {
      elements = this.wrap(elements);
      events = this.wrap(events);

      var _iterator3 = _createForOfIteratorHelper(elements),
          _step3;

      try {
        for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
          var element = _step3.value;

          var _iterator4 = _createForOfIteratorHelper(events),
              _step4;

          try {
            for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
              var event = _step4.value;
              element.removeEventListener(event.split('.')[0], element.namespaces[event]);
              delete element.namespaces[event];
            }
          } catch (err) {
            _iterator4.e(err);
          } finally {
            _iterator4.f();
          }
        }
      } catch (err) {
        _iterator3.e(err);
      } finally {
        _iterator3.f();
      }
    }
  }, {
    key: "fadeOut",
    value: function fadeOut(elements, duration, callback) {
      var _this9 = this;

      elements = this.wrap(elements);

      var _iterator5 = _createForOfIteratorHelper(elements),
          _step5;

      try {
        for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
          var element = _step5.value;
          element.style.opacity = 1;
        }
      } catch (err) {
        _iterator5.e(err);
      } finally {
        _iterator5.f();
      }

      var step = 16.66666 / (duration || 300),
          fade = function fade() {
        var currentOpacity = parseFloat(elements[0].style.opacity);

        if ((currentOpacity -= step) < 0) {
          var _iterator6 = _createForOfIteratorHelper(elements),
              _step6;

          try {
            for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
              var element = _step6.value;
              element.style.display = "none";
              element.style.opacity = '';
            }
          } catch (err) {
            _iterator6.e(err);
          } finally {
            _iterator6.f();
          }

          callback && callback.call(_this9, elements);
        } else {
          var _iterator7 = _createForOfIteratorHelper(elements),
              _step7;

          try {
            for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
              var _element = _step7.value;
              _element.style.opacity = currentOpacity;
            }
          } catch (err) {
            _iterator7.e(err);
          } finally {
            _iterator7.f();
          }

          requestAnimationFrame(fade);
        }
      };

      fade();
    }
  }, {
    key: "fadeIn",
    value: function fadeIn(elements, duration, callback, display) {
      var _this10 = this;

      elements = this.wrap(elements);

      var _iterator8 = _createForOfIteratorHelper(elements),
          _step8;

      try {
        for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
          var element = _step8.value;
          element.style.opacity = 0;
          element.style.display = display || "block";
        }
      } catch (err) {
        _iterator8.e(err);
      } finally {
        _iterator8.f();
      }

      var opacityTarget = parseFloat(elements[0].dataset.opacityTarget || 1),
          step = 16.66666 * opacityTarget / (duration || 300),
          fade = function fade() {
        var currentOpacity = parseFloat(elements[0].style.opacity);

        if (!((currentOpacity += step) > opacityTarget)) {
          var _iterator9 = _createForOfIteratorHelper(elements),
              _step9;

          try {
            for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
              var element = _step9.value;
              element.style.opacity = currentOpacity;
            }
          } catch (err) {
            _iterator9.e(err);
          } finally {
            _iterator9.f();
          }

          requestAnimationFrame(fade);
        } else {
          var _iterator10 = _createForOfIteratorHelper(elements),
              _step10;

          try {
            for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
              var _element2 = _step10.value;
              _element2.style.opacity = '';
            }
          } catch (err) {
            _iterator10.e(err);
          } finally {
            _iterator10.f();
          }

          callback && callback.call(_this10, elements);
        }
      };

      fade();
    }
  }, {
    key: "hide",
    value: function hide(elements) {
      elements = this.wrap(elements);

      var _iterator11 = _createForOfIteratorHelper(elements),
          _step11;

      try {
        for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
          var element = _step11.value;
          element.dataset.initialDisplay = element.style.display;
          element.style.display = 'none';
        }
      } catch (err) {
        _iterator11.e(err);
      } finally {
        _iterator11.f();
      }
    }
  }, {
    key: "show",
    value: function show(elements, display) {
      elements = this.wrap(elements);

      var _iterator12 = _createForOfIteratorHelper(elements),
          _step12;

      try {
        for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) {
          var element = _step12.value;
          element.style.display = element.dataset.initialDisplay || display || 'block';
        }
      } catch (err) {
        _iterator12.e(err);
      } finally {
        _iterator12.f();
      }
    }
  }, {
    key: "wrap",
    value: function wrap(input) {
      return typeof input[Symbol.iterator] === 'function' && typeof input !== 'string' ? input : [input];
    }
  }, {
    key: "on",
    value: function on(events, callback) {
      events = this.wrap(events);

      var _iterator13 = _createForOfIteratorHelper(this.elements),
          _step13;

      try {
        for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) {
          var element = _step13.value;

          if (!element.fullyNamespacedEvents) {
            element.fullyNamespacedEvents = {};
          }

          var _iterator14 = _createForOfIteratorHelper(events),
              _step14;

          try {
            for (_iterator14.s(); !(_step14 = _iterator14.n()).done;) {
              var event = _step14.value;
              element.fullyNamespacedEvents[event] = callback;
              element.addEventListener(event, callback);
            }
          } catch (err) {
            _iterator14.e(err);
          } finally {
            _iterator14.f();
          }
        }
      } catch (err) {
        _iterator13.e(err);
      } finally {
        _iterator13.f();
      }

      return this;
    }
  }, {
    key: "off",
    value: function off(events) {
      events = this.wrap(events);

      var _iterator15 = _createForOfIteratorHelper(this.elements),
          _step15;

      try {
        for (_iterator15.s(); !(_step15 = _iterator15.n()).done;) {
          var element = _step15.value;

          var _iterator16 = _createForOfIteratorHelper(events),
              _step16;

          try {
            for (_iterator16.s(); !(_step16 = _iterator16.n()).done;) {
              var event = _step16.value;

              if (typeof element.fullyNamespacedEvents !== 'undefined' && event in element.fullyNamespacedEvents) {
                element.removeEventListener(event, element.fullyNamespacedEvents[event]);
              }
            }
          } catch (err) {
            _iterator16.e(err);
          } finally {
            _iterator16.f();
          }
        }
      } catch (err) {
        _iterator15.e(err);
      } finally {
        _iterator15.f();
      }

      return this;
    } // api

  }, {
    key: "open",
    value: function open(elem) {
      elem = elem || this.elements[0];

      if (typeof jQuery !== "undefined" && elem instanceof jQuery) {
        elem = elem.get(0);
      }

      this.initialImageIndex = this.elements.indexOf(elem);

      if (this.initialImageIndex > -1) {
        this.openImage(elem);
      }
    }
  }, {
    key: "next",
    value: function next() {
      this.loadImage(1);
    }
  }, {
    key: "prev",
    value: function prev() {
      this.loadImage(-1);
    } //close is exposed anyways..

  }, {
    key: "destroy",
    value: function destroy() {
      //remove all custom event listeners from elements
      this.off(['close.' + this.eventNamespace, 'closed.' + this.eventNamespace, 'nextImageLoaded.' + this.eventNamespace, 'prevImageLoaded.' + this.eventNamespace, 'change.' + this.eventNamespace, 'nextDone.' + this.eventNamespace, 'prevDone.' + this.eventNamespace, 'error.' + this.eventNamespace, 'changed.' + this.eventNamespace, 'next.' + this.eventNamespace, 'prev.' + this.eventNamespace, 'show.' + this.eventNamespace, 'shown.' + this.eventNamespace]);
      this.removeEventListener(this.elements, 'click.' + this.eventNamespace);
      this.removeEventListener(document.body, 'contextmenu.' + this.eventNamespace);
      this.removeEventListener(document.body, 'keyup.' + this.eventNamespace);
      this.removeEventListener(this.domNodes.navigation.getElementsByTagName('button'), 'click.' + this.eventNamespace);
      this.removeEventListener(this.domNodes.closeButton, 'click.' + this.eventNamespace);
      this.removeEventListener(window, 'resize.' + this.eventNamespace);
      this.removeEventListener(window, 'hashchange.' + this.eventNamespace);
      this.close();

      if (this.isOpen) {
        document.body.removeChild(this.domNodes.wrapper);
        document.body.removeChild(this.domNodes.overlay);
      }

      this.elements = null;
    }
  }, {
    key: "refresh",
    value: function refresh() {
      if (!this.initialSelector) {
        throw 'refreshing only works when you initialize using a selector!';
      }

      var options = this.options,
          selector = this.initialSelector;
      this.destroy();
      this.constructor(selector, options);
      return this;
    }
  }, {
    key: "hash",
    get: function get() {
      return window.location.hash.substring(1);
    }
  }]);

  return SimpleLightbox;
}();

(function ($, window, document, undefined) {
  'use strict';

  $.fn.simpleLightbox = function (options) {
    return new SimpleLightbox(this.get(), options);
  };
})(jQuery, window, document);



// nextgen-simple-light-box.js
jQuery(function($) {

    var selector = null;
    var lightbox = null;
    
    var nextgen_simplebox_options = {
        history: false,
        animationSlide: false,
        animationSpeed: 100,
        captionSelector: 'self'
    };

    var nextgen_simplelightbox_init = function() {
        selector = nextgen_lightbox_filter_selector($, $(".ngg-simplelightbox"));
        if (selector.length > 0) {
            lightbox = selector.simpleLightbox(nextgen_simplebox_options);
        }
    };

    nextgen_simplelightbox_init();

    $(window).on('refreshed', function() {
        if (typeof lightbox !== 'undefined') {
            lightbox.destroy();
        }
        selector = nextgen_lightbox_filter_selector($, $(".ngg-simplelightbox"));
        if (selector.length > 0) {
            lightbox = selector.simpleLightbox(nextgen_simplebox_options);
        }
    });
});

How To Add JavaScript Functions into WordPress

Assuming that the ‘Code Snippets’ plugin has already been installed, here’s a demonstration of adding a lightbox_context.js?ver=3.17

<?php
// closeout php to output JavaScript directly
?>
<script type="text/javascript">
    function nextgen_lightbox_filter_selector($, selector) 
{
		if (nextgen_lightbox_settings && nextgen_lightbox_settings.context) {
			var context = nextgen_lightbox_settings.context;
			
			if (context == 'all_images') {
				 selector = selector.add($('a > img').parent());
			}
			else if (context == 'all_images_direct') {
				selector = selector.add($('a[href] > img').parent()
				 		.filter(function() {
							var href = $(this).attr('href').toLowerCase();
							var ext = href.substring(href.length - 3);
							var ext2 = href.substring(href.length - 4);
							
							return (ext == 'jpg' || ext == 'gif' || ext == 'png' || ext2 == 'tiff' || ext2 == 'jpeg' || ext2 == 'webp');
						}));
			}
			else if (context == 'nextgen_and_wp_images') {
				 selector = selector.add($('a > img[class*="wp-image-"]').parent());
			}
			
			selector = selector.not('.gallery_link');
            selector = selector.not('.use_imagebrowser_effect');
		}		
		return selector;
};
</script>

WordPress Code Snippet Crashed My Site

Error Message:

ParseError thrown

syntax error, unexpected ‘$’, expecting variable (T_VARIABLE)

Resolution:

1. If still login to the site as Admin, call the safe-mode method via the site’s URL: https://kimconnect.com/wp-admin/admin.php?page=snippets&snippets-safe-mode=true

2. If Admin login session has expired or one cannot login add: define('CODE_SNIPPETS_SAFE_MODE', true); to wp-config.php

WordPress Plugin to Customize How Posts are Displayed

A. Install the ‘Display Posts’ plugin

B. Install the ‘Code Snippets’ plugin

C. Add this code snippet

/**
* Display Posts, use first attached image
* @link https://displayposts.com/2019/10/16/display-image-from-post-content-if-no-featured-image/
*/
function be_dps_first_image( $output, $original_atts, $image, $title, $date, $excerpt, $inner_wrapper, $content, $class, $author, $category_display_text ) {

// Only run if image_size is set and no featured image
if( empty( $original_atts['image_size'] ) || !empty( $image ) )
return $output;

$images = new WP_Query( array(
'post_parent' => get_the_ID(),
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'order' => 'ASC',
'fields' => 'ids',
));

if( !empty( $images->posts ) ) {
$image = '<a href="' . get_permalink() . '" class="image">' . wp_get_attachment_image( $images->posts[0], $original_atts['image_size'] ) . '</a>';
$output = '<' . $inner_wrapper . ' class="' . implode( ' ', $class ) . '">' . $image . $title . $date . $author . $category_display_text . $excerpt . $content . '</' . $inner_wrapper . '>';
}

return $output;
}
add_filter( 'display_posts_shortcode_output', 'be_dps_first_image', 10, 11 );

D. Add this CSS into Appearance > Customize > Additional CSS

.display-posts-listing.image-left .listing-item {
overflow: hidden;
margin-bottom: 32px;
width: 100%;
}

.display-posts-listing.image-left .image {
float: left;
margin: 0 16px 0 0;
}

.display-posts-listing.image-left .title {
display: block;
}

.display-posts-listing.image-left .excerpt-dash {
display: none;
}

WordPress NextGen Gallery Plugin Error

Error Message:

Failed to load plugin url: /bitnami/wordpress/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/attach_to_post/static/ngg_attach_to_post_tinymce_plugin.js?ver=3.17

Resolution:

Although the root cause hasn’t been determined. This issue has automatically resolved when the running WordPress ‘container’ or ‘pod’ has been destroyed and recreated.

Posting this note here in case someone Googles similar error messages. Also, this maybe is a symptom of an unhealthy container in a pod.

PowerShell: Update Cryptocurrency Prices in WordPress WooCommerce

In the absence of true integrated plugins in WordPress to perform scripted actions (updating certain fields of a WooCommerce item page), here’s an improvised PowerShell script that performs this rudimentary function. This is a slight improvement over the method of manually updating prices of certain cryptocurrencies; although, this should only be considered an intermediary development before devising a much more robust method that could run ‘headless’ in Python rather than PowerShell. Better yet, direct database updates would be the final solution for best performance and security.

# updateCoinPrices.ps1
# version: 0.01

# $bitcoinUri='https://api.coinstats.app/public/v1/coins/bitcoin?currency=USD'
# $etherUri='https://api.coinstats.app/public/v1/coins/ethereum?currency=USD'
# $dogeUri='https://api.coinstats.app/public/v1/coins/dogecoin?currency=USD'
$coinName='nano'
$coinApi='https://api.coinstats.app/public/v1/coins/nano?currency=USD'
$coinPage='https://dragoncoin.com/wp-admin/post.php?post=3302&action=edit'
$username='SERVICEACCOUNTUSERNAMEHERE'
$password='SOMEVERYCOMPLEXPASSWORDHERE'
$expectedPageTitle='Edit product ‹ Dragon Coin — WordPress'
$coinFieldId='_regular_price'
$buttonId='publish'
$updateSeconds=120
$markupPercent=10
$decimalPoints=2
$timesToDeterminePause=3

function autologinFirefox{
    param(
        $url,
        $username,
        $password,
        $usernameElementId='user_login',
        $passwordElementId='user_pass',
        $submitButtonId='wp-submit',
        $exitWhenDone=$false
    )
    $ErrorActionPreference = 'continue'
  
    # Initial validation
    if(!$url){write-warning "No URL specified.";return $false}
  
    function includeSelenium{
      Import-Module Selenium -ea SilentlyContinue
      if (!(get-module selenium -EA SilentlyContinue)){
              try{
                  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
                  if(!(Get-PackageProvider Nuget -ea SilentlyContinue)){Install-PackageProvider -Name NuGet -Force}
                  # Defining $ENV:ChocotaleyInstall so that it would be called by refreshenv
                  $ENV:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."   
                  Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
                  Install-Module Selenium -Force -Confirm:$False
                  Update-SessionEnvironment
                  Import-Module Selenium
                  return $true
              }catch{
                  write-warning $_
                  return $false
              }
          }else{
              return $true
          }
      }
  
      function includeFirefox{
          $firefoxInstalled=get-command "C:\Program Files\Mozilla Firefox\firefox.exe"
          if (!$firefoxInstalled){
              try{
                  if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                      [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
                      Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))}
                  choco install firefox --ignore-checksums -y
                  return $true
              }catch{
                  write-warning $_
                  return $false
              }
          }else{
              return $true
          }
      }
  
    function invokeSelenium($url,$userName,$password,$usernameElementId,$passwordElementId,$submitButtonId){
        $ErrorActionPreference = "Stop"
        function closeSelenium($selenium){
            if($selenium){
                $selenium.close()
                $selenium.quit()
                }
            }
        
        function noLogin($url){
            $seleniumFirefox = New-Object "OpenQA.Selenium.firefox.FirefoxDriver"
            $seleniumFirefox.Navigate().GoToURL($url)
            $title=$seleniumFirefox.Title
            write-host "Page reached: '$title'"
            $trustedSiteError=$title -match '^Error'
            if($trustedSiteError){
                write-host "An site trust issue has been detected. Adding root domain to the trusted sites list to resolve this issue."
                addDomainToTrustedSites $url              
                closeSelenium $seleniumIe                
                return $false
                }
            else{
                return $seleniumFirefox                
                }
            }
  
        function login($url,$username,$password,$usernameElementId,$passwordElementId,$submitButtonId){
          $seleniumFirefox = New-Object "OpenQA.Selenium.firefox.FirefoxDriver"
          $seleniumFirefox.Navigate().GoToURL($url)
          $userField=$seleniumFirefox.FindElementById($usernameElementId)
          $userField.clear()
          $userField.SendKeys($username)
          $passwordField=$seleniumFirefox.FindElementById($passwordElementId)
          $passwordField.SendKeys('')
          $passwordField.clear()           
          $passwordField.SendKeys($password)
          $submitButton=$seleniumFirefox.FindElementById($submitButtonId)
          $submitButton.Click()
          $title=$seleniumFirefox.Title
          write-host "Page reached: '$title'"
          $trustedSiteError=$title -match '^Error'
          if($trustedSiteError){
              write-warning "A site trust issue has been detected."                
              closeSelenium $seleniumFirefox                
              return $false
          }else{
              return $seleniumFirefox                
              }
          }
        
        try{  
            $isLogin=$userName,$password,$usernameElementId,$passwordElementId,$submitButtonId|?{!(!$_)}
            if($isLogin){
                write-host "Login to $url as $userName..."
                $firefox=login $url $userName $password $usernameElementId $passwordElementId $submitButtonId
            }else{
                write-host "Accesing $url without login..."
                $firefox=nologin $url
                }
            return $firefox
            }
        catch{            
            Write-Warning $Error[0].Exception.Message
            return $false
            }
      }
  
    try{
        write-host "Username`t: $username`r`nPassword`t: $(!(!$password))`r`nusernameElementId`t: $usernameElementId`r`npasswordElementId`t: $passwordElementId`r`nsubmitButtonId`t: $submitButtonId"
        if(!(includeSelenium)){
            write-warning "cannot proceed without selenium"
            return $null
          }
        if(!(includeFirefox)){
            write-warning "cannot proceed without Firefox"
            return $null
          }
        $null=get-process firefox -ea silentlycontinue|stop-process -force -ea silentlycontinue
        if(get-module selenium -ea SilentlyContinue){
            $isLogin=$userName,$password,$usernameElementId,$passwordElementId,$submitButtonId|?{!(!$_)}
            if($isLogin){                
                $selenium=invokeSelenium $url $userName $password $usernameElementId $passwordElementId $submitButtonId
            }else{
                write-host "No username or password are given. Proceeding to access only the provided URL."
                $selenium=invokeSelenium $url
                }
        }else{
            write-warning "Please manually verify that the Selenium module is installed before retrying this function."
            }
        if($selenium){            
            if($exitWhenDone){
                return $true
            }else{                
                return $selenium
                }
        }else{
            write-warning "There were errors preventing a successful login."
            return $false
            }
        }
    catch {
        write-warning "$_"        
        return $false
        }
  
}
  
function getCoinPrice{
  param(
      $commodityName='name',
      $commodityUri
  )
  $priceData=Invoke-RestMethod $commodityUri
  $price=$priceData.coin.price
  write-host "$(get-date) --- $commodityName price is $price"
  return [hashtable]@{
      $commodityName=$price
  }
}

function updateFieldValues{
    param(
        $browser,
        $fieldId,
        $value,
        $countMax=3
    )
    $erroractionpreference='stop'
    $success=$false
    $count=0
    do{
        try{
            $field=$browser.FindElementById($fieldId)
            $field.clear()
            $field.SendKeys($value)
            $success=$true
        }catch{
            write-warning $_ 
            $count++
            sleep 1
        } 
    }until($success -or ($count -ge $countMax))
}

function updateCoinPage{
    param(
        $coinName='nano',
        $coinApi='https://api.coinstats.app/public/v1/coins/nano?currency=USD',
        $coinPage='https://dragoncoin.com/wp-admin/post.php?post=3302&action=edit',
        $username,
        $password,
        $expectedPageTitle='Edit product ‹ Dragon Coin — WordPress',
        $coinFieldId='_regular_price',
        $buttonId='publish',
        $markupPercent=15,
        $decimalPoints=2,
        $updateSeconds=120,
        $timesToDeterminePause=3
    )

    function getFirefoxSession{
        param(
            $url,
            $username,
            $password
        )
        $firefoxSession=autologinFirefox $url $username $password
        $title=$firefoxSession.Title
        if($title -notlike $expectedPageTitle){
            $null=get-process firefox | stop-process -force
            $firefox=autoLoginFirefox $coinPage $username $password
            if($firefox.Title -ne $expectedPageTitle){$firefoxSession.Navigate().GoToURL($url)}
            clear-Host
            write-host "Connected to $url"
        }
        return $firefoxSession
    }
    
    $countDown=0
    Do{
        if($countDown -lt $timesToDeterminePause){
            $changesDetected=$false
            $title=$firefox.Title
            if($title -notlike $expectedPageTitle){
                $firefox=getFirefoxSession $coinPage $username $password
            }
            $coinPrice=[math]::round($(getCoinPrice $coinName $coinApi).$coinName*$(1+($markupPercent/100)),$decimalPoints)
            if(!$coinPrice){
                write-warning "Cannot update if $coinName price value is Null"
            }elseif($previousCoinPrice -ne $coinPrice){
                updateFieldValues $firefox $coinFieldId $coinPrice
                $changesDetected=$true
            }else{
                write-host "$coinName prices haven't changed."
            }
            if($changesDetected){
                $submitButton=$firefox.findElementById($buttonId)
                if($submitButton.enabled){
                    try{
                        $submitButton.Click()
                        write-host "$(get-date) --- $coinName price has been updated to $coinPrice"
                    }catch{
                        write-warning $_
                    }
                    $countDown=0
                
                }else{
                    write-warning "Firefox session is not working. Timing out 5 minutes..."
                    Start-Sleep 300
                }
            }else{
                $countDown++
            }
            $previousCoinPrice=$coinPrice
            write-host "time-out $updateSeconds seconds until next iteration"
            Start-Sleep -s $updateSeconds
        }else{
            Start-Sleep -s $updateSeconds
            $countDown=0
        }
    }until($false)
}

updateCoinPage -coinName $coinName `
    -coinApi $coinApi `
    -coinPage $coinPage `
    -username $username `
    -password $password `
    -expectedPageTitle $expectedPageTitle `
    -coinFieldId $coinFieldId `
    -buttonId $buttonId `
    -markupPercent $markupPercent `
    -decimalPoints $decimalPoints `
    -updateSeconds $updateSeconds `
    -timesToDeterminePause $timesToDeterminePause

WordPress: Remove ‘Built with Storefront & WooCommerce’ in footer

Credit: @jobthomas Automattic Happiness Engineer
https://wordpress.org/support/topic/remove-built-with-storefront-footer-link-2/

How to apply: search and install Code Snippets > add this snippet > apply everywhere

if ( ! function_exists( 'storefront_credit' ) ) {
	/**
	 * Display the theme credit
	 *
	 * @since  1.0.0
	 * @return void
	 */
	function storefront_credit() {
		?>
		<div class="site-info">
			<?php echo esc_html( apply_filters( 'storefront_copyright_text', $content = '&copy; ' . get_bloginfo( 'name' ) . ' ' . date( 'Y' ) ) ); ?>
		</div>
		<?php
	}
}

CSS: How To Set Color Gradient and Animation to Text and Background

// Static color gradient
.colorGradientClass {
    background-color: #ffffff !important;
    background-image: linear-gradient(315deg, #ffffff 0%, #d9d9d9 74%)  !important;
}

// Animated color gradient
.colorTransitionClass {
    background-image: 
        linear-gradient(to right, transparent, white),
        linear-gradient(to right,yellow, white, yellow, white);
    background-size: 100% 100%, 2000% 100%;
    animation: move 5s infinite;
}
@keyframes move {
 from {background-position: center center, left center;}
 to {background-position: center center, right center;}
}

// This will result in blinking text
.blinkingText {
    animation: blinker 5s linear infinite;
    color: red !important;
}
@keyframes blinker {
  50% {
    opacity: 0;
  }
}

How To Move WordPress Site To Kubernetes Cluster

a. Create backups of source files and database

  - Logon to Current Hosting Provider to make backups
  - Files:
    - Assuming cPanel:
      - Login to cPanel
      - Click on 'File Manager'
      - Select public_html or the directory containing WordPress files
      - Select Compress from the top-right menu
      - Select 'Bzip2ed Tar Archive' (better compression than Gzip)
      - Click 'Compress File(s)' and wait for the process to finish
      - Right-click the newly generated public_html.tar.bz2 from cPanel File Manager > select Download
      - Find the file in a default download directory (e.g. /home/$(whoami)/Downloads/public_html.tar.bz2)
  - Database:
    - Assuming cPanel with phpMyAdmin
      - Click 'phpMyAdmin' from the 'DATABASES' control group
      - Click 'Export'
      - Set Export method = Quick, Format = Custom
      - Click Go
      - Find the *.sql file being downloaded into a default download directory (e.g. /home/$(whoami)/Downloads/localhost.sql)

b. Install Bitnami WordPress in a Kubernetes Cluster

# Add helm chart if not already available
helm repo add bitnami https://charts.bitnami.com/bitnami

# Install WordPress with Dynamic NFS Provisioning
# Documentation: https://hub.kubeapps.com/charts/bitnami/wordpress/10.0.1
# Set variables
appName=kimconnectblog
domainName=blog.kimconnect.com
wordpressusername=kimconnect
wordpressPassword=SOMEPASSWORDHERE
rootPassword=SOMEPASSWORDHERE2
storageClass=nfs-client
# Install
helm install $appName bitnami/wordpress \
  --set persistence.accessMode=ReadWriteMany,persistence.storageClass=nfs-client \
  --set mariadb.primary.persistence.storageClass=nfs-client \
  --set wordpressUsername=$wordpressusername,wordpressPassword=$wordpressPassword \
  --set mariadb.auth.rootPassword=$rootPassword \
  --set mariadb.auth.password=$rootPassword \
  --set ingress.enabled=true,ingress.hostname=$domainName
# Patch the deployed ingress with an existing SSL cert
# Assuming the $appName-cert has already been generated
appName=kimconnectblog
domainName=blog.kimconnect.com
certName=$appName-cert
serviceName=$appName-wordpress
servicePort=80
cat <<EOF > $appName-patch.yaml
spec:
  tls:
  - hosts:
    - $domainName
    secretName: $certName
  rules:
  - host: $domainName
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: $serviceName
            port:
              number: $servicePort         
EOF
kubectl patch ingress/$appName-wordpress -p "$(cat $appName-patch.yaml)"

c. Import files and database onto new hosting server

  - Database:
    - Access DB server and import sql dump
      podName=kimconnectblog-mariadb-0
      kubectl exec --stdin --tty $podName -- /bin/bash
      rootPassword=SOMEPASSWORD
      echo "show databases;" | mysql -u root -p$rootPassword
      MariaDB [(none)]> show databases;exit;
        +--------------------+
        | Database           |
        +--------------------+
        | bitnami_wordpress  |
        | information_schema |
        | mysql              |
        | performance_schema |
        | test               |
        +--------------------+
        5 rows in set (0.009 sec)
      oldDb=kimconne_blog
      sqlDump=/bitnami/mariadb/data/kimconnect.sql
      mysql -uroot -p$rootPassword test < $sqlDump
      grantUser=bn_wordpress # this is the default Bitnami WordPress user
      echo "GRANT ALL PRIVILEGES ON $oldDb.* TO $grantUser;" | mysql -uroot -p$rootPassword
      #echo "create database $databaseName;" | mysql -uroot -p$rootPassword
      #mysql -uroot -p$rootPassword $oldDb -sNe 'show tables' | while read table; do mysql -uroot -p$rootPassword -sNe "RENAME TABLE $oldDb.$table TO $newDb.$table"; done
      #echo "create user kimconne_blog@localhost;grant all privileges on kimconne_blog.* to 'kimconne_blog';"| mysql -uroot -p$rootPassword
      #ALTER USER 'kimconne_blog'@'localhost' IDENTIFIED BY 'SOMEPASSWORDHERE';
  - Files:
    - Assuming nfs:
      nfsShare=k8s
      nfsServer=10.10.10.5
      sharePath=/volume1/$nfsShare
      mountPoint=/mnt/$nfsShare
      sudo mkdir $mountPoint
      sudo mount -t nfs $nfsServer:$sharePath $mountPoint # Test mounting
      sudo mount | grep $nfsShare # validate mount
      # Assuming Kubernetes NFS
      # sudo mv /home/$(whoami)/Downloads/localhost.sql $mountPoint/path_to_default-data-sitename-mariadb/data/localhost.sql
      # sudo mv /home/$(whoami)/Downloads/public_html.tar.bz2 $mountPoint/public_html.tar.bz2
      bz2File=/mnt/k8s/kimconnectblog/public_html.tar.bz2
      containerPath=/mnt/k8s/default-kimconnectblog-wordpress-pvc-9f1dd4bd-81f3-489f-9b76-bf70f4fd291c/wordpress/wp-content
      tar -xf $bz2File -C $containerPath
      cd $containerPath
      mv public_html/wp-content wp-content
      vim wp-config.php # edit wp config to match the imported database and its prefix