DebugConfig ( {
//all: true,
generate _table : 0 ,
SI : 1 ,
SI _set _options : 1 ,
SI _set _precision : 0 ,
timingUIChange : 1 ,
} ) ;
CTA861 = { } ;
// The intepreted current state of all input fields is stored here
Global _InputVars = {
'HRES' : '' , // Int
'VRES' : '' , // Int
'FREQ' : '' , // Float
'COLOR_DEPTH' : '' ,
'PIXEL_FORMAT' : '' ,
'COMP' : '' ,
'SCAN' : '' ,
'MARGINS' : '' ,
'TIMING_STD' : '' ,
'V_FP' : '' ,
'V_BP' : '' ,
'V_SW' : '' ,
'H_FP' : '' ,
'H_BP' : '' ,
'H_SW' : '' ,
}
Encoding _Overhead = {
'8b/10b' : 1.25 ,
'16b/18b' : 1.125 ,
} ;
Interface = [
{
name : "DisplayPort 1.3–1.4" ,
encoding : "8b/10b" ,
datarate : 25.92 * ( 10 * * 9 ) ,
} , {
name : "DisplayPort 1.2" ,
encoding : "8b/10b" ,
datarate : 17.28 * ( 10 * * 9 ) ,
} , {
name : "DisplayPort 1.0–1.1" ,
encoding : "8b/10b" ,
datarate : 8.64 * ( 10 * * 9 ) ,
} , {
name : "HDMI 2.1" ,
encoding : "16b/18b" ,
datarate : 48 * ( 10 * * 9 ) * ( 16 / 18 ) ,
} , {
name : "HDMI 2.0" ,
encoding : "8b/10b" ,
datarate : 14.4 * ( 10 * * 9 ) ,
} , {
name : "HDMI 1.3–1.4" ,
encoding : "8b/10b" ,
datarate : 8.16 * ( 10 * * 9 ) ,
} , {
name : "HDMI 1.0–1.2" ,
encoding : "8b/10b" ,
datarate : 3.96 * ( 10 * * 9 ) ,
} , {
name : "Dual-Link DVI" ,
encoding : "8b/10b" ,
datarate : 7.92 * ( 10 * * 9 ) ,
} , {
name : "Single-Link DVI" ,
encoding : "8b/10b" ,
datarate : 3.96 * ( 10 * * 9 ) ,
} , {
name : "Thunderbolt 3 (Gen 2)" ,
encoding : "unknown" ,
datarate : 40 * ( 10 * * 9 ) ,
} , {
name : "Thunderbolt 3 (Gen 1)" ,
encoding : "unknown" ,
datarate : 34.56 * ( 10 * * 9 ) ,
} , {
name : "Thunderbolt 2" ,
encoding : "unknown" ,
datarate : 17.28 * ( 10 * * 9 ) ,
} , {
name : "Thunderbolt" ,
encoding : "unknown" ,
datarate : 8.64 * ( 10 * * 9 ) ,
} ,
] ;
function submitVar ( id , val ) {
DEBUG ( id , val ) ;
var len = 0 ;
var GlobalVars _Key = '' ;
if ( typeof ( val ) === 'number' ) { val = val . toString ( ) ; }
if ( id == 'INPUT_HRES' || id == 'INPUT_VRES' ) {
if ( id == 'INPUT_HRES' ) GlobalVars _Key = 'HRES' ;
else if ( id == 'INPUT_VRES' ) GlobalVars _Key = 'VRES' ;
val = parseNum ( val ) ;
if ( isNum ( val ) == true ) {
val = Math . abs ( parseInt ( val ) ) ;
Global _InputVars [ GlobalVars _Key ] = val ;
}
else {
DEBUG ( id + ' input is not a number:' , val ) ;
Global _InputVars [ GlobalVars _Key ] = '' ;
}
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
}
else if ( id == 'INPUT_F' ) {
GlobalVars _Key = 'FREQ' ;
val = parseNum ( val ) ;
if ( isNum ( val ) == true ) {
Global _InputVars [ GlobalVars _Key ] = Math . abs ( val ) ;
}
else {
DEBUG ( id + ' input is not a number:' , val ) ;
Global _InputVars [ GlobalVars _Key ] = '' ;
}
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
}
else if ( id == 'COLOR_DEPTH_FORM' ) {
// Value is either the color depth number (in bits per pixel), or the string 'Custom'
GlobalVars _Key = 'COLOR_DEPTH' ;
colordepthUIChange ( ) ;
if ( val != 'Custom' ) { Global _InputVars [ GlobalVars _Key ] = val ; }
else if ( val == 'Custom' ) { // Custom color depth
// If Custom, grab the number from the custom color depth input field
val = parseInt ( parseNum ( $ ( '#CUSTOM_COLOR_DEPTH' ) . val ( ) ) ) ;
if ( isNum ( val ) == true ) {
if ( $ ( 'input[name=CD_UNIT_SLCT]:checked' ) . val ( ) == 'bpc' ) {
val = val * 3 ; // If user enters values in units of bpc, then multiply by 3 to convert to bit/px (bpp)
}
Global _InputVars [ GlobalVars _Key ] = Math . abs ( val ) ;
}
else {
DEBUG ( id + ' input is not a number:' , val ) ;
Global _InputVars [ GlobalVars _Key ] = '' ;
}
}
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
}
else if ( id == 'PIXEL_FORMAT_FORM' ) {
GlobalVars _Key = 'PIXEL_FORMAT' ;
if ( val == 'RGB' || val == 'YCBCR 4:4:4' ) { val = 1.0 ; }
else if ( val == 'YCBCR 4:2:2' ) { val = 1.5 ; }
else if ( val == 'YCBCR 4:2:0' ) { val = 2.0 ; }
Global _InputVars [ GlobalVars _Key ] = val ;
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
}
else if ( id == 'COMPRESSION_FORM' ) {
GlobalVars _Key = 'COMP' ;
if ( val == 'Uncompressed' ) { val = 1.0 ; }
else if ( val == 'DSC 2.0x' ) { val = 2.0 ; }
else if ( val == 'DSC 2.5x' ) { val = 2.5 ; }
else if ( val == 'DSC 3.0x' ) { val = 3.0 ; }
Global _InputVars [ GlobalVars _Key ] = val ;
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
}
else if ( id == 'SCAN_FORM' ) {
GlobalVars _Key = 'SCAN' ;
if ( val == 'p' ) { val = 1 ; }
else if ( val == 'i' ) { val = 2 ; }
Global _InputVars [ GlobalVars _Key ] = val ;
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
timingUIChange ( ) ;
DEBUG ( 'Set interlaced timing row display to:' , $ ( '#Interlaced_Timing_Row' ) . css ( 'display' ) ) ;
}
else if ( id == 'MARGINS_FORM' ) {
// val is either 'y' or 'n'
GlobalVars _Key = 'MARGINS' ;
marginsUIChange ( ) ;
if ( val == 'n' ) {
Global _InputVars [ GlobalVars _Key ] = 0 ;
}
else if ( val == 'y' ) {
val = parseFloat ( parseNum ( $ ( '#CUSTOM_MARGINS' ) . val ( ) ) ) ;
if ( isNum ( val ) == true ) {
Global _InputVars [ GlobalVars _Key ] = Math . abs ( val ) ;
}
else {
DEBUG ( id + ' input is not a number:' , val ) ;
Global _InputVars [ GlobalVars _Key ] = '' ;
}
}
else { DEBUG ( 'invalid input combination. id/val = ' , id , val ) } // If val is not 'y' or 'n', then something somewhere has gone terribly wrong
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
}
else if ( id == 'TIMING_DROP' ) {
GlobalVars _Key = 'TIMING_STD' ;
Global _InputVars [ GlobalVars _Key ] = val ;
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
timingUIChange ( ) ;
}
else if ( id == 'V_FP' || id == 'V_BP' || id == 'V_SW' ||
id == 'H_FP' || id == 'H_BP' || id == 'H_SW' ||
id == 'V_FP_ODD' || id == 'V_SW_ODD' || id == 'V_BP_ODD' ) {
GlobalVars _Key = id ;
val = parseFloat ( parseNum ( val ) ) ;
if ( isNum ( val ) ) {
val = Math . abs ( val ) ;
Global _InputVars [ id ] = val ;
$ ( '#' + id ) . val ( val ) ;
$ ( '#V_BLANK' ) . html ( Global _InputVars [ 'V_FP' ] + Global _InputVars [ 'V_BP' ] + Global _InputVars [ 'V_SW' ] ) ;
$ ( '#H_BLANK' ) . html ( Global _InputVars [ 'H_FP' ] + Global _InputVars [ 'H_BP' ] + Global _InputVars [ 'H_SW' ] ) ;
$ ( '#V_BLANK_ODD' ) . html ( Global _InputVars [ 'V_FP_ODD' ] + Global _InputVars [ 'V_BP_ODD' ] + Global _InputVars [ 'V_SW_ODD' ] ) ;
}
else {
DEBUG ( id + ' input is not a number:' , val ) ;
Global _InputVars [ id ] = '' ;
$ ( '#' + id ) . val ( '' ) ;
$ ( '#V_BLANK' ) . html ( '' ) ;
$ ( '#H_BLANK' ) . html ( '' ) ;
$ ( '#V_BLANK_ODD' ) . html ( '' ) ;
}
DEBUG ( "Global_InputVars['" + GlobalVars _Key + "'] was set to" , Global _InputVars [ GlobalVars _Key ] ) ;
// Update value displayed in the UI input field
// Update total Vblank and Hblank numbers on the UI
}
}
function generate _table ( type , input _datarate ) {
if ( type == "Interface Support" ) {
table = $ ( "#interface_support_table" ) ;
contents = '' ;
contents += ( '<tr><th class="title" colspan="6">Interface Support</th></tr>' ) ;
contents += ( '<tr>' ) ;
contents += ( '<th rowspan="2">% Usage</th>' ) ;
contents += ( '<th rowspan="2">Interface</th>' ) ;
contents += ( '<th rowspan="2">Encoding</th>' ) ;
contents += ( '<th colspan="3" style="border-bottom:1px solid #FFFFFF;">Maximum</th></tr>' ) ;
contents += ( '<tr><th>Pixel Clock</th><th>Datarate</th><th>Bandwidth</th></tr>' ) ;
var name ;
var encoding ;
var datarate ;
var pixel _clock ;
var bandwidth ;
var overhead ;
var saturation ;
for ( var x = 0 ; x < Interface . length ; x ++ ) {
name = Interface [ x ] [ 'name' ] ;
encoding = Interface [ x ] [ 'encoding' ] ;
if ( encoding == 'unknown' ) {
datarate = Interface [ x ] [ 'datarate' ] ;
bandwidth = '?' ;
encoding = '?' ;
}
else {
overhead = Encoding _Overhead [ encoding ] ;
datarate = Interface [ x ] [ 'datarate' ] ;
bandwidth = datarate * overhead ;
}
pixel _clock = datarate / 24 ;
saturation = input _datarate / datarate ;
if ( input _datarate == - 1 ) {
contents += ( '<tr><td></td>' ) ;
}
else {
contents += ( '<tr><td>' + saturation . toFixed ( 1 ) + '%</td>' ) ;
}
contents += ( '<td style="text-align:left; white-space:nowrap;">' + name + '</td><td>' + encoding + '</td><td>' + SI ( pixel _clock , 'Hz' , { p : [ 'M0' , 'G3' ] } ) + '</td><td>' + SI ( datarate , 'bit/s' , 2 ) + '</td><td>' + SI ( bandwidth , 'bit/s' , 2 ) + '</td></tr>' ) ;
}
table . html ( contents ) ;
return ;
}
else if ( type == "Maximum Refresh Frequency" ) {
return ;
}
return ;
}
function input _ok ( ) {
// This checks to make sure all the necessary fields are filled in, and their contents are valid, before allowing the Calculation function to proceed.
var val ;
var checklist = [ 'HRES' , 'VRES' , 'FREQ' ] ;
for ( var i = 0 ; i < checklist . length ; i ++ ) {
val = Global _InputVars [ checklist [ i ] ] ;
if ( ! ( isPositive ( val ) ) ) {
DEBUG ( 'Calculation aborted. ' + checklist [ i ] + ' contains an empty or non-numeric string.' , val ) ;
return false ;
}
}
if ( $ ( 'input[name=COLOR_DEPTH_SLCT]:checked' ) . val ( ) == 'Custom' ) {
val = Global _InputVars [ 'COLOR_DEPTH' ] ;
if ( ! ( isPositive ( val ) ) ) {
DEBUG ( 'Calculation aborted. COLOR_DEPTH_SLCT is set to Custom, but CUSTOM_COLOR_DEPTH contains an empty or non-numeric string, or 0.' , val ) ;
return false ;
}
}
if ( Global _InputVars [ 'TIMING_STD' ] == 'Custom' ) {
checklist = [ 'V_FP' , 'V_BP' , 'V_SW' , 'H_FP' , 'H_BP' , 'H_SW' ] ;
var abort = false ;
for ( var i = 0 ; i < checklist . length ; i ++ ) {
val = Global _InputVars [ checklist [ i ] ] ;
if ( ! ( isPositiveZ ( val ) ) ) {
abort = true ;
}
}
if ( abort == true ) {
DEBUG ( 'Calculation aborted. TIMING_DROP is set to Custom, but one or more timing parameter fields contains an empty or non-numeric string.' , Global _InputVars ) ;
return false ;
}
}
if ( $ ( 'input[name=MARGINS_SLCT]:checked' ) . val ( ) == 'y' ) {
val = Global _InputVars [ 'MARGINS' ] ;
if ( ! ( isPositiveZ ( val ) ) ) {
DEBUG ( 'Calculation aborted. MARGINS_SLCT is set to Yes, but CUSTOM_MARGINS contains an empty or non-numeric string.' , val ) ;
return false ;
}
}
return true ;
}
function calcMain ( ) {
if ( ! input _ok ( ) ) { clearResults ( ) ; return ; }
var hres = Global _InputVars [ 'HRES' ] ;
var vres = Global _InputVars [ 'VRES' ] ;
var freq = Global _InputVars [ 'FREQ' ] ;
var color _depth = Global _InputVars [ 'COLOR_DEPTH' ] ;
var px _format = Global _InputVars [ 'PIXEL_FORMAT' ] ;
var comp = Global _InputVars [ 'COMP' ] ;
var scan = Global _InputVars [ 'SCAN' ] ;
var timing _standard = Global _InputVars [ 'TIMING_STD' ] ;
// Get timing parameters
Timing = getTiming ( timing _standard ) ;
DEBUG ( 'Timing:' , Timing ) ;
var results = {
hres : hres ,
vres : vres ,
freq : freq ,
px _bits : color _depth ,
px _format : px _format ,
comp : comp ,
scan : scan ,
Timing : Timing ,
// Calculate results
// Resolution with blanking intervals
h _eff : Timing [ 'H_EFF' ] ,
v _eff : Timing [ 'V_EFF' ] ,
// Aspect ratio
ratio _num : hres / vres ,
ratio _str : ( hres / GCD ( hres , vres ) ) + ':' + ( vres / GCD ( hres , vres ) ) ,
// Total pixel count of image
px _per _frame : hres * vres ,
px _per _frame _eff : ( Timing [ 'H_EFF' ] ) * ( Timing [ 'V_EFF' ] ) ,
// Size (bits) of one frame
bits _per _frame : hres * vres * color _depth ,
bits _per _frame _eff : ( Timing [ 'H_EFF' ] ) * ( Timing [ 'V_EFF' ] ) * color _depth ,
// Pixel clock
px _per _sec : hres * vres * Timing [ 'F_ACTUAL' ] ,
px _per _sec _eff : ( Timing [ 'H_EFF' ] ) * ( Timing [ 'V_EFF' ] ) * Timing [ 'F_ACTUAL' ] ,
// Raw bit rate
bits _per _sec _eff : ( Timing [ 'H_EFF' ] ) * ( Timing [ 'V_EFF' ] ) * color _depth * Timing [ 'F_ACTUAL' ] ,
} ;
DEBUG ( 'Results:' , SI ( results [ 'bits_per_sec_eff' ] , 'bit/s' , 2 ) , results ) ;
updateDisplay ( results ) ;
}
function getTiming ( timing _standard ) {
// Just a traffic control function
// Actual parameters are calculated in dedicated functions for each standard
if ( ! ( isPositiveZ ( Global _InputVars [ 'HRES' ] ) && isPositiveZ ( Global _InputVars [ 'VRES' ] ) && isPositiveZ ( Global _InputVars [ 'FREQ' ] ) ) ) {
DEBUG ( 'Timing calculation aborted, HRES, VRES, or FREQ contains invalid input.' , Global _InputVars [ 'HRES' ] , Global _InputVars [ 'VRES' ] , Global _InputVars [ 'FREQ' ] ) ;
return {
'V_FP' : '' ,
'V_BP' : '' ,
'V_SW' : '' ,
'H_FP' : '' ,
'H_BP' : '' ,
'H_SW' : '' ,
'V_BL' : '' ,
'H_BL' : '' ,
'F_ACTUAL' : Global _InputVars [ 'FREQ' ] ,
} ;
}
if ( timing _standard != 'Custom' ) {
if ( timing _standard == 'CVT-R2' ) {
Timing = CVT _R ( 2 ) ;
}
else if ( timing _standard == 'CVT-RB' ) {
Timing = CVT _R ( 1 ) ;
}
else if ( timing _standard == 'CVT' ) {
Timing = CVT ( ) ;
}
else if ( timing _standard == 'GTF' ) {
Timing = GTF ( ) ;
}
else if ( timing _standard == 'DMT' ) {
Timing = DMT ( ) ;
}
else if ( timing _standard == 'CTA-861' ) {
Timing = CTA ( ) ;
}
else if ( timing _standard == 'None' ) {
Timing = {
'V_FP' : 0 ,
'V_BP' : 0 ,
'V_SW' : 0 ,
'H_FP' : 0 ,
'H_BP' : 0 ,
'H_SW' : 0 ,
'V_FP_ODD' : 0 ,
'V_BP_ODD' : 0 ,
'V_SW_ODD' : 0 ,
'V_BL' : 0 ,
'H_BL' : 0 ,
'F_ACTUAL' : Global _InputVars [ 'FREQ' ] ,
}
}
// Update UI timing parameter fields with the newly generated timings
submitVar ( 'V_FP' , Timing [ 'V_FP' ] ) ;
submitVar ( 'V_BP' , Timing [ 'V_BP' ] ) ;
submitVar ( 'V_SW' , Timing [ 'V_SW' ] ) ;
submitVar ( 'H_FP' , Timing [ 'H_FP' ] ) ;
submitVar ( 'H_BP' , Timing [ 'H_BP' ] ) ;
submitVar ( 'H_SW' , Timing [ 'H_SW' ] ) ;
submitVar ( 'V_FP_ODD' , Timing [ 'V_FP_ODD' ] ) ;
submitVar ( 'V_SW_ODD' , Timing [ 'V_SW_ODD' ] ) ;
submitVar ( 'V_BP_ODD' , Timing [ 'V_BP_ODD' ] ) ;
}
else if ( timing _standard == 'Custom' ) {
// Read the timing from the UI
submitVar ( 'V_FP' , $ ( '#V_FP' ) . val ( ) ) ;
submitVar ( 'V_BP' , $ ( '#V_BP' ) . val ( ) ) ;
submitVar ( 'V_SW' , $ ( '#V_SW' ) . val ( ) ) ;
submitVar ( 'H_FP' , $ ( '#H_FP' ) . val ( ) ) ;
submitVar ( 'H_BP' , $ ( '#H_BP' ) . val ( ) ) ;
submitVar ( 'H_SW' , $ ( '#H_SW' ) . val ( ) ) ;
submitVar ( 'V_FP_ODD' , Global _InputVars [ 'V_FP' ] + 0.5 ) ;
submitVar ( 'V_SW_ODD' , Global _InputVars [ 'V_SW' ] ) ;
submitVar ( 'V_BP_ODD' , Global _InputVars [ 'V_BP' ] + 0.5 ) ;
Timing = {
'V_FP' : Global _InputVars [ 'V_FP' ] ,
'V_BP' : Global _InputVars [ 'V_BP' ] ,
'V_SW' : Global _InputVars [ 'V_SW' ] ,
'H_FP' : Global _InputVars [ 'H_FP' ] ,
'H_BP' : Global _InputVars [ 'H_BP' ] ,
'H_SW' : Global _InputVars [ 'H_SW' ] ,
'V_FP_ODD' : Global _InputVars [ 'V_FP_ODD' ] ,
'V_BP_ODD' : Global _InputVars [ 'V_BP_ODD' ] ,
'V_SW_ODD' : Global _InputVars [ 'V_SW_ODD' ] ,
'V_BL' : Global _InputVars [ 'V_FP' ] + Global _InputVars [ 'V_BP' ] + Global _InputVars [ 'V_SW' ] ,
'H_BL' : Global _InputVars [ 'H_FP' ] + Global _InputVars [ 'H_BP' ] + Global _InputVars [ 'H_SW' ] ,
'F_ACTUAL' : Global _InputVars [ 'FREQ' ] ,
}
}
$ ( '#V_BLANK' ) . html ( Timing [ 'V_BL' ] ) ;
$ ( '#H_BLANK' ) . html ( Timing [ 'H_BL' ] ) ;
return Timing ;
}
function CVT _R ( R ) { // Variable R is an integer representing the reduced blanking revision to use, version 1 or version 2.
var H = Global _InputVars [ 'HRES' ] ; // Horizontal active pixels
var V = Global _InputVars [ 'VRES' ] ; // Vertical active pixels
var F = Global _InputVars [ 'FREQ' ] ; // Nominal vertical refresh frequency
var S = Global _InputVars [ 'SCAN' ] ; // 1 for progressive scan, 2 for interlaced
var M = Global _InputVars [ 'MARGINS' ] ; // Margins (%)
// Declaring variables for all results
var V _FP ; // Vertical front porch
var V _SW ; // Vertical sync width
var V _BP ; // Vertical back porch
var H _FP ; // Horizontal front porch
var H _SW ; // Horizontal sync width
var H _BP ; // Horizontal back porch
var V _BLANK ; // Total vertical blanking (V_FP + V_SW + V_BP)
var H _BLANK ; // Total horizontal blanking (H_FP + H_SW + H_BP)
var V _EFF ; // V + V_Blank + V_Margins
var H _EFF ; // H + H_Blank + H_Margins
var F _ACTUAL ; // Actual vertical refresh frequency (after pixel clock rounding)
var F _HOR ; // Horizontal refresh frequency
// Common constants
var V _PER _MIN = 0.00046 ; /* Minimum vertical blanking period for reduced blank timing (in seconds), defined by VESA CVT 1.2 standard */
var V _LINES = Math . floor ( V / S ) // If progressive scan, S = 1 and V_LINES = V. If interlaced, V_LINES = floor(V / 2).
if ( R == 1 ) {
// CVT-RB constants
H _BLANK = 160 ;
H _FP = 48 ;
H _BP = 80 ;
H _SW = 32 ;
V _FP = 3 ;
var V _BP _MIN = 6 ;
// All H timings are defined, as well as V_FP. Only V_SW and V_BP remain (and V_BLANK, the sum of all 3 V parameters)
// Determine vertical sync width (V_SW) from table of magic numbers defined in VESA CVT standard
var V _SYNC _TABLE = [
[ 4 / 3 , 4 ] ,
[ 16 / 9 , 5 ] ,
[ 8 / 5 , 6 ] ,
[ 5 / 3 , 7 ] ,
[ 5 / 4 , 7 ] ,
]
V _SW = 10 ; // default value defined in standard
for ( var i = 0 ; i < V _SYNC _TABLE . length ; i ++ ) {
if ( ( H / V ) - V _SYNC _TABLE [ i ] [ 0 ] < 0.05 ) { // Check if aspect ratio of image (H/V) matches an aspect ratio defined in table, within 0.05
V _SW = V _SYNC _TABLE [ i ] [ 1 ] ;
}
}
// V_BP is determined in reverse, by calculating V_BLANK first (the sum of V_FP, V_SW, and V_BP) and subtracting out V_FP and V_SW.
var CellGran = 8 ; // Cell granularity constant defined by CVT standard
var H _RND = Math . floor ( H / CellGran ) * CellGran ; // Round down horizontal resolution to be a multiple 8
var V _MARGIN = Math . floor ( M / 100 ) * V _LINES ; // If margins percent (M) is 0, this result is 0
var H _MARGIN = Math . floor ( H _RND * M / 100 / CellGran ) * CellGran ; // If margins percent (M) is 0, this result is 0
var H _PER _EST = ( ( 1 / F ) - V _PER _MIN ) / ( V _LINES + ( 2 * V _MARGIN ) ) ; // Horizontal blanking period estimate
V _BLANK = Math . floor ( ( V _PER _MIN / H _PER _EST ) + 1 ) ;
var V _BLANK _MIN = V _FP + V _SW + V _BP _MIN ;
if ( V _BLANK < V _BLANK _MIN ) { V _BLANK = V _BLANK _MIN ; } // Enforce minimum value for V_blank
V _BP = V _BLANK - ( V _FP + V _SW ) ;
V _EFF = V _LINES + V _BLANK + V _MARGIN + ( ( S - 1 ) / 2 ) ; // (S-1)/2 = 0 for progressive, 0.5 for interlaced
H _EFF = H _RND + H _BLANK + H _MARGIN ;
// Calculate pixel clock, to enforce pixel clock rounding to the nearest 250 kHz, as required by the CVT standard
var CLK = F * ( V _EFF ) * ( H _EFF ) ; // Pixel clock (Hz)
CLK = Math . floor ( CLK / 250000 ) * 250000 ; // Pixel clock (Hz) rounded down to the next multiple of 0.25 MHz (250 kHz, 250000 Hz)
F _HOR = CLK / ( H _EFF ) ; // Horizontal refresh frequency (Hz)
F _ACTUAL = F _HOR / ( V _EFF ) ; // Pixel clock rounding is enforced via lowering the vertical refresh frequency to adjust pixel clock
}
else if ( R == 2 ) {
// CVT-R2 constants defined by CVT standard
H _BLANK = 80 ;
H _FP = 8 ;
H _BP = 40 ;
H _SW = 32 ;
V _BP = 6 ;
V _SW = 8 ;
var V _FP _MIN = 1 ;
// All parameters are defined as constant except V_FP
// V_FP is determined in reverse, by calculating V_BLANK first (the sum of V_FP, V_SW, and V_BP) and subtracting V_SW and V_BP out.
var V _MARGIN = Math . floor ( M / 100 ) * V _LINES ; // If margins percent (M) is 0, this result is 0
var H _MARGIN = Math . floor ( H * M / 100 ) ; // If margins percent (M) is 0, this result is 0
var H _PER _EST = ( ( 1 / F ) - V _PER _MIN ) / ( V _LINES + ( 2 * V _MARGIN ) ) ; // Horizontal blanking period estimate
V _BLANK = Math . floor ( ( V _PER _MIN / H _PER _EST ) + 1 ) ;
var V _BLANK _MIN = V _FP _MIN + V _SW + V _BP ;
if ( V _BLANK < V _BLANK _MIN ) { V _BLANK = V _BLANK _MIN ; } // Enforce minimum value for V_blank
V _FP = V _BLANK - ( V _BP + V _SW ) ;
V _EFF = V _LINES + V _BLANK + V _MARGIN + ( ( S - 1 ) / 2 ) ; // (S-1)/2 = 0 for progressive, 0.5 for interlaced
H _EFF = H + H _BLANK + H _MARGIN ;
// Calculate pixel clock, to enforce pixel clock rounding to the nearest 1 kHz, as required by the CVT standard
var CLK = F * ( V _EFF ) * ( H _EFF ) ; // Pixel clock (Hz)
CLK = Math . floor ( CLK / 1000 ) * 1000 ; // Pixel clock (Hz) rounded down to the next multiple of 0.001 MHz (1 kHz, 1000 Hz)
F _HOR = CLK / ( H _EFF ) ; // Horizontal refresh frequency (Hz)
F _ACTUAL = F _HOR / ( V _EFF ) ;
}
//DEBUG('CLK', CLK);
//DEBUG('F_HOR', F_HOR);
//DEBUG('F_ACTUAL', F_ACTUAL);
//DEBUG(V_BLANK);
return {
V _FP : V _FP , // For interlaced, these vertical timing are used for the even fields
V _BP : V _BP ,
V _SW : V _SW ,
H _FP : H _FP ,
H _BP : H _BP ,
H _SW : H _SW ,
V _FP _ODD : V _FP + 0.5 , // For interlaced, V_FP and V_BP are 0.5 higher for odd fields (V_SW is same)
V _SW _ODD : V _SW ,
V _BP _ODD : V _BP + 0.5 ,
V _BL : V _BLANK ,
H _BL : H _BLANK ,
V _EFF : V _EFF ,
H _EFF : H _EFF ,
F _ACTUAL : F _ACTUAL ,
} ;
}
function CVT ( ) {
return ;
}
function CTA ( ) {
return ;
}
function updateDisplay ( ) {
return ;
}
function clearResults ( ) {
return ;
}
function timingUIChange ( ) {
// Controls the enabled/disabled state of the custom timing format input fields
var timing _params = [
$ ( '#V_FP' ) ,
$ ( '#V_BP' ) ,
$ ( '#V_SW' ) ,
$ ( '#H_FP' ) ,
$ ( '#H_BP' ) ,
$ ( '#H_SW' ) ,
] ;
value = $ ( '#TIMING_DROP' ) . val ( ) ;
if ( value == 'Custom' ) {
for ( var i = 0 ; i < timing _params . length ; i ++ ) {
param = timing _params [ i ] ;
param . prop ( 'disabled' , false ) ;
//if (field.prop('oldvalue') != '') {
// field.val(field.prop('oldvalue'));
//}
}
}
else {
for ( var i = 0 ; i < timing _params . length ; i ++ ) {
param = timing _params [ i ] ;
param . prop ( 'disabled' , true ) ;
//field.prop('oldvalue', field.val());
//field.val('');
}
}
value = $ ( 'input[name=SCAN_SLCT]:checked' ) . val ( ) ;
if ( value == 'i' ) {
$ ( '#V_BLANK_ODD_LABEL' ) . css ( 'display' , 'table-cell' ) ;
$ ( '#V_FP_ODD_CONTAINER' ) . css ( 'display' , 'table-cell' ) ;
$ ( '#V_BP_ODD_CONTAINER' ) . css ( 'display' , 'table-cell' ) ;
$ ( '#V_SW_ODD_CONTAINER' ) . css ( 'display' , 'table-cell' ) ;
$ ( '#V_BLANK_ODD_CONTAINER' ) . css ( 'display' , 'table-cell' ) ;
$ ( '#V_BLANK_EVEN_LABEL' ) . html ( '(Even) V<sub>blank</sub>' ) ;
}
else if ( value == 'p' ) {
$ ( '#V_BLANK_ODD_LABEL' ) . css ( 'display' , 'none' ) ;
$ ( '#V_FP_ODD_CONTAINER' ) . css ( 'display' , 'none' ) ;
$ ( '#V_BP_ODD_CONTAINER' ) . css ( 'display' , 'none' ) ;
$ ( '#V_SW_ODD_CONTAINER' ) . css ( 'display' , 'none' ) ;
$ ( '#V_BLANK_ODD_CONTAINER' ) . css ( 'display' , 'none' ) ;
$ ( '#V_BLANK_EVEN_LABEL' ) . html ( 'V<sub>blank</sub>' ) ;
}
else {
DEBUG ( 'Something somewhere has gone terribly wrong. Attemped to grab SCAN_SLCT value, and it was neither "p" nor "i"!' ) ;
}
}
function colordepthUIChange ( ) {
// Controls the enabled/disabled state of the custom timing format input fields
var field = $ ( '#CUSTOM_COLOR_DEPTH' ) ;
value = $ ( 'input[name=COLOR_DEPTH_SLCT]:checked' ) . val ( ) ;
if ( value == 'Custom' ) {
field . prop ( 'disabled' , false ) ;
$ ( 'input[name=CD_UNIT_SLCT]' ) . prop ( 'disabled' , false ) ;
}
else {
field . prop ( 'disabled' , true ) ;
$ ( 'input[name=CD_UNIT_SLCT]' ) . prop ( 'disabled' , true ) ;
}
}
function marginsUIChange ( ) {
// Controls the enabled/disabled state of the custom timing format input fields
var field = $ ( '#CUSTOM_MARGINS' ) ;
value = $ ( 'input[name=MARGINS_SLCT]:checked' ) . val ( ) ;
if ( value == 'y' ) {
field . prop ( 'disabled' , false ) ;
if ( field . val ( ) == '' ) {
field . val ( '1.8' ) ;
}
}
else {
field . prop ( 'disabled' , true ) ;
}
}
function SI ( value , unit , options _input ) {
DEBUG ( 'Input:' , value , unit , options _input ) ;
if ( isNaN ( parseInt ( value ) ) ) { return value ; }
if ( typeof ( options _input ) == 'number' || typeof ( options _input ) == 'string' ) { options _input = { p : options _input . toString ( ) } ; }
/ * V a l i d o p t i o n s :
SI _options = {
p : 2 Specifies default precision ( number of decimal places ) . Use '-1' or 'a' or 'adaptive' for adaptive mode ( autodetect decimal places )
Also accepts individual decimal place settings for each prefix , in array format ; for example :
{ p : [ 1 , G2 , M0 ] } sets a default of 1 for all prefixes , then specifies 2 decimal places for Giga and 0 for Mega .
p _max : 4 Maximum number of decimal places ( for adaptive precision mode )
p _min : 2 Minimum number of decimal places ( for adaptive precision mode )
separator : ',' Character to use as thousands separator ( default comma )
decimal : '.' Character to use as decimal point ( default period )
unit _sep : ' ' Character to place between the value and unit symbol ( default non - breaking space )
cap _kilo : 'true' Use capital K for kilo instead of small k . ( default false )
no _mu : 'true' Use "u" instead of "<22> " on output , if necessary for compatibility reasons ( default false )
exclude : [ 'c' , 'd' ] SI prefixes to exclude , for situational use ( i . e . for displaying time , one may not wish for "centiseconds" ) .
Symbols can also be used as shortcuts , as in the following examples :
'>=G' excludes Giga and larger
'>G' excludes larger than Giga , but not Giga itself
'<G' and "<=G" same as above , but for prefixes smaller than Giga
'*' excludes all prefixes ( unless protected )
'!G' protects a prefix from '*' , i . e . { exclude : [ '*' , '!k' , '!M' ] } excludes all prefixes except Kilo and Mega )
Multiple arguments accepted in array format
"u" is accepted as an argument for excluding "<22> " , and "0" is accepted for excluding the prefixless base unit
By default , the following are excluded already , and must be un - excluded ( using ! c , ! d , etc . ) to be used :
'c' ( centi , 10 ^ - 2 )
'd' ( deci , 10 ^ - 1 )
'D' ( deca , 10 ^ 1 )
'H' ( hecto , 10 ^ 2 )
}
* /
//console.log(SI_options['precision']);
var out _value ;
var out _prefix ;
var precision ;
var prefixDef = {
'-24' : { sym : 'y' , name : 'yocto' , p : 0 , excl : false } ,
'-21' : { sym : 'z' , name : 'zepto' , p : 0 , excl : false } ,
'-18' : { sym : 'a' , name : 'atto' , p : 0 , excl : false } ,
'-15' : { sym : 'f' , name : 'femto' , p : 0 , excl : false } ,
'-12' : { sym : 'p' , name : 'pico' , p : 0 , excl : false } ,
'-9' : { sym : 'n' , name : 'nano' , p : 0 , excl : false } ,
'-6' : { sym : '<27> ' , name : 'micro' , p : 0 , excl : false } ,
'-3' : { sym : 'm' , name : 'milli' , p : 0 , excl : false } ,
'-2' : { sym : 'c' , name : 'centi' , p : 0 , excl : false } ,
'-1' : { sym : 'd' , name : 'deci' , p : 0 , excl : false } ,
'0' : { sym : '' , name : '' , p : 0 , excl : false } ,
'1' : { sym : 'D' , name : 'deca' , p : 0 , excl : false } ,
'2' : { sym : 'H' , name : 'hecto' , p : 0 , excl : false } ,
'3' : { sym : 'k' , name : 'kilo' , p : 0 , excl : false } ,
'6' : { sym : 'M' , name : 'mega' , p : 0 , excl : false } ,
'9' : { sym : 'G' , name : 'giga' , p : 0 , excl : false } ,
'12' : { sym : 'T' , name : 'tera' , p : 0 , excl : false } ,
'15' : { sym : 'P' , name : 'peta' , p : 0 , excl : false } ,
'18' : { sym : 'E' , name : 'exa' , p : 0 , excl : false } ,
'21' : { sym : 'Z' , name : 'zetta' , p : 0 , excl : false } ,
'24' : { sym : 'Y' , name : 'yotta' , p : 0 , excl : false } ,
} ;
var pre2num = {
'y' : - 24 ,
'z' : - 21 ,
'a' : - 18 ,
'f' : - 15 ,
'p' : - 12 ,
'n' : - 9 ,
'<27> ' : - 6 ,
'u' : - 6 ,
'm' : - 3 ,
'c' : - 2 ,
'd' : - 1 ,
'0' : 0 ,
'D' : 1 ,
'H' : 2 ,
'k' : 3 ,
'K' : 3 ,
'M' : 6 ,
'G' : 9 ,
'T' : 12 ,
'P' : 15 ,
'E' : 18 ,
'Z' : 21 ,
'Y' : 24 ,
}
SI _defaults = {
p : 2 ,
p _max : 8 ,
p _min : 0 ,
separator : ',' ,
decimal : '.' ,
unit _sep : ' ' ,
cap _kilo : false ,
no _mu : false ,
exclude : [ 'c' , 'd' , 'D' , 'H' ] ,
}
// Apply the default precision setting above
for ( var i = - 24 ; i <= 24 ; i ++ ) {
if ( i . toString ( ) in prefixDef ) {
prefixDef [ i . toString ( ) ] [ 'p' ] = SI _defaults [ 'p' ] ;
}
}
//SI_options = Unpack_SI_options(SI_options, options_input);
var SI _options = SI _set _options ( SI _defaults , options _input , prefixDef , pre2num ) ;
//console.log(SI_options);
//console.log(options_input);
SI _options [ 'p' ] = options _input [ 'p' ] ;
//console.log('SI_options: ', SI_options);
prefixDef = SI _set _precision ( SI _options , prefixDef , pre2num ) ;
var magnitude = Math . floor ( Math . log ( value ) / Math . log ( 10 ) ) ;
if ( magnitude >= - 24 && magnitude <= 24 ) {
// Change any inbetween magnitudes to one that has a prefix assigned to it
/ *
if ( ! ( magnitude in prefixDef ) ) {
magnitude = 3 * Math . floor ( magnitude / 3 ) ;
} * /
while ( ! ( magnitude in prefixDef ) ) {
magnitude -- ;
}
// Get the precision specified for that magnitude
precision = prefixDef [ magnitude ] [ 'p' ] ;
// Divide the number by the appropriate power of 10, and return the new number and associated SI prefix
out _value = Commas ( Number ( value / Math . pow ( 10 , magnitude ) ) . toFixed ( precision ) ) ;
out _prefix = prefixDef [ magnitude ] [ 'sym' ] ;
}
else {
out _value = Commas ( value ) ;
out _prefix = '' ;
}
return ( out _value + ' ' + out _prefix + unit ) ;
}
function SI _set _options ( SI _options , options _input , prefixDef , pre2num ) {
var opt = [ 'p' , 'p_max' , 'p_min' , 'separator' , 'decimal' , 'unit_sep' , 'cap_k' , 'no_mu' ] ;
// Loop through all options, if specified by options_input, replace the default
for ( var i = 0 ; i < opt . length ; i ++ ) {
if ( opt [ i ] in options _input ) {
SI _options [ opt [ i ] ] = options _input [ opt [ i ] ] ;
}
}
var explicit _incl = [ ] ;
var arg ;
var offset ;
if ( ! ( 'exclude' in options _input ) ) {
DEBUG ( 'options_input contained no exclusions' ) ;
}
if ( typeof ( options _input [ 'exclude' ] ) == 'string' ) {
DEBUG ( '(SI_options == string) returns true' ) ;
}
else if ( typeof ( options _input [ 'exclude' ] ) == 'object' ) {
DEBUG ( '(SI_options == object) returns true' ) ;
// First pass, loops through [exclude] list provided in the input options, to check for any prefixes declared as Explicitly Included, with '!'
for ( var i = 0 ; i < options _input [ 'exclude' ] . length ; i ++ ) {
arg = options _input [ 'exclude' ] [ i ] ;
if ( isNaN ( parseInt ( arg [ 0 ] ) ) ) {
// If arg[0] is !, add arg[1] to the list of explicitly included prefixes
if ( arg [ 0 ] == '!' && pre2num [ arg [ 1 ] ] in prefixDef ) {
explicit _incl . push ( arg [ 1 ] ) ;
}
}
}
DEBUG ( 'Explicit inclusions:' , explicit _incl ) ;
// Second pass, loops through [exclude] list to look for '*', which excludes all prefixes except the ones on the explicit_incl list.
for ( var i = 0 ; i < options _input [ 'exclude' ] . length ; i ++ ) {
arg = options _input [ 'exclude' ] [ i ] ;
if ( isNaN ( parseInt ( arg [ 0 ] ) ) ) {
if ( arg [ 0 ] == '*' ) {
// If arg[0] is *, then exclude all prefixes except those explicitly included
for ( var j = - 24 ; j <= 24 ; j ++ ) {
if ( j in prefixDef ) {
if ( explicit _incl . indexOf ( prefixDef [ j ] [ 'sym' ] ) == - 1 && ! ( prefixDef [ j ] [ 'sym' ] in SI _options [ 'exclude' ] ) ) {
SI _options [ 'exclude' ] . push ( prefixDef [ j ] [ 'sym' ] ) ;
DEBUG ( 'Prefix "' + prefixDef [ j ] [ 'sym' ] + '" excluded' ) ;
DEBUG ( prefixDef [ j ] [ 'sym' ] + ' not in explicit_incl: ' + ! ( prefixDef [ j ] [ 'sym' ] in explicit _incl ) ) ;
}
}
}
break ;
}
}
}
// Third pass, loops through [exclude] list again, to expand all shortcuts like ">=G" into "[G, T, P, E ... ]"
for ( var i = 0 ; i < options _input [ 'exclude' ] . length ; i ++ ) {
arg = options _input [ 'exclude' ] [ i ] ;
if ( isNaN ( parseInt ( arg [ 0 ] ) ) ) {
if ( arg [ 0 ] == '>' ) {
if ( arg [ 1 ] == '=' ) { offset = 0 ; DEBUG ( 'Offset set to 0' ) } // If command is ">=", offset 0 to include starting position.
else { offset = 1 ; } // Otherwise, command is ">", offset 1 to not include the specified prefix
for ( var j = pre2num [ arg [ 1 + ( 1 - offset ) ] ] + offset ; j <= 24 ; j ++ ) {
DEBUG ( 'J:' , j ) ;
if ( j in prefixDef ) {
// Check to make sure it hasn't been explicitly included (present in explicit_incl list) or duplicate (present in SI[exclude])
if ( explicit _incl . indexOf ( prefixDef [ j ] [ 'sym' ] ) == - 1 && ! ( prefixDef [ j ] [ 'sym' ] in SI _options [ 'exclude' ] ) ) {
// Exclude prefixes at/above the prefix specified by arg[1]
SI _options [ 'exclude' ] . push ( prefixDef [ j ] [ 'sym' ] ) ;
}
}
}
}
else if ( arg [ 0 ] == '<' ) {
if ( arg [ 1 ] == '=' ) { offset = 0 ; } // If command is "<=", offset 0
else { offset = 1 ; } // Otherwise, command is "<", offset 1 to include the specified prefix
for ( var j = pre2num [ arg [ 1 + ( 1 - offset ) ] ] - offset ; j >= - 24 ; j -- ) {
if ( j in prefixDef ) {
// Check to make sure it hasn't been explicitly included (present in explicit_incl list) or duplicate (present in SI[exclude])
if ( explicit _incl . indexOf ( prefixDef [ j ] [ 'sym' ] ) == - 1 && ! ( prefixDef [ j ] [ 'sym' ] in SI _options [ 'exclude' ] ) ) {
// Exclude prefixes at/below the prefix specified by arg[1]
SI _options [ 'exclude' ] . push ( prefixDef [ j ] [ 'sym' ] ) ;
}
}
}
}
else {
j = pre2num [ arg [ 0 ] ] ;
if ( j in prefixDef ) {
if ( explicit _incl . indexOf ( prefixDef [ j ] [ 'sym' ] ) == - 1 && ! ( prefixDef [ j ] [ 'sym' ] in SI _options [ 'exclude' ] ) ) {
SI _options [ 'exclude' ] . push ( prefixDef [ j ] [ 'sym' ] ) ;
}
}
}
}
}
}
return SI _options ;
}
function SI _set _precision ( SI _options , prefixDef , pre2num ) {
for ( var i = 0 ; i < SI _options [ 'exclude' ] . length ; i ++ ) {
//prefixDef[pre2num[SI_options['exclude'][i]]]['excl'] = true;
// Delete prefixes that have been excluded
delete prefixDef [ pre2num [ SI _options [ 'exclude' ] [ i ] ] ] ;
}
var precision = SI _options [ 'p' ] ;
// If the precision argument is a string, then there is only 1 precision specified
if ( typeof ( precision ) == 'string' ) {
// If it's a 1-character string, then it's a pure number, which means it is set as the default for all prefixes
if ( precision . length == 1 ) {
for ( var i = - 24 ; i <= 24 ; i ++ ) {
if ( i . toString ( ) in prefixDef ) {
prefixDef [ i . toString ( ) ] [ 'p' ] = precision ;
}
}
}
// If it's a 2-character string, it could be a 2 digit number or a prefix-specific code
else if ( precision . length == 2 ) {
if ( isNaN ( parseInt ( precision [ 0 ] ) ) ) {
prefixDef [ pre2num [ precision [ 0 ] ] ] [ 'p' ] = parseInt ( precision [ 1 ] ) ;
}
}
}
else if ( typeof ( precision ) == 'object' ) {
for ( var j = 0 ; j < precision . length ; j ++ ) {
if ( precision [ j ] . length == 1 ) {
for ( var i = - 24 ; i <= 24 ; i ++ ) {
if ( i . toString ( ) in prefixDef ) {
prefixDef [ i . toString ( ) ] [ 'p' ] = parseInt ( precision [ j ] ) ;
}
}
}
// If it's a 2-character string, it could be a 2 digit number or a prefix-specific code
else if ( precision [ j ] . length == 2 ) {
if ( isNaN ( parseInt ( precision [ j ] [ 0 ] ) ) && ( pre2num [ precision [ j ] [ 0 ] ] in prefixDef ) ) {
prefixDef [ pre2num [ precision [ j ] [ 0 ] ] ] [ 'p' ] = parseInt ( precision [ j ] [ 1 ] ) ;
}
}
}
}
return prefixDef
}
async function Load _CTA _861 ( ) {
DEBUG ( 'CTA Test 6' ) ;
// Loads the timing definitions for the CTA-861 standard from a csv file
response = await fetch ( 'CTA861.txt' ) ;
CTA861 = $ . csv . toObjects ( await response . text ( ) ) ;
sleep ( 100 ) ;
DEBUG ( CTA861 ) ;
}
//Small functions
{
function Commas ( num ) {
var parts = num . toString ( ) . split ( "." ) ;
parts [ 0 ] = parts [ 0 ] . replace ( /\B(?=(\d{3})+(?!\d))/g , "," ) ;
return parts . join ( "." ) ;
}
function getPrecision ( x , watchdog ) {
// https://stackoverflow.com/questions/27082377/get-number-of-decimal-places-with-javascript
x = Math . abs ( x ) ;
watchdog = watchdog || 32 ;
var i = 0 ;
while ( x % 1 > 0 && i < watchdog ) {
i ++ ;
x = x * 10 ;
}
return i ;
}
function GCD ( a , b ) {
a = Math . abs ( a ) ;
b = Math . abs ( b ) ;
if ( b > a ) { var temp = a ; a = b ; b = temp ; }
while ( true ) {
if ( b == 0 ) return a ;
a %= b ;
if ( a == 0 ) return b ;
b %= a ;
}
}
function SameRatio ( H , V , A ) {
/* Checks if the ratio H/V is equal to the given ratio A (within a defined margin of error E) */
/* Negative signs on H, V, and A are ignored */
var E = 0.001 ; /* E is a percent error written as a decimal (i.e. E = 0.001 would give an acceptable error margin of 0.1%) */
if ( Math . abs ( ( Math . abs ( H / V ) / Math . abs ( A ) ) - 1 ) <= E ) { return true ; }
else { return false ; }
}
/ *
function parseNum ( val ) {
// Converts string to floating point if it has a decimal point, or integer if there is no decimal point. Also strips commas and spaces, and optionally applies absolute value.
if ( typeof val === "string" ) {
// val = val.replace(/[^0-9\. ]/g, ''); // Apply absolute value and ignore non-numeric characters
// val = val.replace(/[^0-9\. -]/g, ''); // Allow negative numbers and ignore non-numeric characters
//val = val.replace(/-/g, ''); // Remove minus signs
// Return NaN if...
if ( val == '' // input is blank
|| val . indexOf ( '.' ) != val . lastIndexOf ( '.' ) // input contains multiple decimal places
|| val . indexOf ( '-' ) != val . lastIndexOf ( '-' ) // input contains multiple minus signs
|| ( val . indexOf ( '-' ) != - 1 && val . indexOf ( '-' ) != 0 ) // input contains a minus sign in a position other than the first character
|| val . match ( /[^0-9.-]/g ) ) // input contains any character other than a number, decimal, minus, or space
{
return NaN ;
}
else {
// Once we have checked that the input contains no characters other than numerals, periods, and minus signs
// And that there are at most one period and one minus sign, and both are in valid positions, we can evaluate the number as either float or string.
// If for some reason either of them fail, it will still return NaN anyway
if ( val . indexOf ( '.' ) == - 1 )
return parseInt ( val ) ;
else {
return parseFloat ( val ) ;
}
}
}
else if ( Number . isNaN ( val ) ) {
return NaN ; // Check if val is literally 'NaN'
}
else if ( typeof val === "number" ) {
return val ; // NaN is recognized as a number, so this line must be after NaN is handled
}
else {
return NaN ;
}
} * /
function parseNum ( val ) {
// This function is designed to interpret strings with formatted numbers (which may contain currencies, digit grouping commas, units, etc.)
// It will return NaN if it cannot be interpreted as a valid number (i.e. no numeric characters, multiple periods or minus signs, etc.)
if ( typeof ( val ) === 'number' ) {
// If the input argument is already a number, then nothing needs to be done, simply return the input value
if ( Number . isNaN ( val ) == true ) {
// However, we do need to check that it isn't NaN, because that is identified as a number.
return NaN ;
}
else {
return val ;
}
}
else if ( typeof val === 'string' ) {
// First, remove all non-numeric characters on the outsides of the string
for ( var i = 0 ; i < val . length ; i ++ ) {
if ( ! ( i < val . length ) ) { break ; }
//DEBUG('i:', i, 'val:', val, 'val[i]:', val[i]);
// Loop through each character starting from the front
if ( ( /[^0-9.-]/g ) . test ( val [ i ] ) == true ) {
// If character is not a number, period, or minus sign, remove it
if ( i == 0 && val . length > 1 ) { val = val . slice ( 1 ) ; }
else if ( i == val . length - 1 ) { return NaN ; } // If this is the last character in the string
else if ( i > 0 ) { val = ( val . slice ( 0 , i ) ) + ( val . slice ( i + 1 ) ) ; }
i = i - 1 ; // Since a character has been removed, the next character is now at the same index
continue ;
}
else if ( ( /[-]/g ) . test ( val [ i ] ) == true ) {
// If character is a negative sign, ignore it. This is because there may still be non-number characters between the negative sign and the first digit, such as "-$1.00". The negative sign should stay but the dollar needs to be removed.
continue ;
}
else if ( ( /[.]/g ) . test ( val [ i ] ) == true ) {
// If character is a period, then following character MUST be a digit, unless it's the end of the number.
if ( i + 1 < val . length ) {
if ( ( /[0-9]/g ) . test ( val [ i + 1 ] ) == true ) {
// If the character after the period is a digit, then the first digit has been reached
break ;
}
else {
// If the string contained a period followed by a non-numeric character, it is not a number
return NaN ;
}
}
else {
// If i+1 was not < val.length, then the period is the last character in the string, which implicitly means the string contains no numeric characters.
return NaN ;
}
}
else if ( ( /[0-9]/g ) . test ( val [ i ] ) == true ) {
// If character was a numeric character, we have successfully stripped any leading characters and reached the start of the number.
break ;
}
}
// Now do a similar starting from the backside, to strip any trailing characters (such as units of measurement)
for ( var i = ( val . length - 1 ) ; i >= 0 ; i -- ) {
if ( ( /[^0-9]/g ) . test ( val [ i ] ) == true ) {
// No need to modify iterator for this direction
// Since we are dealing with the end of the number, minus signs are no longer part of the number. Periods at the end are valid, but superfluous
// Since we are only checking the characters after the digits have ended, minus signs are not invalid, as they may be part of a unit of measurement. Therefore, they are simply removed, rather than returning NaN.
if ( val . length == 1 ) { return NaN ; }
else { val = val . slice ( 0 , i ) ; }
continue ;
}
else if ( ( /[0-9]/g ) . test ( val [ i ] ) == true ) {
// If character is a numeric character, we have reached the back end of the number and successfully stripped any trailing characters.
break ;
}
}
// We now strip any commas and whitespace throughout the string
val = val . replace ( /[, ]/g , '' ) ;
// If, after removing leading and trailing units, whitespace, and commas, there are any non-numeric characters (i.e. in the middle of the string after the numbers start), it is not a valid number
if ( ( /[^0-9.-]/g ) . test ( val ) == true ) {
return NaN ;
}
// Now that the string only contains numeric characters, minus signs, and periods, we must check if there are any invalid usages of the periods and signs, such as multiple periods/signs, or a minus sign anywhere other than the first character.
if ( val == '' // string is empty
|| val . indexOf ( '.' ) != val . lastIndexOf ( '.' ) // string contains multiple decimal point
|| val . indexOf ( '-' ) != val . lastIndexOf ( '-' ) // string contains multiple minus signs
|| ( val . indexOf ( '-' ) != - 1 && val . indexOf ( '-' ) != 0 ) ) // input contains a minus sign and it isn't the first character
{
return NaN ;
}
// Finished; string now only contains numbers, up to one period, and up to one minus sign as the first character of the string.
// If for some reason the parseFloat fails, the function will return NaN, so the output is still consistent with any other failed condition.
return parseFloat ( val ) ;
}
}
function isNum ( num ) {
// Returns false if input is not a number, including a blank string or NaN
// Can accept arrays, and returns true only if all elements would return true individually
if ( Array . isArray ( num ) == true ) {
for ( a = 0 ; a < num . length ; a ++ ) {
if ( Number . isNaN ( parseFloat ( num [ a ] ) ) == true ) {
return false ;
}
return true ;
}
}
else {
return ! Number . isNaN ( parseFloat ( num ) ) ;
}
}
function isPositive ( num ) {
if ( Array . isArray ( num ) == true ) {
for ( a = 0 ; a < num . length ; a ++ ) {
if ( Number . isNaN ( parseNum ( num [ a ] ) ) == true )
return false ;
else if ( num [ a ] <= 0 )
return false ;
}
return true ;
}
else {
if ( Number . isNaN ( parseNum ( num ) ) == true )
return false ;
else if ( num > 0 )
return true ;
else {
return false ;
}
}
}
function isPositiveZ ( num ) {
// Returns true if input is positive or zero. Can recognize formatted numbers (i.e. "1,234,567 Gbit/s" or "-$1,234 USD") as allowed by parseNum
// Combine with isNum() to allow only true (non-formatted) numbers and strings (i.e. if (isNum(x) && isPositiveZ(x))
/ *
True :
isPositiveZ ( 1 )
isPositiveZ ( '1' )
isPositiveZ ( 1.1 )
isPositiveZ ( '1.1' )
isPositiveZ ( '1.' ) Number string with stray decimal
isPositiveZ ( 0 ) or ( '0' )
isPositiveZ ( Infinity )
isPositiveZ ( [ 1 , '2' , 3.3 ] ) true iff all elements are non - negative
False :
isPositiveZ ( - 1 )
isPositiveZ ( '-1' )
isPositiveZ ( - 1.1 )
isPositiveZ ( '-1.1' )
isPositiveZ ( '1 ' ) Number with whitespace
isPositiveZ ( '' ) Empty String
isPositiveZ ( 'abc' )
isPositiveZ ( NaN )
isPositiveZ ( [ - 1 , '2' , 3.3 ] ) If any element is negative
* /
if ( Array . isArray ( num ) == true ) {
for ( a = 0 ; a < num . length ; a ++ ) {
if ( Number . isNaN ( parseNum ( num [ a ] ) ) == true )
return false ;
else if ( num [ a ] < 0 )
return false ;
}
return true ;
}
else {
if ( Number . isNaN ( parseNum ( num ) ) == true )
return false ;
else if ( num >= 0 )
return true ;
else {
return false ;
}
}
}
function isInt ( num ) {
/ *
True :
isInt ( 1 )
isInt ( '1' )
isInt ( - 1 )
isInt ( '-1' )
isInt ( 0 )
isInt ( [ 1 , - 2 , '3' ] )
isInt ( 1 , 'a' ) - > true ( be careful to enter multiple arguments as an array , otherwise it will only evaluate the first argument , as seen here )
False :
isInt ( 1.1 )
isInt ( '1 ' )
isInt ( '1 2' )
isInt ( 'abc' )
isInt ( 'abc123' )
isInt ( '' )
isInt ( NaN )
isInt ( Infinity )
isInt ( [ 1 , 2 , 3.3 ] ) Returns false if any of the array elements are not an integer
* /
if ( Array . isArray ( num ) == true ) {
for ( a = 0 ; a < num . length ; a ++ ) {
if ( Number . isInteger ( parseNum ( num [ a ] ) ) == false ) {
return false ;
}
}
return true ;
}
else {
num = parseNum ( num ) ;
return Number . isInteger ( num ) ;
}
}
function isFloat ( num ) {
/ *
True :
isFloat ( 1.1 )
isFloat ( '1.1' )
isFloat ( - 1.1 )
isFloat ( '-1.1' )
isFloat ( '.1' )
isFloat ( '-.1' )
isFloat ( [ 1.1 , - 1.2 , '1.3' ] )
isFloat ( Infinity )
isFloat ( [ 1.1 , 'a' ] ) - > true ( be careful to enter multiple arguments as an array , otherwise it will only evaluate the first argument , as seen here )
False :
isFloat ( 1.0 )
isFloat ( '1.0' )
isFloat ( 0 )
isFloat ( 1 )
isFloat ( '1.1.1' )
isFloat ( '' )
isFloat ( 'a' )
isFloat ( '1.1a' )
isFloat ( [ 1.1 , 1.2 , 3 ] )
isFloat ( NaN )
* /
if ( Array . isArray ( num ) == true ) {
for ( a = 0 ; a < num . length ; a ++ ) {
num [ a ] = parseNum ( num [ a ] ) ;
if ( Number . isInteger ( num [ a ] ) == true || Number . isNaN ( num [ a ] ) == true ) {
return false ;
}
}
return true ;
}
else {
num = parseNum ( num ) ;
return ! ( Number . isInteger ( num ) || Number . isNaN ( num ) ) ;
}
}
}
window . onpageshow = function ( ) {
generate _table ( 'Interface Support' , - 1 ) ;
$ ( '#INPUT_HRES' ) [ 0 ] . onchange ( ) ;
$ ( '#INPUT_VRES' ) [ 0 ] . onchange ( ) ;
$ ( '#INPUT_F' ) [ 0 ] . onchange ( ) ;
$ ( '#COLOR_DEPTH_FORM' ) [ 0 ] . onchange ( ) ;
$ ( '#PIXEL_FORMAT_FORM' ) [ 0 ] . onchange ( ) ;
$ ( '#COMPRESSION_FORM' ) [ 0 ] . onchange ( ) ;
$ ( '#SCAN_FORM' ) [ 0 ] . onchange ( ) ;
$ ( '#MARGINS_FORM' ) [ 0 ] . onchange ( ) ;
$ ( '#TIMING_DROP' ) [ 0 ] . onchange ( ) ;
calcMain ( ) ;
}