Category

Category: Javascript

Javascript – Check if an array contains one or more items from another array.

While there are other interesting ways to achieve this such as arrow functions, I’ve decided to share with you a simple yet effective way that is supported in all major browsers. Refer to code and usage below:

/**
 * @description determine if an array contains one or more items from another array.
 * @param {array} haystack the array to search.
 * @param {array} arr the array providing items to check for in the haystack.
 * @return {boolean} true|false if haystack contains at least one item from arr.
 */
function check_arrayA_items_exists_in_arrayB(haystack, arr) {
    return arr.some(function (v) {
        return haystack.indexOf(v) >= 0;
    });
}

 

Usage:

var userProducts = {
	action: "getProducts",
	products: ["978","970", "979", "980", "981"]
}
var globalProducts= ["978","970"];

if(check_arrayA_items_exists_in_arrayB(userProducts.products, globalProducts)){
	console.log('item/s exists');
}

Print Content Of Bootstrap Modal Window

 

<!doctype html>
<html ng-app="ui.bootstrap.demo">
<head>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
	<style>
	@media print {
		body * {
			visibility: hidden;
		}
		.modal-content * {
			visibility: visible;
			overflow: visible;
		}
		.main-page * {
			display: none;
		}
		.modal {
			position: absolute;
			left: 0;
			top: 0;
			margin: 0;
			padding: 0;
			min-height: 550px;
			visibility: visible;
			overflow: visible !important; /* Remove scrollbar for printing. */
		}
		.modal-dialog {
			visibility: visible !important;
			overflow: visible !important; /* Remove scrollbar for printing. */
		}
	}
	</style>
</head>
<body>
	<div>
		<div id="myModal" class="modal fade" role="dialog">
			<div class="modal-dialog">
				<div class="modal-content">
					<div class="modal-header">
						<a href="javascript:window.print()" class="btn btn-primary pull-right">Print</a>
						<h3 class="modal-title">I'm a modal!</h3>
					</div>
					<div class="modal-body">
						<ul>
							<?php for( $i = 1; $i <= 20; $i++ ): ?>
								<li><a href="#"><?php echo 'Item ' . $i; ?></a></li>
							<?php endfor; ?>
						</ul>
					</div>
					<div class="modal-footer">
						<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
						<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
					</div>
				</div>
			</div>
		</div>
			
		<div class="container main-page">
			<a href="#" class="btn btn-default" data-toggle="modal" data-target="#myModal">Open Modal</a>
			<ul>
				<?php for( $i = 1; $i <= 100; $i++ ): ?>
					<li><a href="#"><?php echo 'Item ' . $i; ?></a></li>
				<?php endfor; ?>
			</ul>
		</div>
	</div>
</body>
</html>

CKEditor – Detect when typing stopped

I was trying to figure out a way to detect when the user typing in the editor has paused / stopped. The documentation of CKEditor and Stackoverflow do not have my answer, fortunately I was able to come up with a simple solution!

I first started with the basics – I played with a simple <textarea />  without CKEditor and the code bellow worked for me:

Type in the text box. it will time out and run the function when you stop for the time alotted.
<textarea id="editor"></textarea>
var timer = null;
$('#editor').keydown(function(){
       clearTimeout(timer); 
       timer = setTimeout(doStuff, 1000)
});

function doStuff() {
    alert('do stuff');
}

And next –  I applied the code above to a CKEditor instance.

CKEditor doesn’t have keyup  or keydown  events, but what they have is key

So I did the following using closures and it worked for me!

var timer = null;
CKEDITOR.instances['#editor'].on('key', function(){ 
	clearTimeout(timer); 
	timer = setTimeout(doStuff.bind(textarea, $('#editor')), 1000)
});

function doStuff(textarea) {
    alert(textarea.val());
}

PHP File Upload for CKEditor

The code bellow is not mine but I find it to be working properly, which is why I am putting it here on my website for future reference. There is a full blown file browser plugin for CKEditor called CKFinder – if you have no knowledge with PHP and how to it works then I suggest that you just install CKFinder. Note that in the code bellow I am using a CDN reference and it will only work with CKEditor version 4.8.0 and bellow.

index.php

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="robots" content="noindex, nofollow">
	<title>File Manager Integration</title>
	<script src="http://cdn.ckeditor.com/4.8.0/standard-all/ckeditor.js"></script>
</head>
<body>
	<textarea cols="10" id="editor1" name="editor1" rows="10"></textarea>
	<script>
	CKEDITOR.replace( 'editor1', {
		height: 300,
		filebrowserUploadUrl: "http://localhost/development/ckeditor/test2/upload.php",
	} );
	</script>
</body>

upload.php

<?php
// Upload script for CKEditor.
// Use at your own risk, no warranty provided. Be careful about who is able to access this file
// The upload folder shouldn't be able to upload any kind of script, just in case.
// If you're not sure, hire a professional that takes care of adjusting the server configuration as well as this script for you.
// (I am not such professional)

// Configuration Options: Change these to alter the way files being written works
$overwriteFiles = false;

//THESE SETTINGS ONLY MATTER IF $overwriteFiles is FALSE

    //Seperator between the name of the file and the generated ending.
    $keepFilesSeperator = "-"; 

    //Use "number" or "random". "number" adds a number, "random" adds a randomly generated string.
    $keepFilesAddonType = "random"; 

    //Only usable when $keepFilesAddonType is "number", this specifies where the number starts iterating from.
    $keepFilesNumberStart = 1; 

    //Only usable when $keepFilesAddonType is "random", this specifies the length of the string.
    $keepFilesRandomLength = 4; 

//END FILE OVERWRITE FALSE SETTINGS

// Step 1: change the true for whatever condition you use in your environment to verify that the user
// is logged in and is allowed to use the script
// if (true) {
    // echo("You're not allowed to upload files");
    // die(0);
// }

// Step 2: Put here the full absolute path of the folder where you want to save the files:
// You must set the proper permissions on that folder (I think that it's 644, but don't trust me on this one)
// ALWAYS put the final slash (/)
$basePath = "C:/xampp/htdocs/development/ckeditor/test2/uploads/";

// Step 3: Put here the Url that should be used for the upload folder (it the URL to access the folder that you have set in $basePath
// you can use a relative url "/images/", or a path including the host "http://example.com/images/"
// ALWAYS put the final slash (/)
$baseUrl = "http://localhost/development/ckeditor/test2/uploads/";

// Done. Now test it!



// No need to modify anything below this line
//----------------------------------------------------

// ------------------------
// Input parameters: optional means that you can ignore it, and required means that you
// must use it to provide the data back to CKEditor.
// ------------------------

// Optional: instance name (might be used to adjust the server folders for example)
$CKEditor = $_GET['CKEditor'] ;

// Required: Function number as indicated by CKEditor.
$funcNum = $_GET['CKEditorFuncNum'] ;

// Optional: To provide localized messages
$langCode = $_GET['langCode'] ;

// ------------------------
// Data processing
// ------------------------

// The returned url of the uploaded file
$url = '' ;

// Optional message to show to the user (file renamed, invalid file, not authenticated...)
$message = '';

// in CKEditor the file is sent as 'upload'
if (isset($_FILES['upload'])) {
    // Be careful about all the data that it's sent!!!
    // Check that the user is authenticated, that the file isn't too big,
    // that it matches the kind of allowed resources...
    $name = $_FILES['upload']['name'];

    //If overwriteFiles is true, files will be overwritten automatically.
    if(!$overwriteFiles) 
    {
        $ext = ".".pathinfo($name, PATHINFO_EXTENSION);
        // Check if file exists, if it does loop through numbers until it doesn't.
        // reassign name at the end, if it does exist.
        if(file_exists($basePath.$name)) 
        {
            if($keepFilesAddonType == "number") {
                $operator = $keepFilesNumberStart;
            } else if($keepFilesAddonType == "random") {
                $operator = bin2hex(openssl_random_pseudo_bytes($keepFilesRandomLength/2));
            }
            //loop until file does not exist, every loop changes the operator to a different value.
            while(file_exists($basePath.$name.$keepFilesSeperator.$operator)) 
            {
                if($keepFilesAddonType == "number") {
                    $operator++;
                } else if($keepFilesAddonType == "random") {
                    $operator = bin2hex(openssl_random_pseudo_bytes($keepFilesRandomLength/2));
                }
            }
            $name = rtrim($name, $ext).$keepFilesSeperator.$operator.$ext;
        }
    }
    move_uploaded_file($_FILES["upload"]["tmp_name"], $basePath . $name);

    // Build the url that should be used for this file   
    $url = $baseUrl . $name ;

    // Usually you don't need any message when everything is OK.
//    $message = 'new file uploaded';   
}
else
{
    $message = 'No file has been sent';
}
// ------------------------
// Write output
// ------------------------
// We are in an iframe, so we must talk to the object in window.parent
echo "<script type='text/javascript'> window.parent.CKEDITOR.tools.callFunction($funcNum, '$url', '$message')</script>";

