diff --git a/bandwidth/CTA861.txt b/bandwidth/CTA861.txt
new file mode 100644
index 0000000..739ab9c
--- /dev/null
+++ b/bandwidth/CTA861.txt
@@ -0,0 +1,163 @@
+Rev,VIC,Alt,H,V,Scan,H tot,H bl,V tot,V bl,H freq (kHz),V freq (Hz),Px CLK,HF,HS,HB,VF,VS,VB,Hpol,Vpol
+A,1,,640,480,p,800,160,525,45,31.469,59.94,25.175,16,96,48,10,2,33,n,n
+A,1,,640,480,p,800,160,525,45,31.5,60,25.2,16,96,48,10,2,33,n,n
+A,2,3,720,480,p,858,138,525,45,31.469,59.94,27,16,62,60,8,6,31,n,n
+A,2,3,720,480,p,858,138,525,45,31.5,60,27.027,16,62,60,8,6,31,p,p
+A,4,69,1280,720,p,1650,370,750,30,44.955,59.939,74.175,110,40,220,5,5,20,p,p
+A,4,69,1280,720,p,1650,370,750,30,45,60,74.25,110,40,220,5,5,20,p,p
+A,5,,1920,1080,i,2200,280,1125,22.5,33.716,59.939,74.175,88,44,148,2,5,15,p,p
+A,5,,1920,1080,i,2200,280,1125,22.5,33.75,60,74.25,88,44,148,2,5,15,p,p
+A,6,7,1440,480,i,1716,276,525,22.5,15.734,59.94,27,38,124,114,4,3,15,n,n
+A,6,7,1440,480,i,1716,276,525,22.5,15.75,60,27.027,38,124,114,4,3,15,n,n
+B,8,9,1440,240,p,1716,276,262,22,15.734,60.054,27,38,124,114,4,3,15,n,n
+B,8,9,1440,240,p,1716,276,263,23,15.734,59.826,27,38,124,114,5,3,15,n,n
+B,10,11,2880,480,i,3432,552,525,22.5,15.734,59.94,54,76,248,228,4,3,15,n,n
+B,12,13,2880,240,p,3432,552,525,22,15.734,60.054,54,76,248,228,4,3,15,n,n
+B,12,13,2880,240,p,3432,552,525,23,15.734,59.826,54,76,248,228,5,3,15,n,n
+B,14,15,1440,480,p,1716,276,525,45,31.469,59.94,54,32,124,120,9,6,30,n,n
+B,14,15,1440,480,p,1716,276,525,45,31.5,60,54.054,32,124,120,9,6,30,n,n
+B,16,76,1920,1080,p,2200,280,1125,45,67.432,59.939,148.35,88,44,148,4,5,36,p,p
+B,16,76,1920,1080,p,2200,280,1125,45,67.5,60,148.5,88,44,148,4,5,36,p,p
+A,17,18,720,576,p,864,144,625,49,31.25,50,27,12,64,68,5,5,39,n,n
+A,19,68,1280,720,p,1980,700,750,30,37.5,50,74.25,440,40,220,5,5,20,p,p
+A,20,,1920,1080,i,2640,720,1125,22.5,28.125,50,74.25,528,44,148,2,5,15,p,p
+A,21,22,1440,576,i,1728,288,625,24.5,15.625,50,27,24,126,138,2,3,19,n,n
+B,23,24,1440,288,p,1728,288,312,24,15.625,50.08,27,24,126,138,2,3,19,n,n
+B,23,24,1440,288,p,1728,288,313,25,15.625,49.92,27,24,126,138,3,3,19,n,n
+B,23,24,1440,288,p,1728,288,314,26,15.625,49.761,27,24,126,138,4,3,19,n,n
+B,25,26,2880,576,i,3456,576,625,24.5,15.625,50,54,48,252,276,2,3,19,n,n
+B,27,28,2880,288,p,3456,576,312,24,15.625,50.08,54,48,252,276,2,3,19,n,n
+B,27,28,2880,288,p,3456,576,313,25,15.625,49.92,54,48,252,276,3,3,19,n,n
+B,27,28,2880,288,p,3456,576,314,26,15.625,49.761,54,48,252,276,4,3,19,n,n
+B,29,30,1440,576,p,1728,288,625,49,31.25,50,54,24,128,136,5,5,39,n,n
+B,31,75,1920,1080,p,2640,720,1125,45,56.25,50,148.5,528,44,148,4,5,36,p,p
+B,32,72,1920,1080,p,2750,830,1125,45,26.973,23.976,74.175,638,44,148,4,5,36,p,p
+B,32,72,1920,1080,p,2750,830,1125,45,27,24,74.25,638,44,148,4,5,36,p,p
+B,33,73,1920,1080,p,2640,720,1125,45,28.125,25,74.25,528,44,148,4,5,36,p,p
+B,34,74,1920,1080,p,2200,280,1125,45,33.716,29.97,74.175,88,44,148,4,5,36,p,p
+B,34,74,1920,1080,p,2200,280,1125,45,33.75,30,74.25,88,44,148,4,5,36,p,p
+C,35,36,2880,480,p,3432,552,525,45,31.5,60,108.108,64,248,240,9,6,30,n,n
+C,37,38,2880,576,p,3456,576,625,49,31.25,50,108,48,256,272,5,5,39,n,n
+C,39,,1920,1080,i,2304,384,1250,85,31.25,50,72,32,168,184,23,5,57,p,n
+C,40,,1920,1080,i,2640,720,1125,22.5,56.25,100,148.5,528,44,148,2,5,15,p,p
+C,41,,1280,720,p,1980,700,750,30,75,100,148.5,440,40,220,5,5,20,p,p
+C,42,43,720,576,p,864,144,625,49,62.5,100,54,12,64,68,5,5,39,n,n
+C,44,45,1440,576,i,1728,288,625,24.5,31.25,100,54,24,126,138,2,3,19,n,n
+C,46,,1920,1080,i,2200,280,1125,22.5,67.432,119.88,148.352,88,44,148,2,5,15,p,p
+C,46,,1920,1080,i,2200,280,1125,22.5,67.5,120,148.5,88,44,148,2,5,15,p,p
+C,47,,1280,720,p,1650,370,750,30,89.909,119.88,148.352,110,40,220,5,5,20,p,p
+C,47,,1280,720,p,1650,370,750,30,90,120,148.5,110,40,220,5,5,20,p,p
+C,48,49,720,480,p,858,138,525,45,62.937,119.88,54,16,62,60,9,6,30,n,n
+C,48,49,720,480,p,858,138,525,45,63,120,54.054,16,62,60,9,6,30,n,n
+C,50,51,1440,480,i,1716,276,525,22.5,31.469,119.88,54,38,124,114,4,3,15,n,n
+C,50,51,1440,480,i,1716,276,525,22.5,31.5,120,54.054,38,124,114,4,3,15,p,p
+C,52,53,720,576,p,864,144,625,49,125,200,108,12,64,68,5,5,39,n,n
+C,54,55,1440,576,i,1728,288,625,24.5,62.5,200,108,24,126,138,2,3,19,n,n
+C,56,57,720,480,p,858,138,525,45,125.874,239.76,108,16,62,60,9,6,30,n,n
+C,56,57,720,480,p,858,138,525,45,126,240,108.108,16,62,60,9,6,30,n,n
+C,58,59,1440,480,i,1716,276,525,22.5,62.937,239.76,108,38,124,114,4,3,15,n,n
+C,58,59,1440,480,i,1716,276,525,22.5,63,240,108.108,38,124,114,4,3,15,n,n
+E,60,65,1280,720,p,3300,2020,750,30,17.982,23.976,59.341,1760,40,220,5,5,20,p,p
+E,60,65,1280,720,p,3300,2020,750,30,18,24,59.4,1760,40,220,5,5,20,p,p
+E,61,66,1280,720,p,3960,2680,750,30,18.75,25,74.25,2420,40,220,5,5,20,p,p
+E,62,67,1280,720,p,3300,2020,750,30,22.478,29.97,74.176,1760,40,220,5,5,20,p,p
+E,62,67,1280,720,p,3300,2020,750,30,22.5,30,74.25,1760,40,220,5,5,20,p,p
+E,63,78,1920,1080,p,2200,280,1125,45,134.865,119.88,296.703,88,44,148,4,5,36,p,p
+E,63,78,1920,1080,p,2200,280,1125,45,135,120,297,88,44,148,4,5,36,p,p
+E,64,77,1920,1080,p,2640,720,1125,45,112.5,100,297,528,44,148,4,5,36,p,p
+F,79,,1680,720,p,3300,1620,750,30,17.982,23.976,59.341,1360,40,220,5,5,20,p,p
+F,79,,1680,720,p,3300,1620,750,30,17.982,23.976,59.341,1360,40,220,5,5,20,p,p
+F,79,,1680,720,p,3300,1620,750,30,18,24,59.4,1360,40,220,5,5,20,p,p
+F,80,,1680,720,p,3168,1488,750,30,18.75,25,59.4,1228,40,220,5,5,20,p,p
+F,81,,1680,720,p,2640,960,750,30,22.478,29.970,59.341,700,40,220,5,5,20,p,p
+F,81,,1680,720,p,2640,960,750,30,22.5,30,59.4,700,40,220,5,5,20,p,p
+F,82,,1680,720,p,2200,520,750,30,37.5,50,82.5,260,40,220,5,5,20,p,p
+F,83,,1680,720,p,2200,520,750,30,44.955,59.940,98.901,260,40,220,5,5,20,p,p
+F,83,,1680,720,p,2200,520,750,30,45,60,99,260,40,220,5,5,20,p,p
+F,84,,1680,720,p,2000,320,825,105,82.5,100,165,60,40,220,5,5,95,p,p
+F,85,,1680,720,p,2000,320,825,105,98.901,119.880,197.802,60,40,220,5,5,95,p,p
+F,85,,1680,720,p,2000,320,825,105,99,120,198,60,40,220,5,5,95,p,p
+F,86,,2560,1080,p,3750,1190,1100,20,26.374,23.976,98.901,998,44,148,4,5,11,p,p
+F,86,,2560,1080,p,3750,1190,1100,20,26.4,24,99,998,44,148,4,5,11,p,p
+F,87,,2560,1080,p,3200,640,1125,45,28.125,25,90,448,44,148,4,5,36,p,p
+F,88,,2560,1080,p,3520,960,1125,45,33.716,29.970,118.681,768,44,148,4,5,36,p,p
+F,88,,2560,1080,p,3520,960,1125,45,33.75,30,118.8,768,44,148,4,5,36,p,p
+F,89,,2560,1080,p,3300,740,1125,45,56.25,50,185.625,548,44,148,4,5,36,p,p
+F,90,,2560,1080,p,3000,440,1100,20,65.934,59.940,197.802,248,44,148,4,5,11,p,p
+F,90,,2560,1080,p,3000,440,1100,20,66,60,198,248,44,148,4,5,11,p,p
+F,91,,2560,1080,p,2970,410,1250,170,125,100,371.25,218,44,148,4,5,161,p,p
+F,92,,2560,1080,p,3300,740,1250,170,149.850,119.880,494.505,548,44,148,4,5,161,p,p
+F,92,,2560,1080,p,3300,740,1250,170,150,120,495,548,44,148,4,5,161,p,p
+F,93,103,3840,2160,p,5500,1660,2250,90,53.946,23.976,296.703,1276,88,296,8,10,72,p,p
+F,93,103,3840,2160,p,5500,1660,2250,90,54,24,297,1276,88,296,8,10,72,p,p
+F,94,104,3840,2160,p,5280,1440,2250,90,56.25,25,297,1056,88,296,8,10,72,p,p
+F,95,105,3840,2160,p,4400,560,2250,90,67.433,29.970,296.703,176,88,296,8,10,72,p,p
+F,95,105,3840,2160,p,4400,560,2250,90,67.5,30,297,176,88,296,8,10,72,p,p
+F,96,106,3840,2160,p,5280,1440,2250,90,112.5,50,594,1056,88,296,8,10,72,p,p
+F,97,107,3840,2160,p,4400,560,2250,90,134.865,59.940,593.407,176,88,296,8,10,72,p,p
+F,97,107,3840,2160,p,4400,560,2250,90,135,60,594,176,88,296,8,10,72,p,p
+F,98,,4096,2160,p,5500,1404,2250,90,53.946,23.976,296.703,1020,88,296,8,10,72,p,p
+F,98,,4096,2160,p,5500,1404,2250,90,54,24,297,1020,88,296,8,10,72,p,p
+F,99,,4096,2160,p,5280,1184,2250,90,56.25,25,297,968,88,128,8,10,72,p,p
+F,100,,4096,2160,p,4400,304,2250,90,67.433,29.970,296.703,88,88,128,8,10,72,p,p
+F,100,,4096,2160,p,4400,304,2250,90,67.5,30,297,88,88,128,8,10,72,p,p
+F,101,,4096,2160,p,5280,1184,2250,90,112.5,50,594,968,88,128,8,10,72,p,p
+F,102,,4096,2160,p,4400,304,2250,90,134.865,59.940,593.407,88,88,128,8,10,72,p,p
+F,102,,4096,2160,p,4400,304,2250,90,135,60,594,88,88,128,8,10,72,p,p
+G,108,109,1280,720,p,2500,1220,750,30,35.964,47.952,89.910,960,40,220,5,5,20,p,p
+G,108,109,1280,720,p,2500,1220,750,30,36,48,90,960,40,220,5,5,20,p,p
+G,110,,1680,720,p,2750,1070,750,30,35.964,47.952,98.901,810,40,220,5,5,20,p,p
+G,110,,1680,720,p,2750,1070,750,30,36,48,99,810,40,220,5,5,20,p,p
+G,111,112,1920,1080,p,2750,830,1125,45,53.946,47.952,148.352,638,44,148,4,5,36,p,p
+G,111,112,1920,1080,p,2750,830,1125,45,54,48,148.5,638,44,148,4,5,36,p,p
+G,113,,2560,1080,p,3750,1190,1100,20,52.747,47.952,197.802,998,44,148,4,5,11,p,p
+G,113,,2560,1080,p,3750,1190,1100,20,52.8,48,198,998,44,148,4,5,11,p,p
+G,114,116,3840,2160,p,5500,1660,2250,90,107.892,47.952,593.407,1276,88,296,8,10,72,p,p
+G,114,116,3840,2160,p,5500,1660,2250,90,108,48,594,1276,88,296,8,10,72,p,p
+G,115,,4096,2160,p,5500,1404,2250,90,107.892,47.952,593.407,1020,88,296,8,10,72,p,p
+G,115,,4096,2160,p,5500,1404,2250,90,108,48,594,1020,88,296,8,10,72,p,p
+G,117,119,3840,2160,p,5280,1440,2250,90,225,100,1188,1056,88,296,8,10,72,p,p
+G,118,120,3840,2160,p,4400,560,2250,90,269.730,119.880,1186.813,176,88,296,8,10,72,p,p
+G,118,120,3840,2160,p,4400,560,2250,90,270,120,1188,176,88,296,8,10,72,p,p
+G,121,,5120,2160,p,7500,2380,2200,40,52.747,23.976,395.604,1996,88,296,8,10,22,p,p
+G,121,,5120,2160,p,7500,2380,2200,40,52.8,24,396,1996,88,296,8,10,22,p,p
+G,122,,5120,2160,p,7200,2080,2200,40,55,25,396,1696,88,296,8,10,22,p,p
+G,123,,5120,2160,p,6000,880,2200,40,65.934,29.970,395.604,664,88,128,8,10,22,p,p
+G,123,,5120,2160,p,6000,880,2200,40,66,30,396,664,88,128,8,10,22,p,p
+G,124,,5120,2160,p,6250,1130,2475,315,118.681,47.952,741.758,746,88,296,8,10,297,p,p
+G,124,,5120,2160,p,6250,1130,2475,315,118.8,48,742.5,746,88,296,8,10,297,p,p
+G,125,,5120,2160,p,6600,1480,2250,90,112.5,50,742.5,1096,88,296,8,10,72,p,p
+G,126,,5120,2160,p,5500,380,2250,90,134.865,59.940,741.758,164,88,128,8,10,72,p,p
+G,126,,5120,2160,p,5500,380,2250,90,135,60,742.5,164,88,128,8,10,72,p,p
+G,127,,5120,2160,p,6600,1480,2250,90,225,100,1485,1096,88,296,8,10,72,p,p
+G,193,,5120,2160,p,5500,380,2250,90,269.730,119.880,1483.516,164,88,128,8,10,72,p,p
+G,193,,5120,2160,p,5500,380,2250,90,270,120,1485,164,88,128,8,10,72,p,p
+G,194,202,7680,4320,p,11000,3320,4500,180,107.892,23.976,1186.813,2552,176,592,16,20,144,p,p
+G,194,202,7680,4320,p,11000,3320,4500,180,108,24,1188,2552,176,592,16,20,144,p,p
+G,195,203,7680,4320,p,10800,3120,4400,80,110,25,1188,2352,176,592,16,20,44,p,p
+G,196,204,7680,4320,p,9000,1320,4400,80,131.868,29.970,1186.813,552,176,592,16,20,44,p,p
+G,196,204,7680,4320,p,9000,1320,4400,80,132,30,1188,552,176,592,16,20,44,p,p
+G,197,205,7680,4320,p,11000,3320,4500,180,215.784,47.952,2373.626,2552,176,592,16,20,144,p,p
+G,197,205,7680,4320,p,11000,3320,4500,180,216,48,2376,2552,176,592,16,20,144,p,p
+G,198,206,7680,4320,p,10800,3120,4400,80,220,50,2376,2352,176,592,16,20,44,p,p
+G,199,207,7680,4320,p,9000,1320,4400,80,263.736,59.940,2373.626,552,176,592,16,20,44,p,p
+G,199,207,7680,4320,p,9000,1320,4400,80,264,60,2376,552,176,592,16,20,44,p,p
+G,200,208,7680,4320,p,10560,2880,4500,180,450,100,4752,2112,176,592,16,20,144,p,p
+G,201,209,7680,4320,p,8800,1120,4500,180,449.550,119.880,4747.253,352,176,592,16,20,144,p,p
+G,201,209,7680,4320,p,8800,1120,4500,180,450,120,4752,352,176,592,16,20,144,p,p
+G,210,,10240,4320,p,12500,2260,4950,630,118.681,23.976,1483.516,1492,176,592,16,20,594,p,p
+G,210,,10240,4320,p,12500,2260,4950,630,118.8,24,1485,1492,176,592,16,20,594,p,p
+G,211,,10240,4320,p,13500,3260,4400,80,110,25,1485,2492,176,592,16,20,44,p,p
+G,212,,10240,4320,p,11000,760,4500,180,134.865,29.970,1483.516,288,176,296,16,20,144,p,p
+G,212,,10240,4320,p,11000,760,4500,180,135,30,1485,288,176,296,16,20,144,p,p
+G,213,,10240,4320,p,12500,2260,4950,630,237.363,47.952,0.000,1492,176,592,16,20,594,p,p
+G,213,,10240,4320,p,12500,2260,4950,630,237.6,48,,1492,176,592,16,20,594,p,p
+G,214,,10240,4320,p,13500,3260,4400,80,220,50,2970,2492,176,592,16,20,44,p,p
+G,215,,10240,4320,p,11000,760,4500,180,269.730,59.940,2967.033,288,176,296,16,20,144,p,p
+G,215,,10240,4320,p,11000,760,4500,180,270,60,2970,288,176,296,16,20,144,p,p
+G,216,,10240,4320,p,13200,2960,4500,180,450,100,5940,2192,176,592,16,20,144,p,p
+G,217,,10240,4320,p,11000,760,4500,180,539.461,119.880,5934.066,288,176,296,16,20,144,p,p
+G,217,,10240,4320,p,11000,760,4500,180,540,120,5940,288,176,296,16,20,144,p,p
+G,218,,4096,2160,p,5280,1184,2250,90,225,100,1188,800,88,296,8,10,72,p,p
+G,219,,4096,2160,p,4400,304,2250,90,269.730,119.880,1186.813,88,88,128,8,10,72,p,p
+G,219,,4096,2160,p,4400,304,2250,90,270,120,1188,88,88,128,8,10,72,p,p
\ No newline at end of file
diff --git a/bandwidth/DebugControl.js b/bandwidth/DebugControl.js
new file mode 100644
index 0000000..14450e7
--- /dev/null
+++ b/bandwidth/DebugControl.js
@@ -0,0 +1,40 @@
+DebugSettings = {};
+
+function DEBUG(text) {
+ Fn = DEBUG.caller.name;
+ proceed_with_msg = false;
+
+ if ('all' in DebugSettings) {
+ if (!DebugSettings['all']) {
+ return;
+ } // if All == false, always skip
+ else if (DebugSettings['all']) {
+ //printDebug(Fn, arguments);
+ proceed_with_msg = true;
+ }
+ }
+ else if (Fn in DebugSettings) {
+ if (DebugSettings[Fn] == false) {
+ //printDebug(Fn, arguments)
+ proceed_with_msg = false;
+ }
+ }
+ else {
+ proceed_with_msg = true;
+ }
+
+ if (proceed_with_msg == true) {
+ str = 'console.log("[" + Fn + "()] ::"';
+ for (var i = 0; i < arguments.length; i++) {
+ str += ', arguments[' + i + ']';
+ }
+ str += ');';
+ eval(str);
+ }
+
+ return;
+}
+
+function DebugConfig(settings) {
+ DebugSettings = settings;
+}
\ No newline at end of file
diff --git a/bandwidth/bandwidth.js b/bandwidth/bandwidth.js
new file mode 100644
index 0000000..83841ea
--- /dev/null
+++ b/bandwidth/bandwidth.js
@@ -0,0 +1,1453 @@
+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 += ('
Interface Support
');
+ contents += ('
');
+ contents += ('
% Usage
');
+ contents += ('
Interface
');
+ contents += ('
Encoding
');
+ contents += ('
Maximum
');
+ contents += ('
Pixel Clock
Datarate
Bandwidth
');
+
+ 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 += ('
');
+ }
+ else {
+ contents += ('
' + saturation.toFixed(1) + '%
');
+ }
+
+ contents += ('
' + name + '
' + encoding + '
' + SI(pixel_clock, 'Hz', {p: ['M0', 'G3']}) + '
' + SI(datarate, 'bit/s', 2) + '
' + SI(bandwidth, 'bit/s', 2) + '
');
+ }
+
+ 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 CTA() {
+
+}
+
+
+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) Vblank');
+ }
+ 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('Vblank');
+ }
+ 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()}; }
+
+ /* Valid options:
+ 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 "µ" 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
+ '= -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
+}
+
+
+function Load_CTA_861() {
+ // Loads the timing definitions for the CTA-861 standard from a csv file
+ fetch('CTA861.txt')
+ .then(
+ function(response) {
+ if (response.status !== 200 && response.status !== 0) {
+ console.log('Response code:', response.status);
+ return;
+ }
+ CTA861 = data;
+ }
+ ).then(
+ function() {
+ 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();
+}
\ No newline at end of file
diff --git a/bandwidth/index.html b/bandwidth/index.html
new file mode 100644
index 0000000..90c2f4a
--- /dev/null
+++ b/bandwidth/index.html
@@ -0,0 +1,435 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Bandwidth Calculator
+
+
+
+
+
+
The timing parameters used for these calculations are defined by the CTA-861 standard. The definitions for this format are listed under Video Identification Code (VIC) #98, first defined in CTA-861-F (formerly CEA-861-F).
+
+
+
+
+
+
+
+
+
+
›
+
+ Interface Support
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bandwidth/jquery.csv.js b/bandwidth/jquery.csv.js
new file mode 100644
index 0000000..9f8e5f7
--- /dev/null
+++ b/bandwidth/jquery.csv.js
@@ -0,0 +1,980 @@
+/**
+ * jQuery-csv (jQuery Plugin)
+ *
+ * This document is licensed as free software under the terms of the
+ * MIT License: http://www.opensource.org/licenses/mit-license.php
+ *
+ * Acknowledgements:
+ * The original design and influence to implement this library as a jquery
+ * plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
+ * If you're looking to use native JSON.Stringify but want additional backwards
+ * compatibility for browsers that don't support it, I highly recommend you
+ * check it out.
+ *
+ * A special thanks goes out to rwk@acm.org for providing a lot of valuable
+ * feedback to the project including the core for the new FSM
+ * (Finite State Machine) parsers. If you're looking for a stable TSV parser
+ * be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
+
+ * For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
+ * USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
+ * library you are accepting responsibility if it breaks your code.
+ *
+ * Legal jargon aside, I will do my best to provide a useful and stable core
+ * that can effectively be built on.
+ *
+ * Copyrighted 2012 by Evan Plaice.
+ */
+
+RegExp.escape = function (s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+};
+
+(function () {
+ 'use strict';
+
+ var $;
+
+ // to keep backwards compatibility
+ if (typeof jQuery !== 'undefined' && jQuery) {
+ $ = jQuery;
+ } else {
+ $ = {};
+ }
+
+ /**
+ * jQuery.csv.defaults
+ * Encapsulates the method paramater defaults for the CSV plugin module.
+ */
+
+ $.csv = {
+ defaults: {
+ separator: ',',
+ delimiter: '"',
+ headers: true
+ },
+
+ hooks: {
+ castToScalar: function (value, state) {
+ var hasDot = /\./;
+ if (isNaN(value)) {
+ return value;
+ } else {
+ if (hasDot.test(value)) {
+ return parseFloat(value);
+ } else {
+ var integer = parseInt(value);
+ if (isNaN(integer)) {
+ return null;
+ } else {
+ return integer;
+ }
+ }
+ }
+ }
+ },
+
+ parsers: {
+ parse: function (csv, options) {
+ // cache settings
+ var separator = options.separator;
+ var delimiter = options.delimiter;
+
+ // set initial state if it's missing
+ if (!options.state.rowNum) {
+ options.state.rowNum = 1;
+ }
+ if (!options.state.colNum) {
+ options.state.colNum = 1;
+ }
+
+ // clear initial state
+ var data = [];
+ var entry = [];
+ var state = 0;
+ var value = '';
+ var exit = false;
+
+ function endOfEntry () {
+ // reset the state
+ state = 0;
+ value = '';
+
+ // if 'start' hasn't been met, don't output
+ if (options.start && options.state.rowNum < options.start) {
+ // update global state
+ entry = [];
+ options.state.rowNum++;
+ options.state.colNum = 1;
+ return;
+ }
+
+ if (options.onParseEntry === undefined) {
+ // onParseEntry hook not set
+ data.push(entry);
+ } else {
+ var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
+ // false skips the row, configurable through a hook
+ if (hookVal !== false) {
+ data.push(hookVal);
+ }
+ }
+ // console.log('entry:' + entry);
+
+ // cleanup
+ entry = [];
+
+ // if 'end' is met, stop parsing
+ if (options.end && options.state.rowNum >= options.end) {
+ exit = true;
+ }
+
+ // update global state
+ options.state.rowNum++;
+ options.state.colNum = 1;
+ }
+
+ function endOfValue () {
+ if (options.onParseValue === undefined) {
+ // onParseValue hook not set
+ entry.push(value);
+ } else {
+ var hook = options.onParseValue(value, options.state); // onParseValue Hook
+ // false skips the row, configurable through a hook
+ if (hook !== false) {
+ entry.push(hook);
+ }
+ }
+ // console.log('value:' + value);
+ // reset the state
+ value = '';
+ state = 0;
+ // update global state
+ options.state.colNum++;
+ }
+
+ // escape regex-specific control chars
+ var escSeparator = RegExp.escape(separator);
+ var escDelimiter = RegExp.escape(delimiter);
+
+ // compile the regEx str using the custom delimiter/separator
+ var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
+ var matchSrc = match.source;
+ matchSrc = matchSrc.replace(/S/g, escSeparator);
+ matchSrc = matchSrc.replace(/D/g, escDelimiter);
+ match = new RegExp(matchSrc, 'gm');
+
+ // put on your fancy pants...
+ // process control chars individually, use look-ahead on non-control chars
+ csv.replace(match, function (m0) {
+ if (exit) {
+ return;
+ }
+ switch (state) {
+ // the start of a value
+ case 0:
+ // null last value
+ if (m0 === separator) {
+ value += '';
+ endOfValue();
+ break;
+ }
+ // opening delimiter
+ if (m0 === delimiter) {
+ state = 1;
+ break;
+ }
+ // null last value
+ if (/^(\r\n|\n|\r)$/.test(m0)) {
+ endOfValue();
+ endOfEntry();
+ break;
+ }
+ // un-delimited value
+ value += m0;
+ state = 3;
+ break;
+
+ // delimited input
+ case 1:
+ // second delimiter? check further
+ if (m0 === delimiter) {
+ state = 2;
+ break;
+ }
+ // delimited data
+ value += m0;
+ state = 1;
+ break;
+
+ // delimiter found in delimited input
+ case 2:
+ // escaped delimiter?
+ if (m0 === delimiter) {
+ value += m0;
+ state = 1;
+ break;
+ }
+ // null value
+ if (m0 === separator) {
+ endOfValue();
+ break;
+ }
+ // end of entry
+ if (/^(\r\n|\n|\r)$/.test(m0)) {
+ endOfValue();
+ endOfEntry();
+ break;
+ }
+ // broken paser?
+ throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+
+ // un-delimited input
+ case 3:
+ // null last value
+ if (m0 === separator) {
+ endOfValue();
+ break;
+ }
+ // end of entry
+ if (/^(\r\n|\n|\r)$/.test(m0)) {
+ endOfValue();
+ endOfEntry();
+ break;
+ }
+ if (m0 === delimiter) {
+ // non-compliant data
+ throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+ }
+ // broken parser?
+ throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+ default:
+ // shenanigans
+ throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+ }
+ // console.log('val:' + m0 + ' state:' + state);
+ });
+
+ // submit the last entry
+ // ignore null last line
+ if (entry.length !== 0) {
+ endOfValue();
+ endOfEntry();
+ }
+
+ return data;
+ },
+
+ // a csv-specific line splitter
+ splitLines: function (csv, options) {
+ if (!csv) {
+ return undefined;
+ }
+
+ options = options || {};
+
+ // cache settings
+ var separator = options.separator || $.csv.defaults.separator;
+ var delimiter = options.delimiter || $.csv.defaults.delimiter;
+
+ // set initial state if it's missing
+ options.state = options.state || {};
+ if (!options.state.rowNum) {
+ options.state.rowNum = 1;
+ }
+
+ // clear initial state
+ var entries = [];
+ var state = 0;
+ var entry = '';
+ var exit = false;
+
+ function endOfLine () {
+ // reset the state
+ state = 0;
+
+ // if 'start' hasn't been met, don't output
+ if (options.start && options.state.rowNum < options.start) {
+ // update global state
+ entry = '';
+ options.state.rowNum++;
+ return;
+ }
+
+ if (options.onParseEntry === undefined) {
+ // onParseEntry hook not set
+ entries.push(entry);
+ } else {
+ var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
+ // false skips the row, configurable through a hook
+ if (hookVal !== false) {
+ entries.push(hookVal);
+ }
+ }
+
+ // cleanup
+ entry = '';
+
+ // if 'end' is met, stop parsing
+ if (options.end && options.state.rowNum >= options.end) {
+ exit = true;
+ }
+
+ // update global state
+ options.state.rowNum++;
+ }
+
+ // escape regex-specific control chars
+ var escSeparator = RegExp.escape(separator);
+ var escDelimiter = RegExp.escape(delimiter);
+
+ // compile the regEx str using the custom delimiter/separator
+ var match = /(D|S|\n|\r|[^DS\r\n]+)/;
+ var matchSrc = match.source;
+ matchSrc = matchSrc.replace(/S/g, escSeparator);
+ matchSrc = matchSrc.replace(/D/g, escDelimiter);
+ match = new RegExp(matchSrc, 'gm');
+
+ // put on your fancy pants...
+ // process control chars individually, use look-ahead on non-control chars
+ csv.replace(match, function (m0) {
+ if (exit) {
+ return;
+ }
+ switch (state) {
+ // the start of a value/entry
+ case 0:
+ // null value
+ if (m0 === separator) {
+ entry += m0;
+ state = 0;
+ break;
+ }
+ // opening delimiter
+ if (m0 === delimiter) {
+ entry += m0;
+ state = 1;
+ break;
+ }
+ // end of line
+ if (m0 === '\n') {
+ endOfLine();
+ break;
+ }
+ // phantom carriage return
+ if (/^\r$/.test(m0)) {
+ break;
+ }
+ // un-delimit value
+ entry += m0;
+ state = 3;
+ break;
+
+ // delimited input
+ case 1:
+ // second delimiter? check further
+ if (m0 === delimiter) {
+ entry += m0;
+ state = 2;
+ break;
+ }
+ // delimited data
+ entry += m0;
+ state = 1;
+ break;
+
+ // delimiter found in delimited input
+ case 2:
+ // escaped delimiter?
+ var prevChar = entry.substr(entry.length - 1);
+ if (m0 === delimiter && prevChar === delimiter) {
+ entry += m0;
+ state = 1;
+ break;
+ }
+ // end of value
+ if (m0 === separator) {
+ entry += m0;
+ state = 0;
+ break;
+ }
+ // end of line
+ if (m0 === '\n') {
+ endOfLine();
+ break;
+ }
+ // phantom carriage return
+ if (m0 === '\r') {
+ break;
+ }
+ // broken paser?
+ throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
+
+ // un-delimited input
+ case 3:
+ // null value
+ if (m0 === separator) {
+ entry += m0;
+ state = 0;
+ break;
+ }
+ // end of line
+ if (m0 === '\n') {
+ endOfLine();
+ break;
+ }
+ // phantom carriage return
+ if (m0 === '\r') {
+ break;
+ }
+ // non-compliant data
+ if (m0 === delimiter) {
+ throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
+ }
+ // broken parser?
+ throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
+ default:
+ // shenanigans
+ throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
+ }
+ // console.log('val:' + m0 + ' state:' + state);
+ });
+
+ // submit the last entry
+ // ignore null last line
+ if (entry !== '') {
+ endOfLine();
+ }
+
+ return entries;
+ },
+
+ // a csv entry parser
+ parseEntry: function (csv, options) {
+ // cache settings
+ var separator = options.separator;
+ var delimiter = options.delimiter;
+
+ // set initial state if it's missing
+ if (!options.state.rowNum) {
+ options.state.rowNum = 1;
+ }
+ if (!options.state.colNum) {
+ options.state.colNum = 1;
+ }
+
+ // clear initial state
+ var entry = [];
+ var state = 0;
+ var value = '';
+
+ function endOfValue () {
+ if (options.onParseValue === undefined) {
+ // onParseValue hook not set
+ entry.push(value);
+ } else {
+ var hook = options.onParseValue(value, options.state); // onParseValue Hook
+ // false skips the value, configurable through a hook
+ if (hook !== false) {
+ entry.push(hook);
+ }
+ }
+ // reset the state
+ value = '';
+ state = 0;
+ // update global state
+ options.state.colNum++;
+ }
+
+ // checked for a cached regEx first
+ if (!options.match) {
+ // escape regex-specific control chars
+ var escSeparator = RegExp.escape(separator);
+ var escDelimiter = RegExp.escape(delimiter);
+
+ // compile the regEx str using the custom delimiter/separator
+ var match = /(D|S|\n|\r|[^DS\r\n]+)/;
+ var matchSrc = match.source;
+ matchSrc = matchSrc.replace(/S/g, escSeparator);
+ matchSrc = matchSrc.replace(/D/g, escDelimiter);
+ options.match = new RegExp(matchSrc, 'gm');
+ }
+
+ // put on your fancy pants...
+ // process control chars individually, use look-ahead on non-control chars
+ csv.replace(options.match, function (m0) {
+ switch (state) {
+ // the start of a value
+ case 0:
+ // null last value
+ if (m0 === separator) {
+ value += '';
+ endOfValue();
+ break;
+ }
+ // opening delimiter
+ if (m0 === delimiter) {
+ state = 1;
+ break;
+ }
+ // skip un-delimited new-lines
+ if (m0 === '\n' || m0 === '\r') {
+ break;
+ }
+ // un-delimited value
+ value += m0;
+ state = 3;
+ break;
+
+ // delimited input
+ case 1:
+ // second delimiter? check further
+ if (m0 === delimiter) {
+ state = 2;
+ break;
+ }
+ // delimited data
+ value += m0;
+ state = 1;
+ break;
+
+ // delimiter found in delimited input
+ case 2:
+ // escaped delimiter?
+ if (m0 === delimiter) {
+ value += m0;
+ state = 1;
+ break;
+ }
+ // null value
+ if (m0 === separator) {
+ endOfValue();
+ break;
+ }
+ // skip un-delimited new-lines
+ if (m0 === '\n' || m0 === '\r') {
+ break;
+ }
+ // broken paser?
+ throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+
+ // un-delimited input
+ case 3:
+ // null last value
+ if (m0 === separator) {
+ endOfValue();
+ break;
+ }
+ // skip un-delimited new-lines
+ if (m0 === '\n' || m0 === '\r') {
+ break;
+ }
+ // non-compliant data
+ if (m0 === delimiter) {
+ throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+ }
+ // broken parser?
+ throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+ default:
+ // shenanigans
+ throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
+ }
+ // console.log('val:' + m0 + ' state:' + state);
+ });
+
+ // submit the last value
+ endOfValue();
+
+ return entry;
+ }
+ },
+
+ helpers: {
+
+ /**
+ * $.csv.helpers.collectPropertyNames(objectsArray)
+ * Collects all unique property names from all passed objects.
+ *
+ * @param {Array} objects Objects to collect properties from.
+ *
+ * Returns an array of property names (array will be empty,
+ * if objects have no own properties).
+ */
+ collectPropertyNames: function (objects) {
+ var o = [];
+ var propName = [];
+ var props = [];
+ for (o in objects) {
+ for (propName in objects[o]) {
+ if ((objects[o].hasOwnProperty(propName)) &&
+ (props.indexOf(propName) < 0) &&
+ (typeof objects[o][propName] !== 'function')) {
+ props.push(propName);
+ }
+ }
+ }
+ return props;
+ }
+ },
+
+ /**
+ * $.csv.toArray(csv)
+ * Converts a CSV entry string to a javascript array.
+ *
+ * @param {Array} csv The string containing the CSV data.
+ * @param {Object} [options] An object containing user-defined options.
+ * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
+ * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
+ *
+ * This method deals with simple CSV strings only. It's useful if you only
+ * need to parse a single entry. If you need to parse more than one line,
+ * use $.csv2Array instead.
+ */
+ toArray: function (csv, options, callback) {
+ options = (options !== undefined ? options : {});
+ var config = {};
+ config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false);
+ config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
+ config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
+ var state = (options.state !== undefined ? options.state : {});
+
+ // setup
+ options = {
+ delimiter: config.delimiter,
+ separator: config.separator,
+ onParseEntry: options.onParseEntry,
+ onParseValue: options.onParseValue,
+ state: state
+ };
+
+ var entry = $.csv.parsers.parseEntry(csv, options);
+
+ // push the value to a callback if one is defined
+ if (!config.callback) {
+ return entry;
+ } else {
+ config.callback('', entry);
+ }
+ },
+
+ /**
+ * $.csv.toArrays(csv)
+ * Converts a CSV string to a javascript array.
+ *
+ * @param {String} csv The string containing the raw CSV data.
+ * @param {Object} [options] An object containing user-defined options.
+ * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
+ * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
+ *
+ * This method deals with multi-line CSV. The breakdown is simple. The first
+ * dimension of the array represents the line (or entry/row) while the second
+ * dimension contains the values (or values/columns).
+ */
+ toArrays: function (csv, options, callback) {
+ options = (options !== undefined ? options : {});
+ var config = {};
+ config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false);
+ config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
+ config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
+
+ // setup
+ var data = [];
+ options = {
+ delimiter: config.delimiter,
+ separator: config.separator,
+ onPreParse: options.onPreParse,
+ onParseEntry: options.onParseEntry,
+ onParseValue: options.onParseValue,
+ onPostParse: options.onPostParse,
+ start: options.start,
+ end: options.end,
+ state: {
+ rowNum: 1,
+ colNum: 1
+ }
+ };
+
+ // onPreParse hook
+ if (options.onPreParse !== undefined) {
+ options.onPreParse(csv, options.state);
+ }
+
+ // parse the data
+ data = $.csv.parsers.parse(csv, options);
+
+ // onPostParse hook
+ if (options.onPostParse !== undefined) {
+ options.onPostParse(data, options.state);
+ }
+
+ // push the value to a callback if one is defined
+ if (!config.callback) {
+ return data;
+ } else {
+ config.callback('', data);
+ }
+ },
+
+ /**
+ * $.csv.toObjects(csv)
+ * Converts a CSV string to a javascript object.
+ * @param {String} csv The string containing the raw CSV data.
+ * @param {Object} [options] An object containing user-defined options.
+ * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
+ * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
+ * @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
+ *
+ * This method deals with multi-line CSV strings. Where the headers line is
+ * used as the key for each value per entry.
+ */
+ toObjects: function (csv, options, callback) {
+ options = (options !== undefined ? options : {});
+ var config = {};
+ config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false);
+ config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
+ config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
+ config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
+ options.start = 'start' in options ? options.start : 1;
+
+ // account for headers
+ if (config.headers) {
+ options.start++;
+ }
+ if (options.end && config.headers) {
+ options.end++;
+ }
+
+ // setup
+ var lines = [];
+ var data = [];
+
+ options = {
+ delimiter: config.delimiter,
+ separator: config.separator,
+ onPreParse: options.onPreParse,
+ onParseEntry: options.onParseEntry,
+ onParseValue: options.onParseValue,
+ onPostParse: options.onPostParse,
+ start: options.start,
+ end: options.end,
+ state: {
+ rowNum: 1,
+ colNum: 1
+ },
+ match: false,
+ transform: options.transform
+ };
+
+ // fetch the headers
+ var headerOptions = {
+ delimiter: config.delimiter,
+ separator: config.separator,
+ start: 1,
+ end: 1,
+ state: {
+ rowNum: 1,
+ colNum: 1
+ }
+ };
+
+ // onPreParse hook
+ if (options.onPreParse !== undefined) {
+ options.onPreParse(csv, options.state);
+ }
+
+ // parse the csv
+ var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
+ var headers = $.csv.toArray(headerLine[0], options);
+
+ // fetch the data
+ lines = $.csv.parsers.splitLines(csv, options);
+
+ // reset the state for re-use
+ options.state.colNum = 1;
+ if (headers) {
+ options.state.rowNum = 2;
+ } else {
+ options.state.rowNum = 1;
+ }
+
+ // convert data to objects
+ for (var i = 0, len = lines.length; i < len; i++) {
+ var entry = $.csv.toArray(lines[i], options);
+ var object = {};
+ for (var j = 0; j < headers.length; j++) {
+ object[headers[j]] = entry[j];
+ }
+ if (options.transform !== undefined) {
+ data.push(options.transform.call(undefined, object));
+ } else {
+ data.push(object);
+ }
+
+ // update row state
+ options.state.rowNum++;
+ }
+
+ // onPostParse hook
+ if (options.onPostParse !== undefined) {
+ options.onPostParse(data, options.state);
+ }
+
+ // push the value to a callback if one is defined
+ if (!config.callback) {
+ return data;
+ } else {
+ config.callback('', data);
+ }
+ },
+
+ /**
+ * $.csv.fromArrays(arrays)
+ * Converts a javascript array to a CSV String.
+ *
+ * @param {Array} arrays An array containing an array of CSV entries.
+ * @param {Object} [options] An object containing user-defined options.
+ * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
+ * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
+ *
+ * This method generates a CSV file from an array of arrays (representing entries).
+ */
+ fromArrays: function (arrays, options, callback) {
+ options = (options !== undefined ? options : {});
+ var config = {};
+ config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false);
+ config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
+ config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
+
+ var output = '';
+ var line;
+ var lineValues;
+ var i;
+ var j;
+
+ for (i = 0; i < arrays.length; i++) {
+ line = arrays[i];
+ lineValues = [];
+ for (j = 0; j < line.length; j++) {
+ var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
+ if (strValue.indexOf(config.delimiter) > -1) {
+ strValue = strValue.replace(new RegExp(config.delimiter, 'g'), config.delimiter + config.delimiter);
+ }
+
+ var escMatcher = '\n|\r|S|D';
+ escMatcher = escMatcher.replace('S', config.separator);
+ escMatcher = escMatcher.replace('D', config.delimiter);
+
+ if (strValue.search(escMatcher) > -1) {
+ strValue = config.delimiter + strValue + config.delimiter;
+ }
+ lineValues.push(strValue);
+ }
+ output += lineValues.join(config.separator) + '\n';
+ }
+
+ // push the value to a callback if one is defined
+ if (!config.callback) {
+ return output;
+ } else {
+ config.callback('', output);
+ }
+ },
+
+ /**
+ * $.csv.fromObjects(objects)
+ * Converts a javascript dictionary to a CSV string.
+ *
+ * @param {Object} objects An array of objects containing the data.
+ * @param {Object} [options] An object containing user-defined options.
+ * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
+ * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
+ * @param {Character} [sortOrder] Sort order of columns (named after
+ * object properties). Use 'alpha' for alphabetic. Default is 'declare',
+ * which means, that properties will _probably_ appear in order they were
+ * declared for the object. But without any guarantee.
+ * @param {Character or Array} [manualOrder] Manually order columns. May be
+ * a strin in a same csv format as an output or an array of header names
+ * (array items won't be parsed). All the properties, not present in
+ * `manualOrder` will be appended to the end in accordance with `sortOrder`
+ * option. So the `manualOrder` always takes preference, if present.
+ *
+ * This method generates a CSV file from an array of objects (name:value pairs).
+ * It starts by detecting the headers and adding them as the first line of
+ * the CSV file, followed by a structured dump of the data.
+ */
+ fromObjects: function (objects, options, callback) {
+ options = (options !== undefined ? options : {});
+ var config = {};
+ config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false);
+ config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
+ config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
+ config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
+ config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
+ config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
+ config.transform = options.transform;
+
+ if (typeof config.manualOrder === 'string') {
+ config.manualOrder = $.csv.toArray(config.manualOrder, config);
+ }
+
+ if (config.transform !== undefined) {
+ var origObjects = objects;
+ objects = [];
+
+ var i;
+ for (i = 0; i < origObjects.length; i++) {
+ objects.push(config.transform.call(undefined, origObjects[i]));
+ }
+ }
+
+ var props = $.csv.helpers.collectPropertyNames(objects);
+
+ if (config.sortOrder === 'alpha') {
+ props.sort();
+ } // else {} - nothing to do for 'declare' order
+
+ if (config.manualOrder.length > 0) {
+ var propsManual = [].concat(config.manualOrder);
+ let p;
+ for (p = 0; p < props.length; p++) {
+ if (propsManual.indexOf(props[p]) < 0) {
+ propsManual.push(props[p]);
+ }
+ }
+ props = propsManual;
+ }
+
+ var o, p, line, output, propName;
+ if (config.headers) {
+ output.push(props);
+ }
+
+ for (o = 0; o < objects.length; o++) {
+ line = [];
+ for (p = 0; p < props.length; p++) {
+ propName = props[p];
+ if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
+ line.push(objects[o][propName]);
+ } else {
+ line.push('');
+ }
+ }
+ output.push(line);
+ }
+
+ // push the value to a callback if one is defined
+ return $.csv.fromArrays(output, options, config.callback);
+ }
+ };
+
+ // Maintenance code to maintain backward-compatibility
+ // Will be removed in release 1.0
+ $.csvEntry2Array = $.csv.toArray;
+ $.csv2Array = $.csv.toArrays;
+ $.csv2Dictionary = $.csv.toObjects;
+
+ // CommonJS module is defined
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = $.csv;
+ }
+}).call(this);
\ No newline at end of file
diff --git a/diu/diuJS.js b/diu/diuJS.js
index e956593..0819bcf 100644
--- a/diu/diuJS.js
+++ b/diu/diuJS.js
@@ -85,6 +85,7 @@
// Returns false if input is a non-integer number or NaN
function isInt(num) {
+ num = parseNum(num);
if (Array.isArray(num) == true) {
for (a = 0; a < num.length; a++) {
if (Number.isInteger(num[a]) == false) {
@@ -97,6 +98,22 @@ function isInt(num) {
return Number.isInteger(num);
}
+
+function isFloat(num) {
+ num = parseNum(num);
+ if (Array.isArray(num) == true) {
+ for (a = 0; a < num.length; a++) {
+ if (Number.isInteger(num[a]) == true || Number.isNaN(num[a]) == true) {
+ return false;
+ }
+ return true;
+ }
+ }
+ else
+ return !(Number.isInteger(num) || Number.isNaN(num));
+}
+
+
// Returns false if input is not a positive number (zero, negative number, or NaN)
function isPositive(num) {
if (Array.isArray(num) == true) {
@@ -113,6 +130,30 @@ function isPositive(num) {
return false;
else if (num > 0)
return true;
+ else {
+ return false;
+ }
+ }
+}
+
+function isNonNegative(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;
+ }
}
}
diff --git a/index.html b/index.html
index af5cc97..f8d9371 100644
--- a/index.html
+++ b/index.html
@@ -11,8 +11,8 @@
}
body {
- font-family: "Consolas", "Inconsolata", "Lucida Console", Monospace;
- font-size: 15px;
+ font-family: "Consolas", "Inconsolata", "Lucida Console", Monospace;
+ font-size: 15px;
line-height: 120%;
min-height: 100%;
}