/* global wp, _validateWCAGColorContrastExports */
/* exported validateWCAGColorContrast */
let validateWCAGColorContrast = ( function ( $ , api , exports ) {
let self = { validate _color _contrast : [ ] } ;
if ( exports ) {
$ . extend ( self , exports ) ;
}
/ * *
* Add contrast validation to a control if it is entitled ( is a valid color control ) .
*
* @ param { wp . customize . Control } setting - Control .
* @ param { wp . customize . Value } setting . validationMessage - Validation message .
* @ return { boolean } Whether validation was added .
* /
self . addWCAGColorContrastValidation = function ( setting ) {
let initialValidate ;
if ( ! self . isColorControl ( setting ) ) {
return false ;
}
initialValidate = setting . validate ;
/ * *
* Wrap the setting ' s validate ( ) method to do validation on the value to be sent to the server .
*
* @ param { mixed } value - New value being assigned to the setting .
* @ returns { * }
* /
setting . validate = function ( value ) {
let setting = this , title , validationError ;
let current _color = value ;
let current _id = this . id ;
let all _color _controls = _ . union ( _ . flatten ( _ . values ( self . validate _color _contrast ) ) ) ;
// remove other (old) notifications
_ . each ( _ . without ( all _color _controls , current _id ) , function ( other _color _control _id ) {
let other _control = api . control . instance ( other _color _control _id ) ;
notice = other _control . container . find ( '.notice' ) ;
notice . hide ( ) ;
} ) ;
// find other color controls and check contrast with current color control
let other _color _controls = self . validate _color _contrast [ current _id ] ;
_ . each ( other _color _controls , function ( other _color _control _id ) {
let other _control = api . control . instance ( other _color _control _id ) ;
let other _color = other _control . container . find ( '.color-picker-hex' ) . val ( ) ;
let name = $ ( '#customize-control-' + other _color _control _id + ' .customize-control-title' ) . text ( ) ;
let contrast = self . hex ( current _color , other _color ) ;
let score = self . score ( contrast ) ;
// contrast >= 7 ? "AAA" : contrast >= 4.5 ? "AA" : ""
if ( contrast < 4.5 ) {
setting . notifications . remove ( other _color _control _id ) ;
validationWarning = new api . Notification ( other _color _control _id , { message : self . sprintf ( 'WCAG conflict with "%s"<br/>contrast: %s' , name , contrast ) , type : 'warning' } ) ;
setting . notifications . add ( validationWarning . code , validationWarning ) ;
// console.log( color_control_id + ' ' + color + ' ' + contrast + ' ' + score );
} else {
setting . notifications . remove ( other _color _control _id ) ;
}
} ) ;
return value ;
} ;
return true ;
} ;
/ * *
* Return whether the setting is entitled ( i . e . if it is a title or has a title ) .
*
* @ param { wp . customize . Setting } setting - Setting .
* @ returns { boolean }
* /
self . isColorControl = function ( setting ) {
return _ . findKey ( self . validate _color _contrast , function ( key , value ) {
return value == setting . id ;
} ) ;
} ;
api . bind ( 'add' , function ( setting ) {
self . addWCAGColorContrastValidation ( setting ) ;
} ) ;
self . sprintf = function ( format ) {
for ( let i = 1 ; i < arguments . length ; i ++ ) {
format = format . replace ( /%s/ , arguments [ i ] ) ;
}
return format ;
} ;
/ * *
* Methods used to calculate WCAG Color Contrast
* /
// from https://github.com/sindresorhus/hex-rgb
self . hexRgb = function ( hex ) {
if ( typeof hex !== 'string' ) {
throw new TypeError ( 'Expected a string' ) ;
}
hex = hex . replace ( /^#/ , '' ) ;
if ( hex . length === 3 ) {
hex = hex [ 0 ] + hex [ 0 ] + hex [ 1 ] + hex [ 1 ] + hex [ 2 ] + hex [ 2 ] ;
}
let num = parseInt ( hex , 16 ) ;
return [ num >> 16 , num >> 8 & 255 , num & 255 ] ;
} ;
// from https://github.com/tmcw/relative-luminance
// # Relative luminance
//
// http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
// https://en.wikipedia.org/wiki/Luminance_(relative)
// https://en.wikipedia.org/wiki/Luminosity_function
// https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients
// red, green, and blue coefficients
let rc = 0.2126 ,
gc = 0.7152 ,
bc = 0.0722 ,
// low-gamma adjust coefficient
lowc = 1 / 12.92 ;
self . adjustGamma = function ( g ) {
return Math . pow ( ( g + 0.055 ) / 1.055 , 2.4 ) ;
} ;
/ * *
* Given a 3 - element array of R , G , B varying from 0 to 255 , return the luminance
* as a number from 0 to 1.
* @ param { Array < number > } rgb 3 - element array of a color
* @ returns { number } luminance , between 0 and 1
* @ example
* var luminance = require ( 'relative-luminance' ) ;
* var black _lum = luminance ( [ 0 , 0 , 0 ] ) ; // 0
* /
self . relativeLuminance = function ( rgb ) {
let rsrgb = rgb [ 0 ] / 255 ;
let gsrgb = rgb [ 1 ] / 255 ;
let bsrgb = rgb [ 2 ] / 255 ;
let r = rsrgb <= 0.03928 ? rsrgb * lowc : self . adjustGamma ( rsrgb ) ,
g = gsrgb <= 0.03928 ? gsrgb * lowc : self . adjustGamma ( gsrgb ) ,
b = bsrgb <= 0.03928 ? bsrgb * lowc : self . adjustGamma ( bsrgb ) ;
return r * rc + g * gc + b * bc ;
} ;
// from https://github.com/tmcw/wcag-contrast
/ * *
* Get the contrast ratio between two relative luminance values
* @ param { number } a luminance value
* @ param { number } b luminance value
* @ returns { number } contrast ratio
* @ example
* luminance ( 1 , 1 ) ; // = 1
* /
self . luminance = function ( a , b ) {
let l1 = Math . max ( a , b ) ;
let l2 = Math . min ( a , b ) ;
return ( l1 + 0.05 ) / ( l2 + 0.05 ) ;
} ;
/ * *
* Get a score for the contrast between two colors as rgb triplets
* @ param { array } a
* @ param { array } b
* @ returns { number } contrast ratio
* @ example
* rgb ( [ 0 , 0 , 0 ] , [ 255 , 255 , 255 ] ) ; // = 21
* /
self . rgb = function ( a , b ) {
return self . luminance ( self . relativeLuminance ( a ) , self . relativeLuminance ( b ) ) ;
} ;
/ * *
* Get a score for the contrast between two colors as hex strings
* @ param { string } a hex value
* @ param { string } b hex value
* @ returns { number } contrast ratio
* @ example
* hex ( '#000' , '#fff' ) ; // = 21
* /
self . hex = function ( a , b ) {
return self . rgb ( self . hexRgb ( a ) , self . hexRgb ( b ) ) ;
} ;
/ * *
* Get a textual score from a numeric contrast value
* @ param { number } contrast
* @ returns { string } score
* @ example
* score ( 10 ) ; // = 'AAA'
* /
self . score = function ( contrast ) {
return contrast >= 7 ? 'AAA' : contrast >= 4.5 ? 'AA' : '' ;
} ;
return self ;
} ( jQuery , wp . customize , _validateWCAGColorContrastExports ) ) ;