JavaScript (.js) file interpreted as MIME type text/html

I had situation with a JavaScript (.js) file seeming to load OK but in reality it was a misdirect.

Using Chrome, I inspected the Network tab in Developer Tools and couldn’t see an error with the file load. But on closer inspection, I noticed the Status was a “302” and the Type was “text/html” as follows (I was expecting a “200” Status and “script” mime Type).

It was luck that I found the answer:

I was loading the JavaScript file using the path “/inc/assets/js/vueks/vue-2.6.10.js”.

But I had a spelling mistake in the path. It should have been: “/inc/assets/js/vuejs/vue-2.6.10.js”.

I changed “vueks” to “vuejs” and the script loaded successfully.

 

The strange thing is the incorrect path didn’t give me a “404” (Not Found) response. Instead I received a “302 Found”, where clearly the file was not found.

My guess is I was loading it in a WordPress site, and while the file was not found I do have code internally to redirect to a “/not-found” page that may have returned some form of response (even though it contained no content). That or some headers were returned which confused Chrome.

(I haven’t tried to replicate this other browsers yet.)

PHP $_POST not populating from jQuery (JavaScript) $.ajax() POST

Problem

I was trying to submit data from a web page to a back-end API in PHP using jQuery’s AJAX (ie. $.ajax()).

But the $_POST variable in PHP didn’t contain the values I was posting.

 

Solution

The problem is I was encoding the POST body data the wrong way in JavaScript.

I was using JSON.stringify(data), where “data” is a JavaScript object or array.

What I needed to do what manually create a string of key1=value1&key2=value2&keyN=valueN pairs.

Example Code from my submit function:

        //sendData = JSON.stringify(data);  //<-- This doesn't work ...

        // ... but this does.
        sendData = "";
        for (var key in data) 
        {
            if (sendData != "") sendData = sendData + "&";
            sendData = sendData + key + "=" + data[key];
        }

	var ajaxRequest = {
		url: url,
		type: "POST",
		data: sendData,
 		dataType: dataType,

		success: function(data, textStatus, jqXHR)  { successCallback(data, textStatus, jqXHR ); },
		error: function(jqXHR, textStatus, errorThrown) { errorCallback(data, textStatus, errorThrown); }
	};

	$.ajax(ajaxRequest);

 

What I searched for (which didn’t help much):

Search “$_POST empty from jquery”: https://duckduckgo.com/?q=%24_POST+empty+from+jquery&amp;atb=v147-1&amp;ia=web

https://stackoverflow.com/questions/1650516/jquery-ajax-method-post-but-post-empty

https://stackoverflow.com/questions/16360008/jquery-post-post-empty

https://stackoverflow.com/questions/54039285/ajax-form-posting-to-php-script-but-post-is-empty-in-php-script

https://stackoverflow.com/questions/1650516/jquery-ajax-method-post-but-post-empty/49948834#49948834

PHP: “Fatal error: Can’t use function return value in write context in …”

I just received the following PHP error.

Fatal error: Can’t use function return value in write context in /path/to/file.php on line 595

And I had no fucking idea what the problem was. I had a “sense” of it, but I didn’t understand the “in write context” part (that’s what happens when you get geeks to write error messages).

The line causing the error was this:

if (strtolower($append_value) = strtolower($base_value))

It’s one of those classic programmer errors that is in your face but is easily lost in the code.

Here’s the fixed line

if (strtolower($append_value) == strtolower($base_value))

Yeap – I missed an equals sign to make it an equality expression rather than an assignment (i.e. “=” changed to “==”).

 

Interestingly, this error was thrown without actually being executed. It must have been while the script was being parsed. I’m using PHP 7.3 in WordPress in the Codeanywhere online IDE.

PHP: Recursively zip a folder/directory structure

Status: Draft (31 May 2019)

This is a work in progress. I want to record my working as I go.


What I Want

I need to zip a folder structure – ultimately, theme and plug-in folder in WordPress – using PHP.

I’m currently developing and testing on a Windows 10 PC. Ultimately it will need to work in Linux (Ubuntu), but the first step is to it working in Windows.

I started with solutions from https://gist.github.com/MarvinMenzerath/4185113/72db1670454bd707b9d761a9d5e83c54da2052ac and https://stackoverflow.com/questions/1334613/how-to-recursively-zip-a-directory-in-php but the zip file was being created, however, not files were added.