?>

 

Get a Perfect Score of 100 on Google PageSpeed Insights

Recently, I was exploring on how to get a perfect score of 100 on Google PageSpeed Insights. I searched for days and did several trial and errors, finally I was able to get my static page to score 100. Bellow are some tips on how I was able to do it.

Checkout Demo site   Run Demo site on PageSpeed Insights

  • Use CDN – This allow users from a around the world to download static content from a closer source instead of spanning the Atlantic ocean to retrieve data.
  • Compress your JS, CSS, Images and HTML. The more compressed you get your scripts / images, the faster the download will be.
  • Get a faster hosting. Google also rank sites base on the amount of time spent to deliver your content to readers. According to Google, users tend to leave sites that take more than 3 seconds to load. So it’s important to to pick the right hosting for your website. Tip: new hosting sites are usually better because their servers are less crowded.
  • Leverage browser caching. I always use the following config to enable caching for specific types of file.
    ExpiresActive On
    ExpiresByType image/jpg "access 1 year"
    ExpiresByType image/jpeg "access 1 year"
    ExpiresByType image/gif "access 1 year"
    ExpiresByType image/png "access 1 year"
    ExpiresByType text/css "access 1 month"
    ExpiresByType text/html "access 1 month"
    ExpiresByType application/pdf "access 1 month"
    ExpiresByType text/x-javascript "access 1 month"
    ExpiresByType application/x-shockwave-flash "access 1 month"
    ExpiresByType image/x-icon "access 1 year"
    ExpiresDefault "access 1 month"
    
  • Enable Gzip Compression – Gzip Compression allows your web server to provide smaller file sizes which load faster for your website users. The code below should be added to your .htaccess file.
    mod_gzip_on Yes
    mod_gzip_dechunk Yes
    mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
    mod_gzip_item_include handler ^cgi-script$
    mod_gzip_item_include mime ^text/.*
    mod_gzip_item_include mime ^application/x-javascript.*
    mod_gzip_item_exclude mime ^image/.*
    mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
    

    The code above will work on Apache. If it’s not working there is another way that may work for you. Remove the above code from your .htaccess file and try this one instead:

    AddOutputFilterByType DEFLATE text/plain
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/xml
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE application/xhtml+xml
    AddOutputFilterByType DEFLATE application/rss+xml
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/x-javascript
    

    In my experience, enabling Gzip Compression gives you most of the score difference on Google PageSpeed Insights.

  • Get rid of render blocking scripts.
    • If you put your scripts within the head tag of your HTML –  the page will have to wait for the css and js to finish loading before it starts rendering your content. The best approach I found is to append css and js scripts dynamically to the header tag and in the end of body tag, that is – after DOM has completed loading.  You can get the script here: https://gist.github.com/carlo-fontanos/abc69dfea9d1e853c0e49fe509dbaa4b
    • Note that the loadScript()  and loadStyle()  loads the files asynchronously so if you have scripts that are depending on another script, ex. jQuery, then you should nest them like this:.
      loadScript("js/script1.js", function(){
      	loadScript("js/script2.js", function(){
      		loadScript("js/script3.js", function(){
      			loadScript("js/script4.js", function(){
      				loadScript("js/script5.js", function(){});
      			});
      		});
      	});
      });
      
    • Observation: when you refresh your page you will notice that it loads an unstyled HTML for a few seconds then renders back properly after your critical stylesheets have been fully loaded – uhh.. that’s ugly. Let’s fix that!  Within the head tag of your HTML (right after your meta tags), add the following styles:
      <style id="init-style">
      *{color:#fff; border:0; background:none;} 
      img,button{display:none;}
      </style>
    • The CSS code above TEMPORARILY sets all the elements to appear white and all images removed. Basically we are trying to make the screen appear like it’s still trying to load the content when actually the content is already there, but just not visible yet.
    • If you look at the <styles>  tag, it has an ID “init-style” – this will help us easily identify and remove it when all our critical css files have been fully loaded. So for example, we need to load a critical css file such as bootstrap: 1st is we load bootstrap dynamically, then 2nd we remove the initial styles from the header tag right after. Like this:
      loadStyle("//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css", function(){
      	document.getElementById("init-style").outerHTML=''; /* Remove initial styles from your page */
      });			
      
  • Combine CSS Files. Fewer HTTP requests for CSS content is better rather than loading a bunch individual CSS files. You can use this PHP script to combine multiple CSS files into a single file then compress it on-the-fly: https://gist.github.com/carlo-fontanos/f2ab6afaa71f29595b3f0d95f40ad2b0

I see a lot of posts complaining about scripts like Google Analytics being a render blocker. Well, if Google is cheating on you, you can cheat Google back:

This is the user-agent for pageSpeed:

“Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.8 (KHTML, like Gecko; Google Page Speed Insights) Chrome/19.0.1084.36 Safari/536.8”

You can insert a conditional to avoid serving the analytics script to PageSpeed:

<?php if (!isset($_SERVER['HTTP_USER_AGENT']) || stripos($_SERVER['HTTP_USER_AGENT'], 'Speed Insights') === false): ?>
	/* your analytics code here or any HTML element you want ignored by PageSpeed Insights */ 
<?php endif; ?>

or via Javascript:

if(navigator.userAgent.indexOf("Speed Insights") == -1) { 
	/* your analytics code here or any HTML element you want ignored by PageSpeed Insights */
}

If your only concern is getting a 100/100 score this will do it.

Flat Surface Shader Tutorial

flat-surface-shader-image-sample

Demo

The Flat Surface Shader is a simple plugin developed by Matthew Wagerfield which is based on the Lambertian Reflectance model, where the color of a triangle is calculated by a variety of different light sources within a particular scene. It is a great alternative to creating 3d effects with WebGL. Basically, this cool toy takes geometry, light and triangles and turns it into something fun, exciting and beautiful that everyone can use. With this Flat Surface Shader, triangles are rendered into three different contexts (WebGL, Canvas 2D and SVG). You can customize the settings yourself and even export your finished piece of art as a SVG or a PNG graphic that you can use later. What makes this project even better is that it is open source. You can visit their website, have a look at the code yourself and you can even adapt it or make it better if you want to. Reference

On the Demo, I rendered FSS using a Canvas.

The Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
MESH: {
    ambient: '#555555', // Default
    diffuse: '#FFFFFF', // Default
    width: 1.2, // Triangle Width
    height: 1.2, // Triangle Height
    depth: 10, // Transparency of the triangles
    segments: 16, // Number of triangles to display in 1 row
    slices: 8, // Number of triangles to display in 1 column
    xRange: 0.8, // Wideness of the triangles in X Position
    yRange: 0.1, // Wideness of the triangles in Y Position
    zRange: 1.0, // Wideness of the triangles in Z Position
    speed: 0.002 // Speed of the moving traingles
},

LIGHT: {
    autopilot: true, // Set this to true if you want the light to follow your mouse cursor
    ambient: '#880066',
    diffuse: '#ff8800',
    count: 2, // Contrast
    zOffset: 100,
   
    xyScalar: 1,
    speed: 0.001,
    gravity: 1200,
    dampening: 0.15,
    minLimit: 8,
    maxLimit: null,
    minDistance: 20,
    maxDistance: 400,
    draw: false // Set to true if you want to just draw a background image (static).
}

You Configure it live @ http://matthew.wagerfield.com/flat-surface-shader/ refer to the screenshot bellow

flat-surface-editor

Another cool example that I saw from CodePen is an Earth Figure with FSS used on the water: codepen.io/Yuschick/pen/tbiHp

AJAX using XMLHttpRequest Object

Bellow is a sample code that asynchronously sends a post request to a file named “test.php”. All of these happens behind the scenes using XMLHttpRequest Object.

So why use AJAX on the client-side instead of PHP? – AJAX allows web pages to be updated asynchronously by exchanging small amounts of data with the server behind the scenes. This means that it is possible to update parts of a web page, without reloading the whole page. Classic web pages, (which do not use AJAX) reloads the entire page for the content to change, which, in my opinion, is slower. So that is why I prefer AJAX for client-side processing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html>
<head>
    <script>
    function cvf_load_php_response() {
        var xmlhttp;
        if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp = new XMLHttpRequest();
        } else { // code for IE6, IE5
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        // Stores a function (or the name of a function) to be called automatically each time the readyState property changes
        xmlhttp.onreadystatechange=function() {
           
            // 0: request not initialized
            // 1: server connection established
            // 2: request received
            // 3: processing request
            // 4: request finished and response is ready
           
            // 200: "OK"
            // 404: Page not found
           
            // When readyState is 4 and status is 200, the response is ready:
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                document.getElementById("cvf_div").innerHTML = xmlhttp.responseText;
            }
        }
        xmlhttp.open("POST","test.php",true);
        // To POST data like an HTML form, add an HTTP header with setRequestHeader(). Specify the data you want to send in the send() method:
        xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        xmlhttp.send("fname=Carlo&lname=Fontanos");
    }
   
   
    // Load the function or if you want trigger the function on button click then use onclick="cvf_load_php_response()"
    cvf_load_php_response();
    </script>
