Skip to content

Instantly share code, notes, and snippets.

@earth3300
Created October 12, 2018 16:09
Show Gist options
  • Select an option

  • Save earth3300/8a90833d6f67f9083acd3bb92a74c46d to your computer and use it in GitHub Desktop.

Select an option

Save earth3300/8a90833d6f67f9083acd3bb92a74c46d to your computer and use it in GitHub Desktop.
Order Colors By HSL
<?php
/**
* EC01 JSON Index.
*
* Allows a JSON data text file to be viewed in a directory through a
* single index file.
*
* @package EC01 JSON Index
* @since 1.0.0
* @author Clarence Bos <[email protected]>
* @copyright Copyright (c) 2018, Clarence Bos
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3.0+
* @link http://wp.cbos.ca/plugins/ec01-json-index
*
* @wordpress-plugin
* Plugin Name: EC01 JSON Index
* Plugin URI: http://wp.cbos.ca/plugins/ec01-json-index/
* Description: Allows a JSON data text file to be viewed in a directory through a single index file.
* Version: 1.0.0
* Author: Clarence Bos
* Author URI: http://ec01.earth3300.info/
* Text Domain: ec01-json-index
* License: GPL-3.0+
* License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
*/
/**
* Allows a json file to be viewed in a directory.
*
* See the bottom of this file for a more complete description
* and the switch for determining the context in which this file
* is found.
*/
class JSONIndex
{
/** @var array Default options. */
protected $opts = [
'max' => 300,
'file' => 'colors-w3c',
'type' => 'json',
'msg' => [ 'na' => '', ],
'allow_print' => true,
'allow_order' => true,
];
/**
* Gets the JSON file as HTML.
*
* @param array $args
*
* @return string
*/
public function get( $args = null )
{
/** If no arguments are set, assume current directory */
if ( $args = $this->setDefaultOptions( $args ) )
{
$max = $this->getMaxFiles( $args );
$arr = $this->getJSONFileDecode( $args );
$json = $arr['json'];
if ( $this->opts['allow_order'] )
{
$json = $this->getOrder( $json );
}
$str = '<article>' . PHP_EOL;
$str .= $this->getHTMLfromJSON( $json );
$str .= '</article>' . PHP_EOL;
if ( $this->opts['allow_print'] && isset( $_GET['print'] ) )
{
$bool = $this->putFile( $arr['file'], $json );
}
if ( isset( $args['doctype'] ) && $args['doctype'] )
{
$str = $this->getPageHtml( $str, $args );
}
return $str;
}
else
{
return "Error.";
}
}
/**
* Get the first JSON file, decode and return array.
*
* Looks for json only.
*
* $str .= $this->getHTMLfromJSON( $json );
*
* @param string $match
* @param array $args
*
* @return array JSON decoded file, file name.
*/
private function getJSONFileDecode( $args )
{
if ( $file = $this->getFileName( $args ) )
{
if ( file_exists( $file ) )
{
$arr['file'] = $file;
$string = file_get_contents( $file );
$arr['json'] = json_decode( $string );
}
else {
$arr = false;
}
}
else
{
return $arr = false;
}
return $arr;
}
/**
* Get the File Name
*
* @param $args
*
* @return string
*/
private function getFileName( $args )
{
if ( isset( $args['file'] ) )
{
if ( $path = $this->getBasePath( $args ) )
{
$file = $path . '/' . $args['file'] . $args['ext'];
}
else {
$file = false;
}
}
else {
$file = false;
}
return $file;
}
/**
* Get the source from the file, checking for a preceding slash.
*
* @param string $str
* @return string
*/
private function getSrcFromFile( $str )
{
$src = str_replace( $this->getSitePath(), '', $str );
/** May be server inconsistency, therefore remove and add again. */
$src = ltrim( $src, '/' );
return '/' . $src;
}
/**
* Get the SITE_PATH
*
* Get the SITE_PATH from the constant, from ABSPATH (if loading within WordPress
* as a plugin), else from the $_SERVER['DOCUMENT_ROOT']
*
* Both of these have been tested online to have a preceding forward slash.
* Therefore do not add one later.
*
* @return bool
*/
private function getSitePath()
{
if ( defined( 'SITE_PATH' ) )
{
return SITE_PATH;
}
/** Available if loading within WordPress as a plugin. */
elseif( defined( 'ABSPATH' ) )
{
return ABSPATH;
}
else
{
return $_SERVER['DOCUMENT_ROOT'];
}
}
/**
* Get the maximum number of images to process.
*
* @param array $args
* @return int
*/
private function getMaxFiles( $args )
{
if ( isset( $args['max'] ) )
{
$max = $args['max'];
}
else
{
$max = $this->opts['max'];
}
return $max;
}
/**
* Build the match string.
*
* This is iterated through for each type added to $types, above. A basic
* check for a reasonable string length (currently 10) is in place. Can
* develop this further, if needed.
*
* @param string $type 'jpg', 'png'
* @param array $args
*
* @return string|false
*/
private function getMatchPattern( $type, $args )
{
$path = $this->getBasePath( $args );
$prefix = "/*";
$match = $path . $prefix . $type;
/** Very basic check. Can improve, if needed. */
if ( strlen( $match ) > 10 )
{
return $match;
}
else {
return false;
}
}
/**
* Get the Base Path to the Media Directory.
*
* This does not need to include the `/media` directory.
*
* @param array $args
* @return string
*/
private function getBasePath( $args )
{
if ( isset( $args['self'] ) )
{
$path = __DIR__;
}
elseif ( defined( 'SITE_CDN_PATH' ) )
{
$path = SITE_CDN_PATH;
}
else {
$path = false;
}
return $path;
}
/**
* Get the Media Directory
*
* @param array $args
*
* @return string
*
* @example $args['dir'] = '/architecture/shelter/micro-cabin/'
*/
private function getMediaDir( $args )
{
if ( isset( $args['dir'] ) )
{
$media = $args['dir'];
}
else
{
$media = '/media';
}
return $media;
}
/**
* Wrap the string in page HTML `<!DOCTYPE html>`, etc.
*
* @param string $str
* @return string
*/
public function getPageHtml( $html, $args )
{
$str = '<!DOCTYPE html>' . PHP_EOL;
$str .= sprintf( '<html class="dynamic %s" lang="en-CA">', $args['type'], PHP_EOL );
$str .= '<head>' . PHP_EOL;
$str .= '<meta charset="UTF-8">' . PHP_EOL;
$str .= '<meta name="viewport" content="width=device-width, initial-scale=1"/>' . PHP_EOL;
$str .= '<title>JSON File</title>' . PHP_EOL;
$str .= '<meta name="robots" content="noindex,nofollow" />' . PHP_EOL;
$str .= '<link rel=stylesheet href="/0/theme/css/style.css">' . PHP_EOL;
$str .= '</head>' . PHP_EOL;
$str .= '<body>' . PHP_EOL;
$str .= '<main>' . PHP_EOL;
$str .= $html;
$str .= '</main>' . PHP_EOL;
$str .= '<footer>' . PHP_EOL;
$str .= '<div class="text-center"><small>';
$str .= 'Note: This page has been <a href="https://github.com/earth3300/ec01-json-index.git">automatically generated</a>. No header, footer, menus or sidebars are available.';
$str .= '</small></div>' . PHP_EOL;
$str .= '</footer>' . PHP_EOL;
$str .= '</html>' . PHP_EOL;
return $str;
}
/**
* Get the File Name from the String.
*
* Return the direct string values of each. Do no extra processing here.
*
* $regex = '/\/([a-z0-9\-]{3,150})\./'
*
* Part 1: Looks for letters, numbers and dashes from 3 to 150 characters.
* Part 2: Followed by a dash, then dimesions from 2 to 4 x 2 to 5 characters.
* Part 3: Ending. Followed by a dot \. (which will come before the extension).
*
* Not only does it divide the string given it into two parts, but it
* also does a basic quality check on the image name structure. If the image
* name does not meet the criteria given, it won't be captured.
*
* @param string $str
*
* @return array $arr['name']
*
* @example $arr['name'] = 'image-name'
*/
private function getImageNameDimArr( $str )
{
/**
* Since we won't have a valid image name with fewer than 13 characaters
* we won't bother processing anything with less than that length.
*/
if ( strlen( $str ) > 12 )
{
/** If this isn't matched, check for a name only */
$regex = '//\/([a-z0-9\-]{3,150})\./';
preg_match( $regex, $str, $match );
if ( empty( $match ) )
{
$regex = '/\/([a-z,0-9\-]{5,150})\./';
preg_match( $regex, $str, $match );
}
if ( ! empty( $match[1] ) )
{
$arr['name'] = $match[1];
}
else
{
$arr['name'] = null;
}
return $arr;
}
else {
return false;
}
}
/**
* Get the Name from the File Name
*
* @param array $args
*
* @return string
*/
private function getNamefromFileName( $str )
{
if ( strlen( $str ) > 2 )
{
$name = str_replace( '-', ' ', $str );
$name = strtoupper( $name );
}
else
{
$name = $this->opts['msg']['na'];
}
return $name;
}
/**
* Set the Default Options.
*
* If $args['self'] or $args['dir'] are not set, it assumes we are in the
* directory for which images are to be processed. Therefore $args['self']
* is set to true and $args['dir'] is set to null. We also have to set the
* $args['doctype'] to true to know whether or not to wrap the output in
* the correct doctype and the containing html and body elements.
*
* @param array $args
*
* @return array
*/
private function setDefaultOptions( $args )
{
/** If $args['dir'] is not set, set it to false. */
$args['dir'] = isset( $args['dir'] ) ? $args['dir'] : false;
/** if $args['dir'] == false, set $args['self'] to true. */
if ( ! $args['dir'] )
{
$args['self'] = true;
$args['doctype'] = true;
$args['file'] = $this->opts['file'];
$args['type'] = $this->opts['type'];
$args['ext'] = '.' . $this->opts['type'];
$args['class'] = $this->opts['type'];
return $args;
}
else
{
return $args;
}
}
/**
* JSON Color Data to HTML
*
* @param object $data JSON decoded file
*
* @return string
*
* @example echo jsonToTable([json-decoded-values]);
*
* @example echo jsonToTable(json_decode('{"name":"Bob","age":23,"skills":["php","javascript"]}'));
*/
private function getHTMLfromJSON($arr)
{
$max = $this->opts['max'];
$cnt = 0;
$str = '<div class="json" style="font-size: 80%;">' . PHP_EOL;
$str .= '<div class="line">' . PHP_EOL;
foreach ($arr as $key => $color)
{
$cnt++;
if( $cnt <= $max )
{
$str .= '<div class="unit size1of4" style="border-radius: 3px;">' . PHP_EOL;
$str .= '<div class="border" style="padding: 3px;">' . PHP_EOL;
$str .= '<div class="inner text-center" style="min-height: 46px; padding:6px; ' . PHP_EOL;
$str .= sprintf( 'background: %s;', $color->hexString );
$str .= sprintf( 'color: %s">', $this->getTextColor( $color->rgb, $color->hsl ) );
$str .= sprintf('%s<br />%s', $color->name, PHP_EOL);
$str .= sprintf('%s<br />%s', $color->hexString, PHP_EOL);
$str .= sprintf('%s<br />%s', $this->getRGBValue( $color->rgb ), PHP_EOL);
$str .= sprintf('%s<br />%s', $this->getHSLValue( $color->hsl ), PHP_EOL);
$str .= sprintf('%s%s', $this->getWavelength( $color ), PHP_EOL);
$str .= '</div>' . PHP_EOL;
$str .= '</div>' . PHP_EOL;
$str .= '</div>' . PHP_EOL;
}
else
{
break;
}
}
$str .= '</div>' . PHP_EOL;
$str .= '</div>' . PHP_EOL;
return $str;
}
/**
* Get the color of the text
*
* Gets the color for the text based on the color of the background.
* A darkerbackground needs a lighter text.
*
* (256 + 256 + 256) / 2 = 384.
* 384 - 256 = 128 / 2 = 64
* 384 - 64 = 320.
*
* @param array $rgb
* @param array $hsl
*
* @return string Hex value, with leading '#'
*/
private function getTextColor( $rgb, $hsl )
{
$rgb_sum = $rgb->r + $rgb->g + $rgb->b;
$hsl_sum = $hsl->h + $hsl->s + $hsl->l;
if ( $rgb_sum <= 320 ) {
$color = '#fff';
}
else {
$color = '#000';
}
return $color;
}
/**
* Get the RGB value as a String
*
* @pararm array $rgb
*
* @return string
*/
private function getRGBValue( $rgb )
{
$hsl = sprintf( '(%s, %s, %s)', $rgb->r, $rgb->g, $rgb->b );
return $hsl;
}
/**
* Get the HSL value as a Comma Separated String
*
* @pararm array $hsl
*
* @return string
*/
private function getHSLValue( $hsl )
{
$hsl = sprintf( '(%s, %s, %s)', number_format( $hsl->h, 0 ), $hsl->s, $hsl->l );
return $hsl;
}
/**
* Get the HSL value as a Concatenated String,
*
* Converts each integer to three digits, then concatenates them
* so that they can be sorted easily as a string. This may contain
* leading zeros and will have nine digits in total.
*
* @pararm array $hsl
*
* @return string
*/
private function getHSLString( $color )
{
$hsl = $color['hsl'];
$h = str_pad((string)$hsl['h'], 3, '0', STR_PAD_LEFT);
$s = str_pad((string)$hsl['s'], 3, '0', STR_PAD_LEFT);
$l = str_pad((string)$hsl['l'], 3, '0', STR_PAD_LEFT);
return $h . $s . $l;
}
/**
* Get the wavelength from the color.
*
* Color: ~390nm - ~730nm
*
* @param array $rgb
* @param array $hsl
*
* @return int
*/
private function getWavelength( $color )
{
if( ! empty( $color->rgb ) ) {
$rgb = $color->rgb;
$hsl = $color->hsl;
$range = 256;
$wave['min'] = 390;
$wave['max'] = 730;
$brightness = (0.21 * $rgb->r) + (0.72 * $rgb->g) + (0.07 * $rgb->b);
$wave['diff'] = $wave['max'] - $wave['min'];
$nm = $brightness * $brightness / $range * ( $wave['diff'] / $range ) + $wave['min'];
if ( $nm > 300 )
{
return number_format( $nm, 1 );
}
else
{
return false;
}
}
else {
return false;
}
}
/**
* Put file
*
* @param string $file
* @param string $str
*
* @return bool
*/
private function putFile( $file='', $str='' )
{
$json = json_encode( $str );
$file_ordered = str_replace( 'colors-w3c.json', 'ordered/colors-w3c.json', $file );
file_put_contents( $file_ordered, $json );
}
/**
* Order array
*
* Calculate the approximate wavelength from the RGB and/or HSL values, then
* sort based on that wavelength. Change the index values to the new order,
* as this is the order we will want to retain.
*
* bool array_multisort ( array &$array1 [, mixed $array1_sort_order = SORT_ASC
* [, mixed $array1_sort_flags = SORT_REGULAR [, mixed $... ]]] )
*
* array_multisort() can be used to sort several arrays at once, or a
* multi-dimensional array by one or more dimensions.
*
* Associative (string) keys will be maintained, but numeric keys will
* be re-indexed.
*
* Note: If two members compare as equal, their relative order in the
* sorted array is undefined.
*
* @param array $arr
*
* @return $arr
*/
private function getOrder( $json )
{
$max = $this->opts['max'];
$cnt = 0;
//convert to associative array
$arr = json_decode(json_encode( $json ), true);
foreach( $arr as $key => $color )
{
$cnt++;
if ( $cnt > $max )
{
break;
}
if ( $hslString = $this->getHSLString( $color ) )
{
$arr[$key]['hslString'] = $hslString;
}
else
{
$arr[$key]['hslString'] = '';
}
if( 1 )
{
$rgb = $color['rgb'];
// Remove the greys
if ( $rgb['r'] == $rgb['g'] && $rgb['g'] == $rgb['b'] )
{
unset( $arr[$key] );
}
}
}
if( 1 ) {
$sorted = $this->sortArrayByKey( $arr, 'hslString' );
}
if ( 0 ) {
echo "<pre>";
var_dump( $sorted );
echo "</pre>";
}
//Convert back to Standard Class array
$sorted = json_decode(json_encode( $sorted ));
return $sorted;
}
/**
* Sort Array By the Value of a Key
*
* The key 'nm' does not exist in all cases.
*
* @param $sort_arr
* @param $sort_key
*
* @return array
*/
private function sortArrayByKey( $sort_arr, $sort_key )
{
$items = Array();
foreach( $sort_arr as $key => $row )
{
if( isset( $row[$sort_key] ) )
{
$items[$key] = $row[$sort_key];
}
else
{
$items[$key] = $row[$key];
}
}
//array sizes inconsistent
array_multisort($items, SORT_FLAG_CASE | SORT_NATURAL, $sort_arr);
return $sort_arr;
}
}
/**
* Callback from the json-index shortcode.
*
* Performs a check, then instantiates the JSONIndex class
* and returns the results as HTML.
*
* @param array $args['dir']
* @return string HTML, wrapped in the article element.
*/
function json_index( $args )
{
if ( is_array( $args ) )
{
$media_index = new JSONIndex();
return $media_index -> get( $args );
}
else
{
return '<!-- Missing the directory to process. [json-index dir=""]-->';
}
}
/**
* Check context (WordPress Plugin File or Directory Index File).
*
* The following checks to see whether or not this file (index.php) is being loaded
* as part of the WordPress package, or not. If it is, we expect a WordPress
* function to be available (in this case, `add_shortcode`). We then ensure there
* is no direct access and add the shortcode hook, `media-index`. If we are not in
* WordPress, then this file acts as an "indexing" type of file by listing all
* of the allowed media types (currently jpg, png, mp3 and mp4) and making them
* viewable to the end user by wrapping them in HTML and making use of a css
* file that is expected to be found at `/0/media/theme/css/style.css`. This
* idea was developed out of work to find a more robust method to develop out a
* site, including that for a community. It makes use of the package found at:
* {@link https://github.com/earth3300/ec01/wiki/}, with the entire codeset
* available there through the same link.
*/
if( function_exists( 'add_shortcode' ) )
{
// No direct access.
defined('ABSPATH') || exit('No direct access.');
//shortcode [json-index dir=""]
add_shortcode( 'json-index', 'json_index' );
}
else
{
/**
* Outside of WordPress. Instantiate directly, assuming current directory.
*
* @return string
*/
$json_index = new JSONIndex();
echo $json_index -> get();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment