Listowanie folderu w node

Aby wylistować folder z plikami i zapisać listę plików jako json na potrzeby tej strony – blog.grzgorztomicki.pl należy oczywiście mieć najpierw zainstalowane nodejs

Tworzymy plik powiedzmy list_images.js

Najpierw używamy wbudowanej biblioteki fs – fs

const fs = require("fs");
const nameGallery = "chiny";
const name = "test";

const test = [];
const author = "Grzegorz Tomicki";

let now = new Date();
let date = now.getDate() + "." + (now.getMonth()+1) + "." + now.getFullYear();

let datePublished = new Date().toISOString().slice(0,10);
let dateModified = datePublished;

Czytamy folder z plikami jpg za pomocą fs.readdir

fs.readdir(`./sources/images/${nameGallery}/1200/`, function (err, files) {
if (err)
throw err;
for (let index in files) {

Tutaj używamy czegoś nowego z ES6 a mianowicie „template string”. Używamy do tego znaku obok jeden na klawiaturze ` a zmienne podajemy w postaci ${} coś wspaniałego 🙂
Poniżej są dwa przykłady użycia a mianowicie path jak i template – wiele linii jak widać nie trzeba sklejać za pomocą „+” cud malina 🙂

let path = `{"path":"./images/${nameGallery}/","img":"${files[index]}","alt":""}`;
test.push("\r\n\t\t\t\t" + path);
}

const template = (`
{
  "head": {
    "title": "",
    "description": ""
  },
  "body": {
    "title": "",
    "date": "${date}",
    "text": "",
    "items": [$ {
      test
    }]
  },
  "footer": {
    "js": "../../build/js/boundle.min.js"
  },
  "schema": {
    "datePublished": "${datePublished}",
    "dateModified": "${dateModified}",
    "author": "${author}"
  }
}
`);

Zapisujemy plik za pomocą fs.writeFile

fs.writeFile(`./sources/data/site/${name}.json`, template, function (err) {});
});

Aby oczywiście uruchomić ten plik należy go uruchomić przez komendę w konsoli node list_images.js
Cały proces tworzenia strony można znaleźć pod tym linkiem tutaj

Jak przyspieszyć WordPress

Poniżej kilka trików które pozwoliły mi przyspieszyć bloga z 32/22 w PageSpeed Insigth do 90/100 – oczywiście to zależy od strony, ilości zdjęć, szybkości serwera itp, wszystko ma znaczenie.
I to jest blog ze zdjęciami (blog.grzegorztomicki.pl), jak dla mnie bardzo zacnie 🙂

1. Będziemy potrzebować dostęp do .htaccess
W którym dodajemy (nie będę opisywał co dokładnie jest w nim robione bo wszystko można znaleźć opisane w internecie)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
#</IfModule>



<IfModule mod_deflate.c>
  # Compress HTML, CSS, JavaScript, Text, XML and fonts
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE application/x-font
  AddOutputFilterByType DEFLATE application/x-font-opentype
  AddOutputFilterByType DEFLATE application/x-font-otf
  AddOutputFilterByType DEFLATE application/x-font-truetype
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE font/opentype
  AddOutputFilterByType DEFLATE font/otf
  AddOutputFilterByType DEFLATE font/ttf
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE image/x-icon
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/xml

  # Remove browser bugs (only needed for really old browsers)
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
  Header append Vary User-Agent
</IfModule>

<IfModule mod_expires.c>
  ExpiresActive on

# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"

# cache.appcache needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"



# Your document html
  ExpiresByType text/html                 "access plus 0 seconds"

# Data
  ExpiresByType text/xml                  "access plus 0 seconds"
  ExpiresByType application/xml           "access plus 0 seconds"
  ExpiresByType application/json          "access plus 0 seconds"

# RSS feed
  ExpiresByType application/rss+xml       "access plus 1 hour"

# Favicon (cannot be renamed)
  ExpiresByType image/x-icon              "access plus 1 week"

# Media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpg                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"

# HTC files  (css3pie)
  ExpiresByType text/x-component          "access plus 1 month"

# Webfonts
  ExpiresByType font/truetype             "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType application/x-font-woff   "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"

# CSS and JavaScript
  ExpiresByType text/css                  "access plus 1 year"
  ExpiresByType application/javascript    "access plus 1 year"
  ExpiresByType text/javascript           "access plus 1 year"

  <IfModule mod_headers.c>
    Header append Cache-Control "public"
    Header unset ETag
    FileETag None
  </IfModule>

</IfModule>

2. Instalujemy „BJ Lazy Load”
Jest to plugin do „wolnego” ładowania się obrazków więcej na https://wordpress.org/plugins/bj-lazy-load/

3. Instalujemy „WP Super Cache”
Włączamy w nim w zakładce „Easy” na Caching on (Recommended) – więcej na https://wordpress.org/plugins/wp-super-cache/, czy działa można sprawdzić na stronie na samym dole powinien pojawić się mniej więcej taki wpis.

<!--HTML compressed, size saved 9.28%. From 64384 bytes, now 58410 bytes-->
<!-- Dynamic page generated in 0.974 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2016-01-01 11:42:46 -->


4. Najtrudniejsza część instalujemy „Above The Fold Optimization”

Nie włączamy go.
Klikamy na zakładkę „Extract Full CSS” pojawia się lista wszystkich css które są na stronie oraz textarea z połączonymi wszystkimi plikami css „Full CSS” – textarea.

Kopiuję Ctrl + C do strony https://jonassebastianohlsson.com/criticalpathcssgenerator/

Na tej stronie w pole:
1. URL podaję adres blog lub specyficznej strony na naszym blogu,
2. W „FULL CSS” wklejam cały nasz wygenerowany css.
3. Klikamy „Create Critical Path CSS”, po dłuższej chwili (w zależności od tego jak duży jest css w moim wypadku wszystkie połączone cssy są wielkości ponad 0,5MB) pojawia się okno textarea „CRITICAL PATH CSS” a w nim dostosowane css które kopiuję do pliku header.php od razu pod ,
oczywiście można to zrobić ładniej ale że jestem lazy to mi się nie chciało 🙂

Uwaga z tego skróconego css usuwamy wszystkie @font-face eot,ttf itd czcionki.

5. Wygląd -> Edytor -> otwieramy Funkcje motywu (functions.php)
Na samym końcu dodaję:

<?php
function remove_head_scripts() {
   remove_action('wp_head', 'wp_print_scripts');
   remove_action('wp_head', 'wp_print_head_scripts', 9);
   remove_action('wp_head', 'wp_enqueue_scripts', 1);

   remove_action( 'wp_head', 'rsd_link');
   remove_action( 'wp_head', 'wlwmanifest_link');
   remove_action( 'wp_head', 'wp_generator');
   remove_action( 'wp_head', 'start_post_rel_link');
   remove_action( 'wp_head', 'index_rel_link');
   remove_action( 'wp_head', 'adjacent_posts_rel_link');
   remove_action( 'wp_head', 'wp_shortlink_wp_head');

   add_action('wp_footer', 'wp_print_scripts', 5);
   add_action('wp_footer', 'wp_enqueue_scripts', 5);
   add_action('wp_footer', 'wp_print_head_scripts', 5);
}
add_action( 'wp_enqueue_scripts', 'remove_head_scripts' );

?>

Powyższa funkcja przenosi cały script do sekcji footer

Dodaję również jedną przydatną rzecz czyli kompresję html – usuwanie zbędnych spacji, nowych linii itd, również dodajemy w function.php na samym dole

<?php
class WP_HTML_Compression
{
    // Settings
    protected $compress_css = true;
    protected $compress_js = true;
    protected $info_comment = true;
    protected $remove_comments = true;

    // Variables
    protected $html;
    public function __construct($html)
    {
     if (!empty($html))
     {
         $this->parseHTML($html);
     }
    }
    public function __toString()
    {
     return $this->html;
    }
    protected function bottomComment($raw, $compressed)
    {
     $raw = strlen($raw);
     $compressed = strlen($compressed);
     
     $savings = ($raw-$compressed) / $raw * 100;
     
     $savings = round($savings, 2);
     
     return '<!--HTML compressed, size saved '.$savings.'%. From '.$raw.' bytes, now '.$compressed.' bytes-->';
    }
    protected function minifyHTML($html)
    {
     $pattern = '/<(?<script>script).*?<\/script\s*>|<(?<style>style).*?<\/style\s*>|<!(?<comment>--).*?-->|<(?<tag>[\/\w.:-]*)(?:".*?"|\'.*?\'|[^\'">]+)*>|(?<text>((<[^!\/\w.:-])?[^<]*)+)|/si';
     preg_match_all($pattern, $html, $matches, PREG_SET_ORDER);
     $overriding = false;
     $raw_tag = false;
     // Variable reused for output
     $html = '';
     foreach ($matches as $token)
     {
         $tag = (isset($token['tag'])) ? strtolower($token['tag']) : null;
         
         $content = $token[0];
         
         if (is_null($tag))
         {
             if ( !empty($token['script']) )
             {
                 $strip = $this->compress_js;
             }
             else if ( !empty($token['style']) )
             {
                 $strip = $this->compress_css;
             }
             else if ($content == '<!--wp-html-compression no compression-->')
             {
                 $overriding = !$overriding;
                 
                 // Don't print the comment
                 continue;
             }
             else if ($this->remove_comments)
             {
                 if (!$overriding && $raw_tag != 'textarea')
                 {
                     // Remove any HTML comments, except MSIE conditional comments
                     $content = preg_replace('/<!--(?!\s*(?:\[if [^\]]+]|<!|>))(?:(?!-->).)*-->/s', '', $content);
                 }
             }
         }
         else
         {
             if ($tag == 'pre' || $tag == 'textarea')
             {
                 $raw_tag = $tag;
             }
             else if ($tag == '/pre' || $tag == '/textarea')
             {
                 $raw_tag = false;
             }
             else
             {
                 if ($raw_tag || $overriding)
                 {
                     $strip = false;
                 }
                 else
                 {
                     $strip = true;
                     
                     // Remove any empty attributes, except:
                     // action, alt, content, src
                     $content = preg_replace('/(\s+)(\w++(?<!\baction|\balt|\bcontent|\bsrc)="")/', '$1', $content);
                     
                     // Remove any space before the end of self-closing XHTML tags
                     // JavaScript excluded
                     $content = str_replace(' />', '/>', $content);
                 }
             }
         }
         
         if ($strip)
         {
             $content = $this->removeWhiteSpace($content);
         }
         
         $html .= $content;
     }
     
     return $html;
    }
     
    public function parseHTML($html)
    {
     $this->html = $this->minifyHTML($html);
     
     if ($this->info_comment)
     {
         $this->html .= "\n" . $this->bottomComment($html, $this->html);
     }
    }
   
    protected function removeWhiteSpace($str)
    {
     $str = str_replace("\t", ' ', $str);
     $str = str_replace("\n",  '', $str);
     $str = str_replace("\r",  '', $str);
     
     while (stristr($str, '  '))
     {
         $str = str_replace('  ', ' ', $str);
     }
     
     return $str;
    }
}

function wp_html_compression_finish($html)
{
    return new WP_HTML_Compression($html);
}

function wp_html_compression_start()
{
    ob_start('wp_html_compression_finish');
}
add_action('get_header', 'wp_html_compression_start');
?>

6. Najważniejsza część pracy nad przyspieszeniem strony to oczywiście modyfikacja zdjęć.
Zdjęcia powinny mieć kompresję ustawioną na 70%, usunięcie wszystkich metadanych ze zdjęć, powinny być zapisane jako progressive, wielkość dostosowana do wielkości miejsca w którym się pojawiają zdjęcia.

7. W przyszłości zdjęcie przerobię na nowe rozwiązania dla wersji mobilnych
Więcej na https://scottjehl.github.io/picturefill/examples/demo-01.html oraz https://ericportis.com/posts/2014/srcset-sizes/

Tego rozwiązania używa między innymi theguardian.com a to wygląda mniej więcej tak (to jest jeden plik tylko że w różnych wielkościach):

<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source media="(min-width: 1300px)" sizes="1300px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=1300&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=10e2200031125f835392892e9f813634 1300w">
<source media="(min-width: 1140px)" sizes="1140px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=1140&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=13d6ffcf420fb95bff93b5ca7a418f8e 1140w">
<source media="(min-width: 980px)" sizes="1125px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=1125&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=63fa2db8b2dccfc0ab408e25551c32e6 1125w">
<source media="(min-width: 740px)" sizes="965px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=965&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=c85b88b5fa49b0854485bd720d689155 965w">
<source media="(min-width: 660px)" sizes="725px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=725&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=048d9b371a22cebba3bb7dcd8e2d619c 725w">
<source media="(min-width: 480px)" sizes="645px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=645&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=97c169eb0122c0552b270a4be96e0410 645w">
<source media="(min-width: 0px)" sizes="465px" srcset="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=465&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=b4476e761590a0a1175cb66c5bfd8a8d 465w">
<!--[if IE 9]></video><![endif]-->
<img class="maxed responsive-img" itemprop="contentUrl" alt="Francesca Chaouqui" src="https://i.guim.co.uk/img/media/2fa72163af4712211cbf9abd929cc377d4af267b/0_0_4928_2957/master/4928.jpg?w=300&amp;q=85&amp;auto=format&amp;sharp=10&amp;s=c69f163a8116e3ac4ad7cc8170a1ad11">
</picture>

A poniżej wynik mojej pracy.

desctop

mobile

W webpagetest też jest bardzo pozytywnie 🙂

webpage

jQuery UI Autocomplete highlight

Mamy sobie prosty formularz. Do podpowiedzi użyjemy pluginu jquery autocomplite. Chcemy również dodać wyboldowanie podpowiedzi.
Dokładnie uzyskać taki efekt „meble, Waszawa” gdzie highlight jest zmianą koloru na czarny i wyboldowaniem.

<form id="search" action="/" method="GET" role="search">
    <fieldset>
        <input id="query" type="text" name="query" value="" maxlength="255" autocomplete="off" placeholder="Produkt, usługa, miasto, ulica">
        <button type="submit" id="button-search"></button>
    </fieldset>
</form>

A teraz trochę js 😉

$("#query").autocomplete({
        'minLength': 2,
        'delay': 0,
        create: function () {
            $(this).data('ui-autocomplete')._renderItem = function (ul, item) {
                var re = new RegExp("^" + this.term, "i") ;
                var t = item.label.replace(re,"<span class='highlight'>" + this.term + "</span>");

                return $( "<li></li>" )
                    .data( "item.autocomplete", item )
                    .append(t)
                    .appendTo( ul );
            };
        },
        source: function (request, response) {
            //escapeRegex - /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"
            var re = $.ui.autocomplete.escapeRegex(request.term);
            var matcher = new RegExp("^" + re, "i");

            $.getJSON("/autocomplete/?what=" + encodeURIComponent(request.term), function (searchWord) {
                var match = $.grep(searchWord, function (item, index) {
                    return matcher.test(item);
                });
                response(match);
            });
        },
        open: function (e, ui) {
            //autocomplete przesuwamy o 1px to góry, aby pozbyć się ramki
            $(".ui-autocomplete:visible").css({top: "-=1"});
        }
    });

A tak wygląda wynik naszej pracy.

autosuggest

Wyrażenie regularne walidacja daty

    function testdate(fld, form) {

        var blad = ”;

        if(fld.value!=”) {
            var data = fld.value;

            var RegDate=/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/i;
            var wynik = RegDate.test(data);
            if (wynik == false)
                blad += ” – data\\n”;
        }

        if(blad!=”)
              blad = „Proszę wypełnić pola:\n” + blad;

        if(blad!=”)  {
            alert(blad);
            return false;
        }
 }

Dynamiczne dodanie markera A do strony

var rA = document.createElement(‚a’);

rA.setAttribute(‚href’, ‚javascript:removeRow(\’n’ + count + ‚\’);’);
rA.appendChild(document.createTextNode(‚Remove’));

function removeRow(rowId) {
 var elem = document.getElementById(rowId));

}

Obliczania na formularzu

var fld_name;
var idx, nr;
cnt = f.elements.length;

for(i=0; i<cnt; i++){
 if(f.elements[i].name.indexOf(„kwota_budzetu_”)!=-1) {
  fld_name = f.elements[i].name;
  idx = fld_name.lastIndexOf(„_”)+1;
  nr = fld_name.substr(idx);

  f.elements[„roznica_proj_”+nr].value =
   eval(f.elements[„kwota_budzetu_”+nr].value) –
   eval(f.elements[„wartosc_proj_”+nr].value);
 }
}

Zwiększenie wysokości pola textarea

function tekstsize(h) {
 var elem = document.getElementById(„main_text”);
 elem.style.height = h+’px’;
}

<input type=”button” name=”wybor” value=” – ” onClick=”tekstsize(150)”> &nbsp;
<input type=”button” name=”wybor” value=” + ” onClick=”tekstsize(1000)”>

<textarea name=”main_text” id=”main_text” cols=’90’ rows=’10’ wrap=’virtual’>

Dymek nad komórką tabeli HTML

Dymek nad komórką tabeli HTML. Dymek generowany jest w Java Script

 <!DOCTYPE HTML PUBLIC „-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
 <HEAD>
  <TITLE> New Document </TITLE>
 </HEAD>

 <BODY>

 <SCRIPT LANGUAGE=”JavaScript”>
<!–
 function vDymek(nazwa) {

     var div = document.getElementById(nazwa);
     div.style.display = ‚block’;

     //var par = div.parentNode.parentNode;
     //alert(div.parentNode.parentNode.style.height );
 }

 function hDymek(nazwa) {
     var div = document.getElementById(nazwa);
     div.style.display = ‚none’;
 }

//–>
</SCRIPT>
  <TABLE border=”0″ bgcolor=”#F0F0F0″>
  <TR>
    <TD style=”height: 20px;”><div style=”position: relative;” onMouseOver=”vDymek(‚dymekA’)” onMouseOut=”hDymek(‚dymekA’)”>MS-G Tekst”<div id=”dymekA” style=”position: absolute; display: none; border: 1px solid; width: 700px; top: 0; left: 0; background-color: #efefef;”>MS-G Tekst” Tłumacz Przysięgły Języka Francuskiego, Czeskiego i Rosyjskiego Małgorzata Sibilska-Górska</div></div></TD>
    <TD>druga kolumna</TD>
    <TD>trzecia kolumna</TD>
  </TR>
  </TABLE>
 </BODY>
</HTML>