I realised the problem was the relative path for folders/files within the zip. My solution below is a modification of the referenced solutions, where I strip the source path from the fill file path of each folder and file to give the relative path to the source.

So far this works in Windows, next step is to test in Linux. I’m actively working the problem so stay tuned.

 

I created a file “D:\Temp\php\test_zip.php” with the code below.
I execute with the following command (I have XAMPP installed with includes PHP): “D:\Temp\php>D:\xampp\php\php.exe test_zip.php”.

The Code

(Aide: I used https://www.freeformatter.com/html-escape.html to convert raw PHP code to something I can past in between <pre> tags in the Classic WorPress content editor).

<?php
// Make sure the script can handle large folders/files
ini_set('max_execution_time', 600);
ini_set('memory_limit','1024M');

// Start the backup!
Zip('D:\\Temp\\php\\Source\\', './compressed.zip');
Zip('D:\\Temp\\hildas_prod_logs\\', 'D:\\Temp\\php\\backup-test.zip');
testOne('D:\\Temp\\php\\Source\\debug.log', 'D:\\Temp\\php\\compressed_testOne.zip');

echo "Finished" . "\n";


// A quick, reduced test.
// This works in Windows.
function testOne($source, $zipTo)
{
	$zip = new ZipArchive;
	if (file_exists($source) === false)
	{
		echo "Sourece does not exist" . "\n";
		return;
	}
	
	if ($zip->open($zipTo, ZIPARCHIVE::CREATE) === true) 
	{
		$zip->addFile($source, 'source\newname.txt');
		$zip->close();
		echo "OK" . "\n";
	} 
	else 
	{
		echo "FAILED" . "\n";
	}
}


// This works in Windows.
// Source and inspiration: https://gist.github.com/MarvinMenzerath/4185113/72db1670454bd707b9d761a9d5e83c54da2052ac - Marvin Menzerath. (http://menzerath.eu)
// Additional source and inspiration: https://stackoverflow.com/questions/1334613/how-to-recursively-zip-a-directory-in-php
function Zip($source, $destination)
{
    if (!extension_loaded('zip')) 
	{
		echo "Zip extension not loaded\n";
        return false;
    }
	
	if (!file_exists($source)) 
	{
		echo "Source not found:" . $source . "\n";
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) 
	{
		echo "Zip not created or opened\n";
        return false;
    }
	
	$raw_source = $source;

    $source = str_replace('\\', '/', realpath($source));
	
	echo "Raw source: " . $raw_source . "\n";
	echo "Clean source: " . $source . "\n";

    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
		
		$sourceWithSeparator = $source . DIRECTORY_SEPARATOR;
        foreach ($files as $file)
        {
            $file = str_replace('\\', '/', $file);

            // Ignore "." and ".." folders
            if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                continue;

            $file = realpath($file);

            if (is_dir($file) === true)
            {
				$dir_path = str_replace($sourceWithSeparator, '', $file . DIRECTORY_SEPARATOR);
				
				echo "Directory: " . $file . " (Path: " . $dir_path . ")\n";
                //$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
				
				$zip->addEmptyDir($dir_path);
            }
            else if (is_file($file) === true)
            {
				$zip_relative_path = str_replace($sourceWithSeparator, '', $file);
				
				$zip_relative_path = remove_from_start($zip_relative_path, $raw_source);
				
				echo "File: " . $file . " (Path: " . $zip_relative_path . ")\n";
                //$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
				//$zip->addFile($file, str_replace($source . '/', '', $file));
				$zip->addFile($file, $zip_relative_path);
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}



function remove_from_start($full_string, $prefix)
{
	if (substr($full_string, 0, strlen($prefix)) == $prefix) 
	{
		$full_string = substr($full_string, strlen($prefix));
	} 
	
	return $full_string;
}

?>

 

WordPress Shortcode: “Show If” for conditional content display

Here’s the code for a simple WordPress shortcode to conditionally display content within pages based on supplied usernames or roles.

 

Example Usage

Display to a specified user:


Display to a specified role:


Display to both a user and a role:


Display to multiple roles (comma separated list. “username” works the same):


 

Code

It’s in a GitHub gist at https://gist.github.com/vylesk/64ca3e6d3da77fb853ca7af181165e6b and below:

<?php
/**
* Plugin Name: "Show If" Shortcode
* Plugin URI: https://jsnelders.com/
* Description: Shortcode to conditionally display content if the user is in a specified role or has a specified username.
* Author: Jason Snelders
* Author URI: http://jsnelders.com
* Version: 190529.1
**/

namespace JasonSnelders\Shortcodes;

/*
*	Show the content only if the current user is one of the specified username(s) or in one ofthe role(s).
*
*	Attributes:
*		role: comma separated list of allowed roles to view the content.
*		username: comma separated list of allowed usernames to view the content.
*
*	Attrubtes combine as an OR expressess - content is displayed if they user is in the "role" list OR the "username" list.
*/
add_shortcode('show_if', 'JasonSnelders\Shortcodes\show_if_handler');

function show_if_handler($atts = [], $content = null)
{
	
	// Get supplied shortcode attributes.
	$show_if_role = "";
	$show_if_username = "";
	if(isset($atts["role"])) $show_if_role = $atts["role"];
	if(isset($atts["username"])) $show_if_username = $atts["username"];
	$show_if_role = explode(',', $show_if_role);
	$show_if_username = explode(',', $show_if_username);
	
	
	// Get the current user.
	$wp_user = \wp_get_current_user();
	$wp_user_roles = $wp_user->roles;
	$wp_user_username = $wp_user->user_login;
	
	// Check if current user can view content/
	$show_to_user = false;
	
	foreach ($wp_user_roles as $role_slug)
	{
		if ($role_slug != "" && in_array_case_insensitive($atts, $role_slug))
		{
			$show_to_user = true;
			break;
		}
	}
	
	if ($show_to_user == false)
	{
		foreach ($show_if_username as $show_username)
		{
			if ($show_username != "" && strtolower($show_username) == strtolower($wp_user_username))
			{
				$show_to_user = true;
				break;
			}
		}
	}
	
    
	ob_start();

	if ($show_to_user == true)
	{
		// Show the content
		$content = do_shortcode($content);
		echo $content;
	}
	else
	{
		// Hide the content
		echo "";
	}

	return ob_get_clean();
}


/*
*	Case-insensitive check if a value is in the array.
*	(Source: https://gist.github.com/sepehr/6351397)
*/
function in_array_case_insensitive($array, $check_value)
{
	return in_array(strtolower($check_value), array_map('strtolower', $array));
}
?>

Visually hidden field with ‘required’ attribute causes error: “An invalid form control with name=’xxxxx’ is not focusable.”

Situation

Receive a JavaScript console error of An invalid form control with name='xxxxx' is not focusable when trying to submit the form.

I asked one of my developers to comment out a field in a web page as part of a requirements change (I prefer to comment out initially before deleting so we are sure everything works fine with the change).

The developer added a style="display: none" to a surrounding div to hide a <select> field.

However, this in turn caused a JavaScript console error of An invalid form control with name='xxxxx' is not focusable when trying to submit the form.

 

Solution

Remove the “required” attribute from the form element.

In helping my developer I did a Google search for “an invalid form control with name=” is not focusable” and one of the first results lead to https://stackoverflow.com/questions/22148080/an-invalid-form-control-with-name-is-not-focusable.

On that page I immediately saw “Adding a novalidate attribute to the form will help” and that made me to immediately remember there is a “required” attribute still on the field that was hidden.

Experience and instinct kicked in.

We removed the “required” attribute and the form submission worked.

My guess is when the form was submitted the native browser-based validation did not work correctly because the specified field is “visually” hidden so the native validation message could not be shown, and that triggered the error in the console.

platform_browser_dynamic_1.platformBrowserDynamic is not a function

I received the following error today in a project I’m working on (the relevant part is in bold):

MainModule.bundle.js:27136 Uncaught TypeError: platform_browser_dynamic_1.platformBrowserDynamic is not a function
   at Object.__decorate (MainModule.bundle.js:27136)
   at __webpack_require__ (Polyfills.bundle.js:55)
   at webpackJsonpCallback (Polyfills.bundle.js:26)
   at MainModule.bundle.js:1

You can see my response to a similar problem at https://github.com/angular/angular/issues/10732#issuecomment-417173741.

And my response here as well, for completeness:

I just had the same issue in a project I’m working on. We’re using Angular 5.0.2 and Webpack on Windows in an IIS/ASP.NET project, with our primary browser as Chrome.
My issue randomly appeared after rebooting my PC and loading up my dev environment again (no code or configuration changes).
I managed to resolve the issue after a couple of attempts at starting the project (in Visual Studio) by also browsing to the the app URL Microsoft Edge. It loaded find. I then did a hard refresh (Ctrl+F5) in Chrome and it came good too.
Note: we manually start and leave running Webpack in separate command window, but start the actual application from withing Visual Studio.
My guess is Webpack crapped out behind the scenes and needed some time and a hard refresh in the browser to get the correctly compiled code to the browser again.

JavaScript helper: getMaxLengthString [TypeScript]

Here’s a quick JavaScript helper snippet to return a trimmed string based on the calculated maximum length of a string to fit the width of a parent container element.

Written in TypeScript.

/**
 * Takes a full string and returns a trimmed version to fit within a parent space.
 * @param fullLengthString  The full length string to trim to a parent space.
 * @param font              String representing the font styling of the text (required to calculate text length).
 * @param maxLengthPixels   Max pixel width of the parent space to fit the text into.
 * @param removeExtraChars  The number of extr characters to remove from the trimmed space, if required. (Default: 0)
 */
private getMaxLengthString(fullLengthString: string, font: string, maxLengthPixels: number, removeExtraChars: number = 0): string {
	let maxLengthString: string = "";

	let canvas = document.createElement("canvas");
	let context = canvas.getContext("2d");
	context.font = font;

	for (let i = 0; i < fullLengthString.length; i++) { // Build the string maxLengthString = maxLengthString + fullLengthString.charAt(i); // Test the length of the string let metrics = context.measureText(maxLengthString); if (metrics.width > maxLengthPixels) {
			// Trim the string to a max length.

			// The number of characters to remove.
			let removeChars = 1;   // At this point the length is too long, so remove 1 character to get it under.
			if (removeExtraChars > 0) {
				removeChars = removeChars + removeExtraChars;
			}
			removeChars = removeChars * -1; // To make sure they're sliced from the end.

			// Remove characters from the string.
			maxLengthString = maxLengthString.slice(0, removeChars);

			return maxLengthString;
		}
	}

	// Full string is OK.
	return maxLengthString;
}

JavaScript helper: getTextWidth [TypeScript]

Here’s a quick JavaScript helper snippet to calculate width of a string, based on a supplied font styling.

Written in TypeScript.

Credit goes to https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393

/**
 * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
 *
 * @param {String} text The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
 *
 * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
 */
private getTextWidth(text: string, font: string): number {
	// re-use canvas object for better performance
	let canvas = document.createElement("canvas");
	let context = canvas.getContext("2d");
	context.font = font;
	let metrics = context.measureText(text);

	return metrics.width;
}

JavaScript helper: GetElementInnerWidth [TypeScript]

Here’s a quick JavaScript helper snippet to get the inner width (excluding padding) of an element in the DOM.

Written in TypeScript.

/**
 * Return the calculated inner width of an element.
 * Returns -1 if width is unknown.
 * @param elementID         ID of the element to calculate the width for. Set to "" to use elementClassName
 * @param elementClassName  CSS class name on the element to calcualte the width for. Ignored if elementID is provided.
 */
public static GetElementInnerWidth(elementID: string, elementClassName: string): number {
	let width: number = -1;
	let element: any = null;

	// Get the DOM element to check,
	if (elementID !== "") {
		// Lookup by element ID.
		let elementByID = document.getElementById(elementID);
		if (elementByID !== undefined) {
			element = elementByID;
		}
	} else {
		// Lookup by CSS class name. Use first found node as actual element.
		let elementByClass = document.querySelectorAll("." + elementClassName);
		if (elementByClass !== undefined && elementByClass && elementByClass[0]) {
			element = elementByClass[0];
		}
	}

	if (element !== null) {
		// The outer width of the element
		width = element.clientWidth;

		// Get the calculated padding so we can remove it.
		let paddingLeft = ComponentHelpers.TryParseInt(window.getComputedStyle(element, null).getPropertyValue("padding-left"), 0);
		let paddingRight = ComponentHelpers.TryParseInt(window.getComputedStyle(element, null).getPropertyValue("padding-right"), 0);

		// Innerwidth = outerwidth - padding
		width = width - (paddingLeft + paddingRight);
	}

	return width;
}