</head>
<body>
    <div id="cvf_div"></div>
</body>
</html>

What we did above is we instantiated the XMLHttpRequest (for IE7+, Firefox, Chrome, Opera, Safari) or ActiveXObject(“Microsoft.XMLHTTP”) – (for IE6, IE5) and set it into a variable xmlhttp. We then set a function onreadystatechange that will be called automatically each time the readyState property changes. If the readyState returns “4” with the status of “200”, we set the response of xmlhttp.responseText to the element where we want the response of “test.php” to be outputted.

Create a new php file “test.php” on the same folder then place this block of code:

1
2
3
4
5
6
7
<?php
// Check if post is set and not empty
if(isset($_POST['fname']) && !empty($_POST['fname'])) {
        // Generate the response
    echo 'I received this first name: ' . $_POST['fname'];
}
?>

if you want to trigger a $_GET to read contents of a document like a text file, then you don’t need to give the .send() method any parameters. There is also no need to set the setRequestHeader() for HTTP header.

1
2
xmlhttp.open("GET","test.txt",true);
xmlhttp.send();

Reference: www.w3schools.com

Select Box with Search Filter

Turn a simple Select box:

ugly-select

Into this:

nice-select

Supported in:

  • IE 8+
  • Chrome 8+
  • Firefox 10+
  • Safari 3+
  • Opera 10.6+

Download the latest plugin here: Download

The idea
Select2 hides the old select box(inlined “display: none;”) and replicates it with a new custom select box structure. It ads a “s2id_” before the name of your select box ID. All options within the old select box are inherited by the new select box.

Sample usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<head>
    <link href="select2.css" rel="stylesheet"/>
    <script src="select2.js"></script>
    <script>
        $(document).ready(function() { $("#education").select2(); });
    </script>
</head>
<body>
    <select id="education">
        <option value="AD">Associates Degree</option>
        ...
        <option value="BD">Bachelors Degree</option>
    </select>
</body>

Commonly used parameters:

placeholder[string] Placeholder for the select element
allowClear[boolean] true or false. Default is false
minimumInputLength[int] Filters 2 string values

Usage:

1
2
3
4
5
$("#education").select2({
    placeholder: "Select an option",
    allowClear: true,
    minimumInputLength: 5
});

Reference Site: http://ivaynberg.github.io/select2/select2-latest.html

Reading URL variables in Javascript

For example you want to get the value of ‘firstname’ on this URL: https://carlofontanos.com/?firstname=carlo&lastname=fontanos

In PHP, we can simply access the value by using the $_GET[], since Javascript does not have any built-in function like this, we need to create our own.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript">
jQuery(document).ready(function($){
   
    function cvf_get_url_value(variable) {
           var query = window.location.search.substring(1);
           var vars = query.split("&");
           for (var i=0;i<vars.length;i++) {
                   var pair = vars[i].split("=");
                   if(pair[0] == variable){return pair[1];}
           }
           return(false);
    }
});
</script>

Get the value of ‘firstname’:

1
cvf_get_url_value('firstname');

Setting and Getting Cookies in Javascript

Basic usage:

Set a cookie

1
document.cookie="username=John Doe";

Get a cookie

1
var x = document.cookie;

Function Based:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<script type="text/javascript">
/**
* Set Cookie
*
* @param string cname the name of the cookie
* @param string cvalue value to be set
* @param string cdays number of days before it expires
*
*/

function setCookie(cname, cvalue, cdays) {
    var d = new Date();
    d.setTime(d.getTime() + (cdays*24*60*60*1000));
    var expires = "expires="+d.toUTCString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
}

/**
* Set Cookie by path
*
* @param string cname the name of the cookie
* @param string cvalue value to be set
* @param string cdays number of days before it expires
* @param string cpath directory path, ex. "/" to apply to the entire domain
*
*/

function setCookieByPath(cname, cvalue, cdays, cpath) {
    var d = new Date();
    d.setTime(d.getTime() + (cdays*24*60*60*1000));
    var expires = "expires="+d.toUTCString();
    document.cookie = cname + "=" + cvalue + "; " + expires + "; path=" + cpath;
}
/**
* Get Cookie
*
* @param string cname the name of the cookie to retrieve
*
*/

function getCookie(cname) {
    var value = "; " + document.cookie;
    var parts = value.split("; " + cname + "=");
    if (parts.length == 2) return parts.pop().split(";").shift();
}
</script>