Created
October 12, 2018 16:09
-
-
Save earth3300/8a90833d6f67f9083acd3bb92a74c46d to your computer and use it in GitHub Desktop.
Order Colors By HSL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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